Skip to content

Instantly share code, notes, and snippets.

@alexisbernard
Created January 6, 2010 22:28
Show Gist options
  • Save alexisbernard/270749 to your computer and use it in GitHub Desktop.
Save alexisbernard/270749 to your computer and use it in GitHub Desktop.
Fluent scopes
# Called dynamically named scopes on a model
module Fluent
module Scopes
def self.included(klass)
klass.extend(ClassMethods)
# The code below fixes the following statement:
# @project = Project.find(123)
# @project.developers.apply_scopes(filters).find
# Without the code below the condition where developers.project_id = 123 is erased
# by apply_scopes, because filters are built from the class Developer instead of
# @project.developers. The solution is to create a named scope apply_scopes and then
# to merge the scopes created by apply_scopes.
if klass < ActiveRecord::Base
klass.class_eval do
named_scope :apply_scopes, lambda {|*args|
result = {}
scope = apply_scopes_for_ar(args[0], args[1] || {})
while scope.class == ActiveRecord::NamedScope::Scope
for key in scope.proxy_options.keys
case key
when :conditions then result[:conditions] = merge_conditions(result[:conditions], scope.proxy_options[:conditions])
when :include then result[:include] = merge_includes(result[:include], scope.proxy_options[:include]).uniq
else result[key] = result[key] || scope.proxy_options[key]
end
end
scope = scope.proxy_scope
end
result
}
end
end
end
module ClassMethods
# Call each specified filter from the current class.
# params is a Hash. Each key is a filter name and the value is given to the
# filter
def apply_scopes(params, options = {})
except = options[:except] || []
except = [except] if not except.is_a?(Array)
# association.members.apply_scopes(filters) will squeez conditions from assocition.members
# from is just a temporary hack to fix this anoising behavior. TODO.
#from = options[:from] || self
params.inject(self) do |memo, (name, values)|
next memo if values.blank?
next memo if except.include?(name.to_sym)
next memo if not fluent_filters.include?(name.to_sym)
memo.__send__(name, *values)
end
end
# Needs to avoid an infitine loop with the named scope apply_scopes.
alias_method :apply_scopes_for_ar, :apply_scopes
# Specifies a white list of filters that can be called by Fluent::Scopes::ClassMethods.apply_scopes.
# The white list is a list of symbols.
def set_fluent_filters(*args)
@fluent_filters = args
end
# Returns an Array with filters names can be applied.
def fluent_filters
@fluent_filters ||= []
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment