如何重写以下代码以更加Ruby-wayish?我正在考虑inject
,但无法弄清楚如何做到这一点。
def nested_page_path(page)
path = "/#{page.slug}"
while page.parent_id do
path.prepend "/#{page.parent.slug}"
page = page.parent
end
path
end
输入是AR对象,具有0-5个连续父项。输出类似于'/pages/services/law'
。
答案 0 :(得分:2)
如果您确定父育中没有周期,您可以递归地执行此操作,即。即具有调用自身的功能。 5级嵌套应该做得很好,数以千计可能会出现麻烦。
def nested_page_path(page)
return "" if page.nil? # Or whatever that is root
"#{nested_page_path(page.parent)}/#{page.slug}"
end
但请记住,上面的方法以及您的方法将在单独的查询中获取每个对象。当你已经获取它们时它很好,但如果没有,你会遇到一些N + 1查询问题。
一个简单的解决方法是缓存。您可以在before_save
上重建此对象及其后代的嵌套路径:这是每次写入时的一些重要开销。有一个更好的方法。
通过使用nested sets,您可以在一个查询中获取对象的层次结构分支。像这样:
page.self_and_ancestors.pluck(:slug).join('/')
# ^
# Nested sets' goodness
该查询的作用基本上是“获取由左边界定的页面,其范围包含我自己的”。我在我的示例中使用awesome_nested_set
。
SELECT "pages"."slug" FROM "pages"
WHERE ("pages"."lft" <= 42) AND ("pages"."rgt" >= 88)
ORDER BY "pages"."lft"
答案 1 :(得分:1)
在不了解您的对象结构的情况下,这很困难。但像这样的递归应该这样做:
def nested_page_path(page)
path = "/#{page.slug}"
return path unless page.parent_id
path.prepend "#{nested_page_path(page.parent)}/"
end
答案 2 :(得分:1)
不确定inject
是一个简单的答案,因为它在Enumerable
上运行,并且您没有明显的可枚举开头。
我建议这样的事情(与你的解决方案不同)
def nested_page_path(page)
pages = [page]
pages << pages.last.parent while pages.last.parent
'/' + pages.reverse.map(&:slug).join('/')
end
那里有减少重复的余地,但这或多或少都是我的目标。