From 0a0b151cde699b359b4488f9764173373778d943 Mon Sep 17 00:00:00 2001 From: Jean Luis Urena Date: Sat, 23 Nov 2024 12:58:59 -0500 Subject: [PATCH 1/5] [ISSUE-408] Lazy network calls for collections This is helpful so that `where` method calls can be chained --- lib/active_resource/associations.rb | 2 +- lib/active_resource/base.rb | 57 +++++++++++++++------------ lib/active_resource/collection.rb | 61 +++++++++++++++++++++++------ test/cases/association_test.rb | 4 +- test/cases/collection_test.rb | 25 +----------- 5 files changed, 86 insertions(+), 63 deletions(-) diff --git a/lib/active_resource/associations.rb b/lib/active_resource/associations.rb index 7d27bba2f8..09c8c6e274 100644 --- a/lib/active_resource/associations.rb +++ b/lib/active_resource/associations.rb @@ -150,7 +150,7 @@ def defines_has_many_finder_method(reflection) elsif !new_record? instance_variable_set(ivar_name, reflection.klass.find(:all, params: { "#{self.class.element_name}_id": self.id })) else - instance_variable_set(ivar_name, self.class.collection_parser.new) + instance_variable_set(ivar_name, reflection.klass.find(:all)) end end end diff --git a/lib/active_resource/base.rb b/lib/active_resource/base.rb index 29376f081d..0d178643a3 100644 --- a/lib/active_resource/base.rb +++ b/lib/active_resource/base.rb @@ -776,6 +776,33 @@ def format_extension include_format_in_path ? ".#{format.extension}" : "" end + # Instantiates a new record with the given options. + # + # This method creates a new instance of the class with the provided record and sets its `prefix_options` attribute. + # + # ==== Options + # + # +record+ [Object] The record to be instantiated. + # +prefix_options+ [Hash, nil] Optional hash containing prefix options for the resource. Defaults to an empty hash. + # + # ==== Returns + # + # [Object] The newly instantiated resource. + # + # ==== Examples + # + # MyResource.instantiate_record(record) + # # Creates a new MyResource instance with default prefix options. + # + # MyResource.instantiate_record(record, { prefix: "admin" }) + # # Creates a new MyResource instance with prefix set to "admin". + # + def instantiate_record(record, prefix_options = {}) + new(record, true).tap do |resource| + resource.prefix_options = prefix_options + end + end + # Gets the element path for the given ID in +id+. If the +query_options+ parameter is omitted, Rails # will split from the \prefix options. # @@ -1096,23 +1123,12 @@ def find_every(options) params = options[:params] prefix_options, query_options = split_options(params) - response = - case from = options[:from] - when Symbol - get(from, params) - when String - path = "#{from}#{query_string(query_options)}" - format.decode(connection.get(path, headers).body) - else - path = collection_path(prefix_options, query_options) - format.decode(connection.get(path, headers).body) - end - - instantiate_collection(response || [], query_options, prefix_options) - rescue ActiveResource::ResourceNotFound - # Swallowing ResourceNotFound exceptions and return nil - as per - # ActiveRecord. - nil + collection_parser.new([], options[:from]).tap do |parser| + parser.resource_class = self + parser.original_params = query_options + parser.prefix_options = prefix_options + parser.path_params = params + end end # Find a single resource from a one-off URL @@ -1140,13 +1156,6 @@ def instantiate_collection(collection, original_params = {}, prefix_options = {} end.collect! { |record| instantiate_record(record, prefix_options) } end - def instantiate_record(record, prefix_options = {}) - new(record, true).tap do |resource| - resource.prefix_options = prefix_options - end - end - - # Accepts a URI and creates the site URI from that. def create_site_uri_from(site) site.is_a?(URI) ? site.dup : URI.parse(site) diff --git a/lib/active_resource/collection.rb b/lib/active_resource/collection.rb index 1093ab3fb8..f862365659 100644 --- a/lib/active_resource/collection.rb +++ b/lib/active_resource/collection.rb @@ -5,12 +5,14 @@ module ActiveResource # :nodoc: class Collection # :nodoc: - SELF_DEFINE_METHODS = [:to_a, :collect!, :map!, :all?] + SELF_DEFINE_METHODS = [:to_a, :all?] include Enumerable delegate :to_yaml, :all?, *(Array.instance_methods(false) - SELF_DEFINE_METHODS), to: :to_a # The array of actual elements returned by index actions - attr_accessor :elements, :resource_class, :original_params + attr_accessor :elements, :resource_class, :original_params, :path_params + attr_writer :prefix_options + attr_reader :from # ActiveResource::Collection is a wrapper to handle parsing index responses that # do not directly map to Rails conventions. @@ -41,7 +43,7 @@ class Collection # :nodoc: # # class PostCollection < ActiveResource::Collection # attr_accessor :next_page - # def initialize(parsed = {}) + # def parse_response(parsed = {}) # @elements = parsed['posts'] # @next_page = parsed['next_page'] # end @@ -54,24 +56,52 @@ class Collection # :nodoc: # @posts.next_page # => "/posts.json?page=2" # @posts.map(&:id) # =>[1, 3, 5 ...] # - # The initialize method will receive the ActiveResource::Formats parsed result + # The ActiveResource::Collection#parse_response method will receive the ActiveResource::Formats parsed result # and should set @elements. - def initialize(elements = []) + def initialize(elements = [], from = nil) + @from = from @elements = elements + # This can get called without a response, so parse only if response is present + parse_response(@elements) if @elements.present? end - def to_a - elements + def parse_response(elements) + @elements = elements || [] + end + + def prefix_options + @prefix_options || {} end - def collect! - return elements unless block_given? - set = [] - each { |o| set << yield(o) } - @elements = set + # Makes network request to get the elements and returns self + def call + to_a self end - alias map! collect! + + def to_a + response = + case from + when Symbol + resource_class.get(from, path_params) + when String + path = "#{from}#{query_string(original_params)}" + resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) + else + path = resource_class.collection_path(prefix_options, original_params) + resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) + end + + # Update the elements + parse_response(response) + @elements = @elements.map do |element| + resource_class.instantiate_record(element, prefix_options) + end + rescue ActiveResource::ResourceNotFound + # Swallowing ResourceNotFound exceptions and return nothing - as per ActiveRecord. + # Needs to be empty array as Array methods are delegated + [] + end def first_or_create(attributes = {}) first || resource_class.create(original_params.update(attributes)) @@ -90,5 +120,10 @@ def where(clauses = {}) new_clauses = original_params.merge(clauses) resource_class.where(new_clauses) end + + private + def query_string(options) + "?#{options.to_query}" unless options.nil? || options.empty? + end end end diff --git a/test/cases/association_test.rb b/test/cases/association_test.rb index f612fc5af7..99523a95ea 100644 --- a/test/cases/association_test.rb +++ b/test/cases/association_test.rb @@ -43,8 +43,8 @@ def test_has_many def test_has_many_on_new_record Post.send(:has_many, :topics) - Topic.stubs(:find).returns([:unexpected_response]) - assert_equal [], Post.new.topics.to_a + + assert_kind_of ActiveResource::Collection, Post.new.topics end def test_has_one diff --git a/test/cases/collection_test.rb b/test/cases/collection_test.rb index 94a5496ecd..141fd7e5a5 100644 --- a/test/cases/collection_test.rb +++ b/test/cases/collection_test.rb @@ -9,14 +9,6 @@ def setup end 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 @@ -33,19 +25,6 @@ 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) - @collection.elements = elements - results = @collection.collect! { |i| i + "!" } - assert_equal results.to_a, elements.collect! { |i| i + "!" } - end - - def test_collect_bang_returns_collection - @collection.elements = %w(a) - results = @collection.collect! { |i| i + "!" } - assert_kind_of ActiveResource::Collection, results - end - def respond_to_where assert @collection.respond_to?(:where) end @@ -53,7 +32,7 @@ def respond_to_where class PaginatedCollection < ActiveResource::Collection attr_accessor :next_page - def initialize(parsed = {}) + def parse_response(parsed) @elements = parsed["results"] @next_page = parsed["next_page"] end @@ -102,7 +81,7 @@ def test_setting_collection_parser_original_params end def test_custom_accessor - assert_equal PaginatedPost.find(:all).next_page, @posts_hash[:next_page] + assert_equal PaginatedPost.find(:all).call.next_page, @posts_hash[:next_page] end def test_first_or_create From b2836d9b8f26f5a20abefbc23ef5606625ba79a0 Mon Sep 17 00:00:00 2001 From: Jean Luis Urena Date: Sat, 23 Nov 2024 13:01:02 -0500 Subject: [PATCH 2/5] [ISSUE-408] Rename original_params to query_params --- lib/active_resource/base.rb | 2 +- lib/active_resource/collection.rb | 12 ++++++------ test/cases/collection_test.rb | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/active_resource/base.rb b/lib/active_resource/base.rb index 0d178643a3..712347bd78 100644 --- a/lib/active_resource/base.rb +++ b/lib/active_resource/base.rb @@ -1125,7 +1125,7 @@ def find_every(options) collection_parser.new([], options[:from]).tap do |parser| parser.resource_class = self - parser.original_params = query_options + parser.query_params = query_options parser.prefix_options = prefix_options parser.path_params = params end diff --git a/lib/active_resource/collection.rb b/lib/active_resource/collection.rb index f862365659..7d7be1a03d 100644 --- a/lib/active_resource/collection.rb +++ b/lib/active_resource/collection.rb @@ -10,7 +10,7 @@ class Collection # :nodoc: delegate :to_yaml, :all?, *(Array.instance_methods(false) - SELF_DEFINE_METHODS), to: :to_a # The array of actual elements returned by index actions - attr_accessor :elements, :resource_class, :original_params, :path_params + attr_accessor :elements, :resource_class, :query_params, :path_params attr_writer :prefix_options attr_reader :from @@ -85,10 +85,10 @@ def to_a when Symbol resource_class.get(from, path_params) when String - path = "#{from}#{query_string(original_params)}" + path = "#{from}#{query_string(query_params)}" resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) else - path = resource_class.collection_path(prefix_options, original_params) + path = resource_class.collection_path(prefix_options, query_params) resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) end @@ -104,20 +104,20 @@ def to_a end def first_or_create(attributes = {}) - first || resource_class.create(original_params.update(attributes)) + first || resource_class.create(query_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)) + first || resource_class.new(query_params.update(attributes)) rescue NoMethodError raise "Cannot build resource from resource type: #{resource_class.inspect}" end def where(clauses = {}) raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash - new_clauses = original_params.merge(clauses) + new_clauses = query_params.merge(clauses) resource_class.where(new_clauses) end diff --git a/test/cases/collection_test.rb b/test/cases/collection_test.rb index 141fd7e5a5..23dcda9fbf 100644 --- a/test/cases/collection_test.rb +++ b/test/cases/collection_test.rb @@ -76,8 +76,8 @@ 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) + def test_setting_collection_parser_query_params + assert_equal({ page: 2 }, PaginatedPost.where(page: 2).query_params) end def test_custom_accessor From ea9a994a8d6b7d1219e671fb52ac63e7af0ab0b0 Mon Sep 17 00:00:00 2001 From: Jean Luis Urena Date: Sat, 23 Nov 2024 13:17:55 -0500 Subject: [PATCH 3/5] [ISSUE-408] Add a spec for lazy network calls --- test/cases/collection_test.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/cases/collection_test.rb b/test/cases/collection_test.rb index 23dcda9fbf..6880fcf61d 100644 --- a/test/cases/collection_test.rb +++ b/test/cases/collection_test.rb @@ -52,6 +52,7 @@ class ReduxCollection < ActiveResource::Base class CollectionInheritanceTest < ActiveSupport::TestCase def setup @post = { id: 1, title: "Awesome" } + @post_even_more = { id: 1, title: "Awesome", subtitle: "EvenMore" } @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 @@ -64,6 +65,7 @@ def setup mock.get "/paginated_posts.json?page=2", {}, @posts mock.get "/paginated_posts.json?title=test", {}, @empty_posts mock.get "/paginated_posts.json?page=2&title=Awesome", {}, @posts + mock.get "/paginated_posts.json?subtitle=EvenMore&title=Awesome", {}, @posts mock.post "/paginated_posts.json", {}, nil end end @@ -99,4 +101,17 @@ def test_where next_posts = posts.where(title: "Awesome") assert_kind_of PaginatedCollection, next_posts end + + def test_where_lazy_chain + expected_request = ActiveResource::Request.new( + :get, + "/paginated_posts.json?subtitle=EvenMore&title=Awesome", + nil, + { "Accept" => "application/json" } + ) + posts = PaginatedPost.where(title: "Awesome").where(subtitle: "EvenMore") + assert_equal 0, ActiveResource::HttpMock.requests.count { |r| r == expected_request } + posts.to_a + assert_equal 1, ActiveResource::HttpMock.requests.count { |r| r == expected_request } + end end From b570702d13ef267f410b385ffad027c8dfe7342d Mon Sep 17 00:00:00 2001 From: Jean Luis Urena Date: Sat, 23 Nov 2024 18:05:12 -0500 Subject: [PATCH 4/5] [ISSUE-408] Refactored Collection Simplified delegation of Array, added docs --- lib/active_resource/collection.rb | 120 ++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 29 deletions(-) diff --git a/lib/active_resource/collection.rb b/lib/active_resource/collection.rb index 7d7be1a03d..a679cb67f4 100644 --- a/lib/active_resource/collection.rb +++ b/lib/active_resource/collection.rb @@ -5,11 +5,9 @@ module ActiveResource # :nodoc: class Collection # :nodoc: - SELF_DEFINE_METHODS = [:to_a, :all?] include Enumerable - delegate :to_yaml, :all?, *(Array.instance_methods(false) - SELF_DEFINE_METHODS), to: :to_a + delegate :to_yaml, *Array.public_instance_methods(false), to: :fetch_resources! - # The array of actual elements returned by index actions attr_accessor :elements, :resource_class, :query_params, :path_params attr_writer :prefix_options attr_reader :from @@ -65,56 +63,92 @@ def initialize(elements = [], from = nil) parse_response(@elements) if @elements.present? end + # Processes and sets the collection elements. This method assigns the provided `elements` + # (or an empty array if none provided) to the `@elements` instance variable. + # + # ==== Arguments + # + # +elements+ (Array) - An optional array of resources to be set as the collection elements. + # Defaults to an empty array. + # + # This method is called after fetching the resource and can be overridden by subclasses to + # handle any specific response format of the API. def parse_response(elements) @elements = elements || [] end + # Returns the prefix options for the collection, which are used for constructing the resource path. + # + # ==== Returns + # + # [Hash] The prefix options for the collection. def prefix_options @prefix_options || {} end - # Makes network request to get the elements and returns self + # Executes the request to fetch the collection of resources from the API and returns the collection. + # + # ==== Returns + # + # [ActiveResource::Collection] The collection of resources. def call - to_a + fetch_resources! self end - def to_a - response = - case from - when Symbol - resource_class.get(from, path_params) - when String - path = "#{from}#{query_string(query_params)}" - resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) - else - path = resource_class.collection_path(prefix_options, query_params) - resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) - end - - # Update the elements - parse_response(response) - @elements = @elements.map do |element| - resource_class.instantiate_record(element, prefix_options) - end - rescue ActiveResource::ResourceNotFound - # Swallowing ResourceNotFound exceptions and return nothing - as per ActiveRecord. - # Needs to be empty array as Array methods are delegated - [] - end - + # Returns the first resource in the collection, or creates a new resource using the provided + # attributes if the collection is empty. + # + # ==== Arguments + # + # +attributes+ (Hash) - The attributes for creating the resource. + # + # ==== Returns + # + # [Object] The first resource, or a newly created resource if none exist. + # + # ==== Example + # post = PostCollection.where(title: "New Post").first_or_create + # # => Post instance with title "New Post" def first_or_create(attributes = {}) first || resource_class.create(query_params.update(attributes)) rescue NoMethodError raise "Cannot create resource from resource type: #{resource_class.inspect}" end + # Returns the first resource in the collection, or initializes a new resource using the provided + # attributes if the collection is empty. + # + # ==== Arguments + # + # +attributes+ (Hash) - The attributes for initializing the resource. + # + # ==== Returns + # + # [Object] The first resource, or a newly initialized resource if none exist. + # + # ==== Example + # post = PostCollection.where(title: "New Post").first_or_initialize + # # => Post instance with title "New Post" def first_or_initialize(attributes = {}) first || resource_class.new(query_params.update(attributes)) rescue NoMethodError raise "Cannot build resource from resource type: #{resource_class.inspect}" end + # Filters the collection based on the provided clauses (query parameters). + # + # ==== Arguments + # + # +clauses+ (Hash) - A hash of query parameters used to filter the collection. + # + # ==== Returns + # + # [ActiveResource::Collection] A new collection filtered by the specified clauses. + # + # ==== Example + # filtered_posts = PostCollection.where(title: "Post 1") + # # => PostCollection:xxx (filtered collection) def where(clauses = {}) raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash new_clauses = query_params.merge(clauses) @@ -125,5 +159,33 @@ def where(clauses = {}) def query_string(options) "?#{options.to_query}" unless options.nil? || options.empty? end + + # Fetches resources from the API and parses the response. The resources are then mapped to their respective + # resource class instances. + # + # ==== Returns + # + # [Array] The collection of resources retrieved from the API. + def fetch_resources! + response = + case from + when Symbol + resource_class.get(from, path_params) + when String + path = "#{from}#{query_string(query_params)}" + resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) + else + path = resource_class.collection_path(prefix_options, query_params) + resource_class.format.decode(resource_class.connection.get(path, resource_class.headers).body) + end + + # Update the elements + parse_response(response) + @elements.map! { |e| resource_class.instantiate_record(e, prefix_options) } + rescue ActiveResource::ResourceNotFound + # Swallowing ResourceNotFound exceptions and return nothing - as per ActiveRecord. + # Needs to be empty array as Array methods are delegated + [] + end end end From c4347c9f1c8b944205cf5073596103e3a7100fff Mon Sep 17 00:00:00 2001 From: Jean Luis Urena Date: Sun, 24 Nov 2024 10:19:07 -0500 Subject: [PATCH 5/5] [ISSUE-408] Refactored and improve lazy collection Similar to Rails ORM, check if request was made and re-use that instead of making another http call. --- lib/active_resource/collection.rb | 33 ++++++++++++++++--- test/cases/collection_test.rb | 55 +++++++++++++++++++++++++++++++ test/cases/connection_test.rb | 2 +- 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/lib/active_resource/collection.rb b/lib/active_resource/collection.rb index a679cb67f4..0c4ee482bb 100644 --- a/lib/active_resource/collection.rb +++ b/lib/active_resource/collection.rb @@ -6,9 +6,9 @@ module ActiveResource # :nodoc: class Collection # :nodoc: include Enumerable - delegate :to_yaml, *Array.public_instance_methods(false), to: :fetch_resources! + delegate :to_yaml, *Array.public_instance_methods(false), to: :request_resources! - attr_accessor :elements, :resource_class, :query_params, :path_params + attr_accessor :resource_class, :query_params, :path_params attr_writer :prefix_options attr_reader :from @@ -59,6 +59,7 @@ class Collection # :nodoc: def initialize(elements = [], from = nil) @from = from @elements = elements + @requested = false # This can get called without a response, so parse only if response is present parse_response(@elements) if @elements.present? end @@ -86,16 +87,35 @@ def prefix_options @prefix_options || {} end + # Refreshes the collection by re-fetching the resources from the API. + # + # ==== Returns + # + # [Array] The collection of resources retrieved from the API. + def refresh + @requested = false + request_resources! + end + # Executes the request to fetch the collection of resources from the API and returns the collection. # # ==== Returns # # [ActiveResource::Collection] The collection of resources. def call - fetch_resources! + request_resources! self end + # Checks if the collection has been requested. + # + # ==== Returns + # + # [Boolean] true if the collection has been requested, false otherwise. + def requested? + @requested + end + # Returns the first resource in the collection, or creates a new resource using the provided # attributes if the collection is empty. # @@ -160,13 +180,14 @@ def query_string(options) "?#{options.to_query}" unless options.nil? || options.empty? end - # Fetches resources from the API and parses the response. The resources are then mapped to their respective + # Requests resources from the API and parses the response. The resources are then mapped to their respective # resource class instances. # # ==== Returns # # [Array] The collection of resources retrieved from the API. - def fetch_resources! + def request_resources! + return @elements if requested? response = case from when Symbol @@ -186,6 +207,8 @@ def fetch_resources! # Swallowing ResourceNotFound exceptions and return nothing - as per ActiveRecord. # Needs to be empty array as Array methods are delegated [] + ensure + @requested = true end end end diff --git a/test/cases/collection_test.rb b/test/cases/collection_test.rb index 6880fcf61d..fa43a1d1e5 100644 --- a/test/cases/collection_test.rb +++ b/test/cases/collection_test.rb @@ -66,6 +66,8 @@ def setup mock.get "/paginated_posts.json?title=test", {}, @empty_posts mock.get "/paginated_posts.json?page=2&title=Awesome", {}, @posts mock.get "/paginated_posts.json?subtitle=EvenMore&title=Awesome", {}, @posts + mock.get "/paginated_posts.json?title=notfound", {}, nil, 404 + mock.get "/paginated_posts.json?title=internalservererror", {}, nil, 500 mock.post "/paginated_posts.json", {}, nil end end @@ -110,8 +112,61 @@ def test_where_lazy_chain { "Accept" => "application/json" } ) posts = PaginatedPost.where(title: "Awesome").where(subtitle: "EvenMore") + assert_not posts.requested? assert_equal 0, ActiveResource::HttpMock.requests.count { |r| r == expected_request } + # Call twice to ensure the request is only made once posts.to_a + posts.to_a + assert_equal 1, ActiveResource::HttpMock.requests.count { |r| r == expected_request } + assert posts.requested? + end + + def test_where_lazy_chain_with_no_results + posts = PaginatedPost.where(title: "notfound") + assert_not posts.requested? + assert_equal [], posts.to_a + assert posts.requested? + end + + def test_where_lazy_chain_internal_server_error + posts = PaginatedPost.where(title: "internalservererror") + assert_not posts.requested? + assert_raise ActiveResource::ServerError do + posts.to_a + end + assert posts.requested? + end + + def test_refresh + expected_request = ActiveResource::Request.new( + :get, + "/paginated_posts.json?page=2", + @posts, + { "Accept" => "application/json" } + ) + posts = PaginatedPost.where(page: 2) + + assert_not posts.requested? + posts.to_a + assert posts.requested? + assert_equal 1, ActiveResource::HttpMock.requests.count { |r| r == expected_request } + posts.refresh + assert_equal 2, ActiveResource::HttpMock.requests.count { |r| r == expected_request } + assert posts.requested? + end + + def test_call + expected_request = ActiveResource::Request.new( + :get, + "/paginated_posts.json?page=2", + @posts, + { "Accept" => "application/json" } + ) + posts = PaginatedPost.where(page: 2) + + assert_not posts.requested? + assert_kind_of PaginatedCollection, posts.call + assert posts.requested? assert_equal 1, ActiveResource::HttpMock.requests.count { |r| r == expected_request } end end diff --git a/test/cases/connection_test.rb b/test/cases/connection_test.rb index a4265f4dde..bc30aef3ad 100644 --- a/test/cases/connection_test.rb +++ b/test/cases/connection_test.rb @@ -258,7 +258,7 @@ def test_accept_http_header @http = mock("new Net::HTTP") @conn.expects(:http).returns(@http) path = "/people/1.xml" - @http.expects(:get).with(path, "Accept" => "application/xhtml+xml").returns(ActiveResource::Response.new(@matz, 200, "Content-Type" => "text/xhtml")) + @http.expects(:get).with(path, { "Accept" => "application/xhtml+xml" }).returns(ActiveResource::Response.new(@matz, 200, { "Content-Type" => "text/xhtml" })) assert_nothing_raised { @conn.get(path, "Accept" => "application/xhtml+xml") } end