Ruby/Rails 方法帮助
所以,我在创建 ruby 方法方面几乎是个新手。我从 gem (meta_search) 获得了以下方法,但我需要更改默认行为。方法如下:
def sort_link(builder, attribute, *args)
raise ArgumentError, "Need a MetaSearch::Builder search object as first param!" unless builder.is_a?(MetaSearch::Builder)
attr_name = attribute.to_s
name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name)
prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.')
current_order = prev_attr == attr_name ? prev_order : nil
new_order = current_order == 'asc' ? 'desc' : 'asc'
options = args.first.is_a?(Hash) ? args.shift : {}
html_options = args.first.is_a?(Hash) ? args.shift : {}
css = ['sort_link', current_order].compact.join(' ')
html_options[:class] = [css, html_options[:class]].compact.join(' ')
options.merge!(
builder.search_key => builder.search_attributes.merge(
'meta_sort' => [attr_name, new_order].join('.')
)
)
link_to [ERB::Util.h(name), order_indicator_for(current_order)].compact.join(' ').html_safe,
url_for(options),
html_options
end
此方法返回搜索的排序链接。输出如下所示:
<a class="sort_link asc" href="/photos?search[meta_sort]=average_rating.desc">Best Photography ▲</a>
问题如下:此方法假设您希望在第一次单击时按升序排序,在第二次单击时按降序排序。我想要相反的行为。我看到我可以把这个……改成
new_order = current_order == 'asc' ? 'desc' : 'asc'
这个…………
new_order = current_order == 'desc' ? 'asc' : 'desc'
但这只是扭转了情况。我真正需要的是能够指定一个选项,并在该选项通过时反转行为。
所以这是我的问题:我真的不明白 *args 是如何传递的。据我所知,这些行正在采用 Rails link_to 方法的选项和 html_option 哈希值...
options = args.first.is_a?(Hash) ? args.shift : {}
html_options = args.first.is_a?(Hash) ? args.shift : {}
我想做的是将自定义选项添加到选项哈希中,如果定义了该选项,则反转排序方程。我尝试这样做......
if defined? options[:sort_preference] && options[:sort_preference]==:desc
new_order = current_order == 'desc' ? 'asc' : 'desc'
options.delete(:sort_preference)
else
new_order = current_order == 'asc' ? 'desc' : 'asc'
options.delete(:sort_preference)
end
但这不起作用,它只是保持标准行为并将 :sort_preference 传递到 URL 中,如下所示:
<a class="sort_link asc" href="/photos?search[meta_sort]=average_rating.desc&sort_preference=desc">Best Photography ▲</a>
在上面的链接中, sort_preference
不应该出现在 URL 中,并且 search[meta_sort]
应该是 average_ rating.asc
——因为链接显示的内容与呈现的内容相反。
所以,显然,我的尝试没有成功,因为我不太明白这个方法中发生了什么。我希望回答这个问题的是:
- 一点帮助理解 *args 的工作原理
- 解释为什么我尝试向此方法添加选项失败
- 一个如何修复它的示例
非常感谢您的阅读!
So, I'm pretty much a newbie at creating ruby methods. I've got the following method from a gem (meta_search), but I need to change the default behavior. Here's the method:
def sort_link(builder, attribute, *args)
raise ArgumentError, "Need a MetaSearch::Builder search object as first param!" unless builder.is_a?(MetaSearch::Builder)
attr_name = attribute.to_s
name = (args.size > 0 && !args.first.is_a?(Hash)) ? args.shift.to_s : builder.base.human_attribute_name(attr_name)
prev_attr, prev_order = builder.search_attributes['meta_sort'].to_s.split('.')
current_order = prev_attr == attr_name ? prev_order : nil
new_order = current_order == 'asc' ? 'desc' : 'asc'
options = args.first.is_a?(Hash) ? args.shift : {}
html_options = args.first.is_a?(Hash) ? args.shift : {}
css = ['sort_link', current_order].compact.join(' ')
html_options[:class] = [css, html_options[:class]].compact.join(' ')
options.merge!(
builder.search_key => builder.search_attributes.merge(
'meta_sort' => [attr_name, new_order].join('.')
)
)
link_to [ERB::Util.h(name), order_indicator_for(current_order)].compact.join(' ').html_safe,
url_for(options),
html_options
end
This method returns a sort link for a search. The output looks like this:
<a class="sort_link asc" href="/photos?search[meta_sort]=average_rating.desc">Best Photography ▲</a>
Here's the problem: this method assumes that you want to sort in ascending order on the first click, and descending order on the second. I want the opposite behavior. I see that I could change this...
new_order = current_order == 'asc' ? 'desc' : 'asc'
to this...
new_order = current_order == 'desc' ? 'asc' : 'desc'
...but that just reverses the situation. What I really need is to be able to specify an option and reverse the behavior if that option is passed.
So here's my problem: I don't really understand how the *args are passed. From what I can tell these lines are taking the option and html_option hashes for rails link_to method...
options = args.first.is_a?(Hash) ? args.shift : {}
html_options = args.first.is_a?(Hash) ? args.shift : {}
What I'd like to do is add a custom option to the options hash, and if that option is defined reverse the sort equation. I tried to do this...
if defined? options[:sort_preference] && options[:sort_preference]==:desc
new_order = current_order == 'desc' ? 'asc' : 'desc'
options.delete(:sort_preference)
else
new_order = current_order == 'asc' ? 'desc' : 'asc'
options.delete(:sort_preference)
end
... but that didn't work, it just kept the standard behavior and passed :sort_preference into the URL like so:
<a class="sort_link asc" href="/photos?search[meta_sort]=average_rating.desc&sort_preference=desc">Best Photography ▲</a>
In the link above, sort_preference
shouldn't appear in the URL, and search[meta_sort]
should be average_rating.asc
-- since the link shows the opposite of what rendered.
So, obviously, my attempt didn't work because I don't really understand what's going on in this method. What I'm hoping for in answer to this question is:
- A little help understanding how *args works
- An explanation of why my attempt to add an option to this method failed
- An example of how to fix it
Thank you very much for taking a look!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
*args
只是意味着您可以传递无限数量的参数。所以这个方法首先需要 2 个参数(构建器和属性),然后你可以传递任意数量的参数。以下是与 args 相关的方法中发生的情况的描述:如果传递给此方法的第三个参数不是哈希,则它将将该 arg 设置为名称并将其删除 (
args. shift
),否则使用构建器名称。然后它查看下一个参数(这将是第四个)。如果它是哈希,则它将选项设置为该参数并删除该参数,否则它将其设置为空哈希。
然后它查看下一个参数(第 5 个参数)。如果它是哈希,则它将其设置为 html_options 并删除该参数,否则 html 将设置为 {}。
使用 args* 可以使方法的调用更加清晰(尽管方法中的实现可能不是这样)。如果您没有要传递的“名称”,则可以这样做:
您可以这样做:
如果您没有名称,它不会强制您添加名称参数。
因此,为了让您的代码正常工作,此方法需要将选项作为第四个参数,并且选项哈希需要包含
:sort_preferences
。调用sort_link
的代码是什么样的?您需要在到达此方法之前将此选项插入到该哈希中。*args
just means that you can pass unlimited number of arguments. So this method needs 2 arguments first (builder & attribute) and then you could pass any number of args after that. Here is a description of what is going on in the method relating to the args:If the third argument passed to this method is not a hash, then it sets that arg as the name and removes it (
args.shift
), otherwise use the builder name.Then it looks at the next arg (which would be the 4th). If its a hash, then it sets options to that arg and deletes that arg, otherwise it sets it to empty hash.
Then it looks at the next arg (5th arg). If its a hash, then it sets that as html_options and deletes that arg, otherwise html is set to {}.
Using args* can make calling the method a little cleaner (although the implementation in the method might not be). If you did not have a "name" to pass, instead of doing this:
you can do this:
It does not force you to add a arg for name, if you don't have one.
So for your code to work, this method would need to take options as the 4th arg, and the options hash would need to contain
:sort_preferences
. What does the code look like that is callingsort_link
? You need to insert this option into that hash before it gets to this method.meta_search gem 支持这一点。无需修改 gem 的代码。在控制器中,查询数据后,将 @search 对象上的 meta_sort 属性设置为您想要的字段和排序顺序。加载视图时,您的数据将按预期排序。
例如,在 Posts 控制器中,您可以执行以下操作将标题字段的默认排序设置为降序:
The meta_search gem supports this. It is not necessary to modify the gem's code. In your controller, after you have queried for your data, set the meta_sort attribute on the @search object to the field and sort order you would like. When the view loads, your data will be sorted as expected.
For example, in a Posts controller, you would do the following to set the default sort to the title field, descending: