Skip to content

Commit

Permalink
Merge pull request #49 from signpost/query_improvement
Browse files Browse the repository at this point in the history
Querying improvements
  • Loading branch information
guilleiguaran committed Feb 22, 2013
2 parents c285a02 + acdacea commit 5d8cb4e
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 6 deletions.
18 changes: 13 additions & 5 deletions lib/active_resource/base.rb
Expand Up @@ -891,6 +891,11 @@ def all(*args)
find(:all, *args)
end

def where(clauses = {})
raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash
find(:all, :params => clauses)
end


# Deletes the resources with the ID in the +id+ parameter.
#
Expand Down Expand Up @@ -943,14 +948,14 @@ def find_every(options)
begin
case from = options[:from]
when Symbol
instantiate_collection(get(from, options[:params]))
instantiate_collection(get(from, options[:params]), options[:params])
when String
path = "#{from}#{query_string(options[:params])}"
instantiate_collection(format.decode(connection.get(path, headers).body) || [])
instantiate_collection(format.decode(connection.get(path, headers).body) || [], options[:params])
else
prefix_options, query_options = split_options(options[:params])
path = collection_path(prefix_options, query_options)
instantiate_collection( (format.decode(connection.get(path, headers).body) || []), prefix_options )
instantiate_collection( (format.decode(connection.get(path, headers).body) || []), query_options, prefix_options )
end
rescue ActiveResource::ResourceNotFound
# Swallowing ResourceNotFound exceptions and return nil - as per
Expand All @@ -977,8 +982,11 @@ def find_single(scope, options)
instantiate_record(format.decode(connection.get(path, headers).body), prefix_options)
end

def instantiate_collection(collection, prefix_options = {})
collection_parser.new(collection).collect! { |record| instantiate_record(record, prefix_options) }
def instantiate_collection(collection, original_params = {}, prefix_options = {})
collection_parser.new(collection).tap do |parser|
parser.resource_class = self
parser.original_params = original_params
end.collect! { |record| instantiate_record(record, prefix_options) }
end

def instantiate_record(record, prefix_options = {})
Expand Down
14 changes: 13 additions & 1 deletion lib/active_resource/collection.rb
@@ -1,12 +1,13 @@
require 'active_support/core_ext/module/delegation'
require 'active_support/inflector'

module ActiveResource # :nodoc:
class Collection # :nodoc:
include Enumerable
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :size, :last, :first, :[], :to => :to_a

# The array of actual elements returned by index actions
attr_accessor :elements
attr_accessor :elements, :resource_class, :original_params

# ActiveResource::Collection is a wrapper to handle parsing index responses that
# do not directly map to Rails conventions.
Expand Down Expand Up @@ -69,5 +70,16 @@ def collect!
end
alias map! collect!

def first_or_create(attributes = {})
first || resource_class.create(original_params.update(attributes))
rescue NoMethodError
raise "Cannot create resource from resource type: #{resource_class.inspect}"
end

def first_or_initialize(attributes = {})
first || resource_class.new(original_params.update(attributes))
rescue NoMethodError
raise "Cannot build resource from resource type: #{resource_class.inspect}"
end
end
end
40 changes: 40 additions & 0 deletions test/cases/collection_test.rb
Expand Up @@ -8,9 +8,26 @@ class BasicCollectionTest < CollectionTest
def test_collection_respond_to_collect!
assert @collection.respond_to?(:collect!)
end

def test_collection_respond_to_map!
assert @collection.respond_to?(:map!)
end

def test_collection_respond_to_first_or_create
assert @collection.respond_to?(:first_or_create)
end

def test_collection_respond_to_first_or_initialize
assert @collection.respond_to?(:first_or_initialize)
end

def test_first_or_create_without_resource_class_raises_error
assert_raise(RuntimeError) { @collection.first_or_create }
end

def test_first_or_initialize_without_resource_class_raises_error
assert_raise(RuntimeError) { @collection.first_or_initialize }
end

def test_collect_bang_modifies_elements
elements = %w(a b c)
Expand Down Expand Up @@ -51,18 +68,41 @@ def setup
@posts_hash = {"results" => [@post], :next_page => '/paginated_posts.json?page=2'}
@posts = @posts_hash.to_json
@posts2 = {"results" => [@post.merge({:id => 2})], :next_page => nil}.to_json

@empty_posts = { "results" => [], :next_page => nil }.to_json
@new_post = { :id => nil, :title => nil }.to_json
ActiveResource::HttpMock.respond_to do |mock|
mock.get '/paginated_posts.json', {}, @posts
mock.get '/paginated_posts/new.json', {}, @new_post
mock.get '/paginated_posts.json?page=2', {}, @posts
mock.get '/paginated_posts.json?title=test', {}, @empty_posts
mock.post '/paginated_posts.json', {}, nil
end
end

def test_setting_collection_parser
assert_kind_of PaginatedCollection, PaginatedPost.find(:all)
end

def test_setting_collection_parser_resource_class
assert_equal PaginatedPost, PaginatedPost.where(:page => 2).resource_class
end

def test_setting_collection_parser_original_params
assert_equal({:page => 2}, PaginatedPost.where(:page => 2).original_params)
end

def test_custom_accessor
assert_equal PaginatedPost.find(:all).next_page, @posts_hash[:next_page]
end

def test_first_or_create
post = PaginatedPost.where(:title => 'test').first_or_create
assert post.valid?
end

def test_first_or_initialize
post = PaginatedPost.where(:title => 'test').first_or_initialize
assert post.valid?
end
end

0 comments on commit 5d8cb4e

Please sign in to comment.