with_scope is protected
August 10th, 2007
I've seen a lot of people run into trouble using with_scope on edge. It's now a protected method Ticket: http://dev.rubyonrails.org/ticket/8524.
The solution? Don't use it or use it sparingly. They made it protected for a reason - to protect people from abusing it - but if you absolutely must have at it then you can do something like this:
klass.send(:with_scope, :find => { :conditions => conditions }) { yield }
So say you want to scope your finders to retrieve only MyModel objects of type 'super':
def my_super_duper_scoping(klass)
klass.send(:with_scope, :find => { :conditions => "type = 'super'" }) { yield }
end
my_super_models = my_super_duper_scoping(MyModel) do
MyModel.find(:all)
end
my_super_models_named_david = my_super_duper_scoping(MyModel) do
MyModel.find(:all, :conditions => ["name = '?'", 'david')
end
Using self.class.send is a way to get around public and private checking in ruby 1.8. Use with caution.
Tag Type-Ahead with Multiple Items
August 6th, 2007
If you've ever implemented type-ahead functionality and wondered how to get it to work when users are entering multiple items, like a list of comma delimited tags, here it is.
You can, of course, roll your own Javascript and Ajax calls but why bother when Rails gives us a nice Javascript helper to handle this. The helper is text_field_with_auto_complete but this defaults to the current controller. For my purposes tags can be entered on different areas of the site for different models so I used text_field_with_auto_complete with one modification:
def text_field_with_auto_complete_with_custom_url(object, method, url_options = {}, tag_options = {}, completion_options = {})
(completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
text_field(object, method, tag_options) +
content_tag("div", "", :id => "#{object}_#{method}_auto_complete", :class => "auto_complete") +
auto_complete_field("#{object}_#{method}", { :url => { :action => "auto_complete_for_#{object}_#{method}" }.update(url_options) }.update(completion_options))
end
Note: text_field_with_auto_complete is deprecated and will be moved to a plugin in Rails 2.0.
The third parameter is now a hash of optional url arguments (action, controller, etc). You can also monkey patch the original method or better use some alias/alias_method_chain trickery in your own app. In your view call the newly created helper with your arguments.
<label for="tag_name">Tags:</label>
<%= text_field_with_auto_complete_with_custom_url :tag, :names,
{:controller => "tagging_demo"}, {}, { :tokens => ','} %>
Include the necessary javascript libraries (prototype and scriptaculous):
<%= javascript_include_tag 'prototype' %>
<%= javascript_include_tag 'scriptaculous' %>
You can set the controller and action to whatever you like but it defaults to a method in the form of auto_complete_for_object_method. Going with that your action could look something like this:
def auto_complete_for_tag_names
@tags = Tag.find(:all,
:conditions => ["name like ?", "%#{params[:tag][:names]}%"],
:order => 'name DESC', :limit => 20)
render :layout => false
end
And your view (auto_complete_for_tag_names.html.erb):
<ul class="tags">
<% for tag in @tags do -%>
<li class="tag"><%= tag.name -%></li>
<% end -%>
</ul>
That's all there is to it. The magic comes from providing a token to Ajax.Autocompleter which we provided in the argument hash to text_field_with_auto_complete_with_custom_url ( { :tokens => ','} ). You can use any delimiter you like. You can also customize the UI in your view and CSS.
Here's a demo http://www.naffis.com/demos/tagging_demo
Racing Presidents
August 4th, 2007
Nats v Reds