lib/api_resource/finders.rb in api_resource-0.6.16 vs lib/api_resource/finders.rb in api_resource-0.6.17

- old
+ new

@@ -27,46 +27,117 @@ # => 6) Klass.includes(:assoc).find(:all, a => b) def find(*arguments) # make sure we have class data before loading self.load_resource_definition - scope = arguments.slice!(0) - options = arguments.slice!(0) || {} - cond = arguments.slice!(0) + # Conditions sometimes call find, passing themselves as the last arg. + if arguments.last.is_a?(ApiResource::Conditions::AbstractCondition) + cond = arguments.slice!(arguments.length - 1) + else + cond = nil + end + # Support options being passed in as a hash. + options = arguments.extract_options! || {} + + # Remaining arguments are the scope. + scope = arguments + # TODO: Make this into a class attribute properly (if it isn't already) # this is a little bit of a hack because options can sometimes be a Condition expiry = (options.is_a?(Hash) ? options.delete(:expires_in) : nil) || ApiResource::Base.ttl || 0 ApiResource.with_ttl(expiry.to_f) do - case scope - when :all, :first, :last - final_cond = ApiResource::Conditions::ScopeCondition.new({}, self) - # we need new conditions here to take into account options, which could - # either be a Condition object or a hash + + # If we have a condition or options to process... + if cond || options.present? + + # Convert options hash to scope condition. if options.is_a?(Hash) opts = options.with_indifferent_access.delete(:params) || options || {} - final_cond = ApiResource::Conditions::ScopeCondition.new(opts, self) - # cond may be nil - unless cond == nil # THIS MUST BE == NOT nil? - final_cond = cond.merge!(final_cond) + options = ApiResource::Conditions::ScopeCondition.new(opts, self) + end + + # Combine all combinations of conditions and options + if cond + if options + final_cond = cond.merge!(options) + else + final_cond = cond end - elsif options.is_a?(ApiResource::Conditions::AbstractCondition) + elsif options final_cond = options end - # now final cond contains all the conditions we should need to pass to the finder - fnd = ApiResource::Finders::ResourceFinder.new(self, final_cond) - fnd.send(scope) + + # If we have one argument, it's either a word argument + # like first, last, or all, or its a number. + if Array.wrap(scope).size == 1 + scope = scope.first if scope.is_a?(Array) + + # Create the finder with the conditions, then call the scope. + # e.g. Class.scope(1).first + if [:all, :first, :last].include?(scope) + fnd = ApiResource::Finders::ResourceFinder.new(self, final_cond) + fnd.send(scope) + + # If we have no conditions or they are only prefixes or + # includes,and only one argument (not a word) then we + # only have a single item to find. + # e.g. Class.includes.find(1) + elsif final_cond.blank_conditions? || final_cond.conditions.include?(:foreign_key_id) + scope = scope.first if scope.is_a?(Array) + final_cond = final_cond.merge!(ApiResource::Conditions::ScopeCondition.new({:id => scope}, self)) + + ApiResource::Finders::SingleFinder.new(self, final_cond).load + else + + # Otherwise we are chaining a find onto + # the end of a set of conditions. + # e.g. Class.scope(1).find(1) + fnd = final_cond.merge!(ApiResource::Conditions::ScopeCondition.new({:find => {:ids => scope}}, self)) + fnd.send(:all) + end + + else + + # We are searching for multiple ids. + # e.g. Class.scope(1).find(1,2) + fnd = final_cond.merge!(ApiResource::Conditions::ScopeCondition.new({:find => {:ids => scope}}, self)) + fnd.send(:all) + end + else - # in this case scope is the id we want to find, and options should be a condition object or nil - final_cond = ApiResource::Conditions::ScopeCondition.new({:id => scope}, self) - if options.is_a?(ApiResource::Conditions::AbstractCondition) - final_cond = options.merge!(final_cond) - elsif options.is_a?(Hash) - opts = options.with_indifferent_access.delete(:params) || options || {} - final_cond = ApiResource::Conditions::ScopeCondition.new(opts, self).merge!(final_cond) + + # No conditions + if Array.wrap(scope).size == 1 + scope = scope.first if scope.is_a?(Array) + + # We are calling first, last, or all on the class itself. + # e.g. Class.first + if [:all, :first, :last].include?(scope) + final_cond = ApiResource::Conditions::ScopeCondition.new({scope => true}, self) + + fnd = ApiResource::Finders::ResourceFinder.new(self, final_cond) + fnd.send(scope) + else + + # We are performing a simple find of a single object + # e.g. Class.find(1) + scope = scope.first if scope.is_a?(Array) + final_cond = ApiResource::Conditions::ScopeCondition.new({:id => scope}, self) + + ApiResource::Finders::SingleFinder.new(self, final_cond).load + end + + else + # We are performing a find on multiple objects + # e.g. Class.find(1,2) + ApiResource::Conditions::ScopeCondition.new({:find => {:ids => scope}}, self) + fnd.send(:all) + end - ApiResource::Finders::SingleFinder.new(self, final_cond).load + end + end end # A convenience wrapper for <tt>find(:first, *args)</tt>. You can pass \ No newline at end of file