Skip to content

Commit

Permalink
Hook into ARs scope hash to dynamically create scopes when needed
Browse files Browse the repository at this point in the history
  • Loading branch information
binarylogic committed Mar 8, 2011
1 parent fdd8e3e commit 6eab745
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 24 deletions.
2 changes: 1 addition & 1 deletion lib/searchlogic.rb
Expand Up @@ -30,7 +30,7 @@ class << self; include Searchlogic::ActiveRecord::Consistency; end
end
end

ActiveRecord::NamedScope::Scope.send(:include, Searchlogic::ActiveRecord::Scope)
ActiveRecord::Base.extend(Searchlogic::ActiveRecord::Scope)
ActiveRecord::Base.extend(Searchlogic::ActiveRecord::NamedScopeTools)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Base)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::ColumnConditions)
Expand Down
35 changes: 15 additions & 20 deletions lib/searchlogic/active_record/scope.rb
@@ -1,27 +1,22 @@
module Searchlogic
module ActiveRecord
# Makes sure chained scopes work correctly. ActiveRecord calls scopes.include?(name) in the ActiveRecord::NamedScopes::Scope class
# This presented a problem because searchlogic scopes are called on the fly. So we need to try and create the scope before they
# check for this. Thats what this module is all about.
# The internals to ActiveRecord like to do scopes.include?(scope_name). And this is how they check for the existence
# of scopes, which is terrible. The problem is that searchlogic scopes are dynamically created. So the only solution
# is to override the include? method for the scopes hash, try to create the named scope, and then check it again.
# This shouldn't effect performance because once its created it never gets called again. I also cache failed names
# so we don't try to create them again.
module Scope
def self.included(klass)
klass.class_eval do
alias_method_chain :method_missing, :searchlogic
end
end

private
def method_missing_with_searchlogic(*args)
method = args.first
if !scopes.include?(method)
begin
proxy_scope.proxy_reflection.klass.respond_to?(method)
rescue NoMethodError => e
proxy_scope.respond_to?(method)
def scopes
read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}.tap do |h|
h.instance_eval <<-eval
def include?(key)
result = super
return result if result
super
end
end
method_missing_without_searchlogic(*args)
end
eval
end)
end
end
end
end
8 changes: 5 additions & 3 deletions lib/searchlogic/named_scopes/column_conditions.rb
Expand Up @@ -60,9 +60,7 @@ def condition?(name)
# We want to return true for any conditions that can be called, and while we're at it. We might as well
# create the condition so we don't have to do it again.
def respond_to?(*args)
name = args.first
result = super
(!result && self != ::ActiveRecord::Base && !create_condition(name).blank?) || result
super || (self != ::ActiveRecord::Base && !create_condition(args.first).blank?)
end

private
Expand Down Expand Up @@ -94,6 +92,10 @@ def condition_details(method_name)
end

def create_condition(name)
@conditions_already_tried ||= []
return nil if @conditions_already_tried.include?(name.to_s)
@conditions_already_tried << name.to_s

if details = condition_details(name)
if PRIMARY_CONDITIONS.include?(details[:condition].to_sym)
create_primary_condition(details[:column], details[:condition])
Expand Down

0 comments on commit 6eab745

Please sign in to comment.