Posts tagged rails
Posts tagged rails
I was tripped up by this today. The behaviour of Rails when it comes to chaining orders seemed counterintuitive to me. Here’s an example…
class Author < ActiveRecord::Base
has_many :posts, order: 'created_at ASC'
end
class Post < ActiveRecord::Base
belongs_to :author
end
author = Author.create
post_1 = author.posts.create
post_2 = author.posts.create
author.posts # => [post_1, post_2]
Now that’s all fine, but what if I want to specify an explicit order (and I don’t care what the original order was). I tried the following thinking it would override the existing order but it doesn’t.
author.posts.order('created_at DESC') # => [post_1, post_2]
I expected it to reverse the order. What it actually did was to add the order onto the query so that the actual SQL statement looks like this:
SELECT "posts".* FROM "posts" WHERE "posts"."author_id" = 2 ORDER BY created_at ASC, created_at DESC
One solution is to use the reorder method:
author.posts.reorder('created_at DESC') # => [post_2, post_1]
This replaces out any existing ordering.
In my case, I’m just going to use the reverse_order method:
author.posts.reverse_order # => [post_2, post_1]
(Source: api.rubyonrails.org)
In my last post I talked about the need to use lambdas in all scopes that call an existing scope that uses a lambda. An alternative to this is to use class methods instead of scope. So instead of:
scope :active, lambda { started.unfinished }
Use this:
def self.active
started.unfinished
end
Now, the active class method doesn’t need to know that started or unfinished scopes use a lambda.
(Source: rubyflare)
Here’s a gotcha to watch out for! Let’s say you have these scopes in your Rails 3 project:
scope :started, lambda { where("starting_at <= ?", Time.now) }
scope :unfinished, lambda { where("ending_at > ?", Time.now) }
When you call either of these scopes, because you have used lambda, the value of the lambda is re-evaluated each time so that Time.now is dynamic. That’s what you want and what you expect.
Now, when you chain these scopes together:
Event.started.unfinished
The value of Time.now will be re-evaluated each time this is run which is what you want and what you expect.
You end up using this a bit, so you decide to wrap it up into its own scope:
scope :active, started.unfinished
Unfortunately, when you use this new scope, the value of Time.now has now become static which is not what you wanted. You need to use a lambda for the same reason you did with the original scopes.
scope :active, lambda { started.unfinished }
When you think about it, it makes sense. The value of the scope is evaluated at the time the scope is defined. So with the first definition of active, the lambdas in the started and unfinished scopes were evaluated. You need to defer that evaluation, hence the need to wrap those calls inside a lambda as well.
(Source: rails.lighthouseapp.com)
To see the sql generated by a scope method in Rails 3 use the to_sql method. For example:
Post.current.to_sql
=> "SELECT `posts`.* FROM `posts` WHERE (created_at > '2012-02-22')"
If you are using rescue_from to handle an exception then the following spec won’t work:
it "should raise an access denied error" do
expect { get(:new) }.to raise_error(CanCan::AccessDenied)
end
Instead you need to use rspec-rails’s bypass_rescue:
it "should raise an access denied error" do
bypass_rescue
expect { get(:new) }.to raise_error(CanCan::AccessDenied)
end
This works in rspec-rails 1.3 but from what I understand it wasn’t originally available in rspec-rails 2. It is about to make a comeback though.
I just came across this object method in Rails (when did this go in?):
Instead of writing something like:
state = params[:state] if params[:state].present?
you can now do:
state = params[:state].presence
Object.presence is the equivalent of object.present? ? object : nil
See the docs
Question:
When you have the latest and greatest version of Rails installed, how do you create a new app using an older version of Rails?
Answer:
# rails 3:
rails _3.0.11_ new myapp
# rails 2:
rails _2.0.11_ myapp
Thanks to this stack overflow question.
So I pulled in some recent changes to a project, ran bundle install, rake db:migrate and then rake db:test:prepare. I was then greeted with the following error:
uninitialized constant ActiveSupport::Dependencies::Mutex
A quick search provided me with the answer on StackOverflow. Simply upgrade to Rails 2.3.11. The problem was caused by me upgrading to RubyGems 1.6.
If you have a namespaced route, like admin_post_path, you can’t use the resource-oriented style of form_for:
<% form_for @post do |f| %>
...
<% end %>
You need to do a little bit more work:
<% form_for([:admin, @post]) do |f| %>
...
<% end %>
(Source: api.rubyonrails.org)
If you need to use a different name for an activeresource, just set the element_name value:
class Candidate < ActiveResource::Base
self.site = 'http://my_other_app.local'
self.element_name = 'users'
end
(Source: api.rubyonrails.org)