27.02.2013 Views

Rails%203%20In%20Action

Rails%203%20In%20Action

Rails%203%20In%20Action

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

170 CHAPTER 8 More authorization<br />

The scope method provides a method you can call on your class or on an association<br />

collection for this class that returns a subset of records. The following scope call,<br />

for example, defines a method called admins:<br />

scope :admins, where(:admin => true)<br />

If you wanted to, you could call this admins method on your User model to return all<br />

the users who are admins:<br />

User.admins<br />

If you didn’t have the scope method, you’d have to specify the where manually on<br />

your queries everywhere you used them, like this:<br />

User.where(:admin => true)<br />

As you can see, manually specifying where isn’t nearly as pretty as simply calling<br />

User.admins. This may seem like a contrived example, but trust us: it gets ugly when<br />

the conditions become more complex. Scopes are yet another great example of the<br />

DRY (Don’t Repeat Yourself) convention seen throughout Rails. Because the scope<br />

method defines your scope’s logic in one central location, you can easily change all<br />

uses of this scope by changing it in this one spot.<br />

Scopes are also chainable. Imagine that you had another scope defined on your<br />

User model, such as the following, as well as a field for storing the gender, appropriately<br />

called gender:<br />

scope :female, where(:gender => "Female")<br />

You can call this scope by itself<br />

User.female<br />

which return all of your female users, or you can get all your female admins by doing<br />

either this<br />

User.admin.female<br />

or this<br />

User.female.admin<br />

Rails builds up the queries by applying the scopes one at a time, and calling them in<br />

any order will result in the same query.<br />

Let’s define a real scope now, along with the permissions association it needs to<br />

use. Put this scope under the validation inside the Project model, as shown in the following<br />

lines:<br />

validates :name, :presence => true, :uniqueness => true<br />

B<br />

Link to thing<br />

has_many :permissions, :as => :thing<br />

association<br />

scope :readable_by, lambda { |user|<br />

joins(:permissions).where(:permissions => { :action => "view",<br />

:user_id => user.id })<br />

}

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!