module BetterAr module FinderMethods # Breaks down the hash to do a {ActiveRecord::Relation} query # # example: # User.all(:age => 10, :limit! => 2, :offset! => 3, :order! => :name) # # is the same as: # User.where(:age => 10).limit(2).offset(3).order(:name) # # if the key :conditions is present it will fall back to legacy # # any key with the '!' at the end will be assumed to be a sql operator. The key should match either an {ActiveRecord::Relation} instance method or an ARel predicate. # # Implicit joins are supported. # example: # User.all(:records => {:name => 'test'}) # # is the same as: # User.joins(:records).where(:records => {:name => 'test'}) # # @param [Hash] # Optional # @return [ActiveRecord::Relation] def all(opts = {}) if opts.is_a?(Hash) if opts.empty? super() elsif opts.key?(:conditions) super(opts) else relation = clone relation = better_ar_apply_sql_clauses(relation, opts) relation = better_ar_apply_associations(relation, opts) relation.where(opts) end else relation = clone relation.where(opts) end end # Forces a limit of 1 on the collection # # example: # User.first(:age => 10, :name => 'Brian') # # is the same as: # User.where(:age => 10, :name => 'Brian').limit(1).first # # if the key :conditions is present it will fall back to legacy # # @param [Hash] # Optional follows same convention as {#all} # @return [ActiveRecord::Base] def first(opts = {}) if opts.empty? super() elsif opts.key?(:conditions) super(opts) else all(opts.merge(:limit! => 1)).first end end private def better_ar_apply_sql_clauses(relation, opts = {}) opts.keys.select { |key| key.to_s =~ /!$/ }.each do |clause| if value = opts.delete(clause) relation = relation.send(clause.to_s.sub('!',''), value) end end relation end def better_ar_apply_associations(relation, opts = {}) reflect_on_all_associations.select do |association| !association.options[:polymorphic] && better_ar_join_keys(opts).include?(association.table_name.to_sym) end.each do |association| relation = relation.joins(association.name) end relation end def better_ar_join_keys(opts = {}) opts.keys.select { |key| !opts[key].kind_of?(ActiveRecord::Base) } end end end ActiveRecord::Relation.send(:include, BetterAr::FinderMethods)