Notes posted to Ruby on Rails
RSS feedHow I use Optimistic Locking
I have used Optimistic locking often, but usually I only need it in one or two places in the codebase, not everywhere an object is saved whose model has a lock_version column. So what I usually end up doing is using a little module I wrote called OptimisticallyLockable (awesome name right?). Here it is:
module OptimisticallyLockable def self.included(receiver) receiver.lock_optimistically = false receiver.class_eval do def self.with_optimistic_locking original_lock = self.lock_optimistically self.lock_optimistically = true begin yield ensure self.lock_optimistically = original_lock end end end end end
When included in a model that has a lock_version column it will turn off optimistic locking. Then when you want to actually use optimistic locking you can just use the with_optimistic_locking method like this:
class Blog include OptimisticallyLockable def do_something_destructive! self.class.with_optimistic_locking do # do something important here end end end
usage example
code
class MyModel < ActiveRecord::Base def created_today? self.created_on.to_date == Date.today end end
Possible gotcha
Please note that exists? doesn’t hold all the conventions of find, i.e. you can’t do:
Person.exists?(:conditions => ['name LIKE ?', "%#{query}%"]) # DOESN'T WORK!
Getting functionality from instances
If you want to get this functionality from instances of the models too, you can easily monkey patch rails to allow this. This is only semi-bad practice, AFAIK.
In config/initializers/overrides.rb:
# # Wrapper for @model.human_attribute_name -> Model.human_attribute_name # class ActiveRecord::Base def human_attribute_name(*args) self.class.human_attribute_name(*args) end end
Now, why is this bad? Because we change a core class, and that makes maintainability a little bit harder since new developers will not know about this.
The good part is that is such a small thing and as long as human_attribute_name exists in the class, this extension will work.
Example usage:
# index.html.haml %th= User.human_attribute_name('login') %th= User.human_attribute_name('email') # ... # show.html.haml %p %strong= "#{@user.human_attribute_name('login')}: " = @user.login
In this example, about nothing was gained at all, really. A better example is when you are building helpers to do stuff like displaying model attributes for you and whatnot. Examples for this would be far too huge to show off here, but basically, you avoid having to do stuff like this:
if options.include? :model model = options[:model] model = model.class unless model.is_a? Class model.human_attribute_name(field) else # ... end
and you want to keep your views clean (@user vs. @user.class)
Nothing here
You’re probably looking for I18n::Backend::Simple.
This method will still rewrite all the values of the table
Even if you update only a small boolean flag on your record, update_attribute will generate an UPDATE statement that will include all the fields of the record, including huge BLOB and TEXT columns. Take this in account.
You can't use Symbols, but you can use Regexps
You can’t use Symbol (although Symbol is accepted with render :action => :new), like:
assert_template :new # WON'T WORK!
But you can use Regexp, e.g.:
assert_template /new/ # WORKS OK
Note that the String matched with your Regexp is the full path to the template relative to the view/ directory of your app, so this will not work:
assert_template /^new$/ # WON'T WORK!
However this might:
assert_template /^employees\/new.html.haml$/
Hash conditions require explicit key and value
When condition passed as hash, the behavior is different from a finder method. Finder methods, such as:
find(:all, :user=>user)
will apply the user_id = user.id convention, provided user is an association (e.g. belongs_to :user). The exists? method will not do the same. You must specify the foreign key name and value explicitly, i.e:
exists?(:user_id=>user.id)
Getting the object in a partial
If you need to get the object for the form inside a partial, and can’t use the instance variable, use the #object method… This is particularly useful when you’re dealing with single-table inheritance subclasses (e.g. MyOtherClass inherits from MyClass) or when you are using the same partial across different controllers.
new.html.erb
<% form_for(@my_object) do %> <%= render :partial => 'form' %> <%= submit_tag 'Create' %> <% end %>
_form.html.erb
<% if f.object.class.is_a? MyClass %> <%# do something... %> <% elsif f.object.is_a? MyOtherClass %> <%# do something else... %> <% end %>
Add Rspec files to the annotations
By default the annotations search the ‘test’ folder, but not the ‘spec’ folder if you are using Rspec. To get those specs involved do this:
require 'source_annotation_extractor' class SourceAnnotationExtractor def find(dirs=%w(app lib spec)) dirs.inject({}) { |h, dir| h.update(find_in(dir)) } end end
If you have other folders you want to check, just add them to the dirs list.
Description
Similar to Ruby’s built-in attr_writer, only it creates a class attribute writer method (as opposed to an instance attribute writer method).
Description
Similar to Ruby’s built-in attr_reader, only it creates a class attribute reader method (as opposed to an instance attribute reader method).
Use collect in nested content_tags
Remember to use #collect instead of #each in nested content_tags
arr = ['a','b','c'] content_tag :div do arr.collect { |letter| content_tag(:scan, letter) end #=> <div> # <scan>a</scan> # <scan>b</scan> # <scan>c</scan> # </div>
If you used #each you would get this (which is probably a mistake):
#=> <div> # abc # </div>
two ways to disable single table inheritance
-
Don’t use the column name ‘type’
-
Or if the first is no option for you: Tell Rails to look for a not existing column like:
class MyModel < ActiveRecord::Base
# disable STI inheritance_column = :_type_disabled end
alternative (working with 2.2.X)
ActionController::Base.relative_url_root
Routes = RouteSet.new
In config/routes.rb you can see this:
ActionController::Routing::Routes.draw do |map| #routes end
If you want to look at the code in ActionController::Routing you won’t find the definition of Routes. That’s because it’s actually an instance of the class RouteSet, defined in action_controller/routing.rb
Routes = RouteSet.new
How to test different responses of respond_to
You can shorten this:
@request.env['HTTP_ACCEPT'] = "application/rss"
To this:
@request.accept = "application/rss"
Also, if you send more than one Mime type, it will render the first one:
@request.accept = "text/javascript, text/html" #=> renders JS @request.accept = "text/html, text/javascript" #=> renders HTML
Adding "alt" text to the image tag
<%= image_submit_tag (“create.gif”, :alt => “Create new entity”) %>
This way when the images are disabled or don’t work you get the nice custom text instead of the standard one. Pretty useful.
Alternate for Rails 2.0
Obviously these methods are protected so usage in an app is discouraged. But if you need to use it anyway for some reason Rails 2.0 also has sanitize_sql_for_conditions which operates exactly like sanitize_sql used to (i.e. it determines if it needs to be processed as an array or hash). So if you are going to blow by the protected status might as well use the easier version. :)
Using fields_for with has_many associations
If you want to edit each element of an array of objects (such as with a has_many type association), you will need to include “[]” in your field parameter name, like so:
<% fields_for "object[]" do |subfield| -%> [...] <% end -%>
Because you named the field parameter “object[]”, fields_for will assume you have an instance variable @object to use for the fields’ values. To fake this, you can do something like:
<% objects.each do |@object| -%> <% fields_for "object[]" do |subfield| -%> [...] <% end -%> <% end -%>
If that looks like sacrilegious Rails code to you, then you could consider:
<% objects.each do |object| -%> <% fields_for "object[]", object do |subfield| -%> [...] <% end -%> <% end -%>
In either case, params[:object] will be a hash where the ID of each object (determined via ActiveRecord::Base#to_param ) is associated with a hash of its new values:
params = { 'object' => { '123' => { 'field' => 'newval' }, '159' => { 'field' => 'newval' } } }
Reloading memoized values
Memoize is used to cache the result of a method. It’s roughly equivalent of having:
def memoized_method(*args) @result[args] ||= ( # do calculation here ) end
However, the result is cached so that it’s not calculated for every request.
To recalculate cached value use either
obj.memoized_method(:reload)
or
obj.memoized_method(true)
Javascript encoding DOES work!
grosser assertion is false :
mail_to('xxx@xxx.com', nil, :encode => :javascript) # => "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%78%78%78%40%78%78%78%2e%63%6f%6d%22%3e%78%78%78%40%78%78%78%2e%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>"
Use “nil” as the second parameter to tell mail_to that you want to use the first parameter for both text and email link
Remember to mixin the ActiveSupport::Memoizable module
To use memoize in your model you need to extend the model class with the module, like this:
class Person < ActiveRecord::Base # Mixin the module extend ActiveSupport::Memoizable def expensive_method # do something that is worth remembering end memoize :expensive_method end
If you use memoizable in most of your models you could consider mixing the module into all ActiveRecord models by doing this in an initializer:
ActiveRecord::Base.extend(ActiveSupport::Memoizable)
constantize
@ncancelliere - Instead use constantize which is provided as part of ActiveSupport. It is much easier. So:
mystring.constantize
Convert String to Class
this example shows how you can take a string and # make it a class and then send it a method
Kernel.const_get(my_string.capitalize).select_options
See #data_select for available options
Available symbols for “options” hash are described on date_select page
Easier Universal Partials
@hosiawak provides a great example of how to use polymorphic_path to produce universal partials. You can actually simply his example even more with the following:
<%= link_to 'Edit', edit_polymorphic_path(obj) %> <%= link_to 'Delete', obj, :method => :delete %>
So the things to note are that in addition to polymorphic_path, Rails also provides an “edit_” version just like on your resources so you can use that instead of specifying the action specifically. The second thing to remember is if you pass just the raw object as the path then then rails will automatically wrap it in a call to polymorphic_path.
Universal partial
polymorphic_url is very useful if you want to create an universal partial that works for more than 1 type of object passed to it.
For example in you sidebar you might have a _sidebar.html.erb partial that’s supposed to display links to “Edit” and “Delete” actions. You can write it in such a way that it can be reused for different types of objects (in the example below we pass either a Post or a Note).
your_template.html.erb
<%= render :partial => 'shared/sidebar', :locals => { :obj => Post.new -%>
other_template.html.erb
<%= render :partial => 'shared/sidebar', :locals => { :obj => Note.new -%>
_sidebar.html.erb
<%= link_to "Edit", polymorhpic_url(obj, :action => 'edit') -%> <%= link_to "Delete", polymorphic_url(obj), :method => :delete -%>
link_to some url with current params
Code example
link_to "some text", users_path(:params => params, :more_params => "more params")