WiceGrid and with_scope
Methods with_scope
and with_exclusive_scope
in ActiveRecord scope parameters to method calls within the block. They can be nested, and when with_scope
blocks are nested, the scope of the inner block is the merge of all the parent scopes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Task < ActiveRecord::Base
class << self
def foo
find(:all) # no conditions
with_scope(:find => { :conditions => "priority_id is null" }) do
find(:all) # :conditions => "priority_id is null"
with_scope(:find => { :conditions => "relevant_version_id is null" }) do
find(:all) # :conditions => "priority_id is null and relevant_version_id is null"
end
find(:all) # back to :conditions => "priority_id is null"
end
end
end
end
with_exclusive_scope
doesn’t merge scope with the parent blocks:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def foo
find(:all) # no conditions
with_scope(:find => { :conditions => "priority_id is null" }) do
find(:all) # :conditions => "priority_id is null"
with_scope(:find => { :conditions => "relevant_version_id is null" }) do
find(:all) # :conditions => "priority_id is null and relevant_version_id is null"
with_exclusive_scope(:find => { :conditions => "relevant_version_id is not null" }) do
find(:all) # :conditions => "relevant_version_id is not null"
end
end
find(:all) # back to :conditions => "priority_id is null"
end
end
If you look in the source code of with_scope
, you will see how simple and elegant it really is, making use of the power of Ruby blocks.
Essentially, an ActiveRecord class object has a stack called scoped_methods
, before yield
a new scope structure is pushed to it, after the block is done executing, the last element is popped:
1
2
3
4
5
6
self.scoped_methods << method_scoping
begin
yield
ensure
self.scoped_methods.pop
end
In case of with_scope
method_scoping
is the merge of all previous elements in scoped_methods
, in case of with_exclusive_scope
method_scoping equals to the first argument to with_exclusive_scope
.
This is how it works:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def foo
p self.scoped_methods #=> []
with_scope(:find => { :conditions => "priority_id is null" }) do
p self.scoped_methods #=> [{:find=>{:conditions=>"priority_id is null"}}]
with_scope(:find => { :conditions => "relevant_version_id is null" }) do
p self.scoped_methods #=> [{:find=>{:conditions=>"priority_id is null"}},
# {:find=>{:conditions=>"(priority_id is null) AND (relevant_version_id is null)"}}]
with_exclusive_scope(:find => { :conditions => "relevant_version_id is not null" }) do
p self.scoped_methods #=> [{:find=>{:conditions=>"priority_id is null"}},
# {:find=>{:conditions=>"(priority_id is null) AND (relevant_version_id is null)"}},
# {:find=>{:conditions=>"relevant_version_id is not null"}}]
end
with_exclusive_scope(:find => { }) do
p self.scoped_methods #=> [{:find=>{:conditions=>"priority_id is null"}},
# {:find=>{:conditions=>"(priority_id is null) AND (relevant_version_id is null)"}},
# {:find=>{}}]
end
end
end
end
Until recently WiceGrid was ignoring with_exclusive_scope
and with_scope
completely. The reason for this is the lazy nature of WiceGrid – the real ActiveRecord#find
call doesn’t happen in initialize_grid
, but later. Beginning with this commit WiceGrid supports with_exclusive_scope
and with_scope
. Knowing how with_scope
works the solution is dead simple – remember the last element in scoped_methods
, when in initialize_grid
, and later run ActiveRecord#find from with with_exclusive_scope
block, submitting the remembered method scope to with_exclusive_scope
.
Happy Rubying :-) !