diff --git a/lib/her/model/associations/association.rb b/lib/her/model/associations/association.rb index 3548dc80..3a78e577 100644 --- a/lib/her/model/associations/association.rb +++ b/lib/her/model/associations/association.rb @@ -49,8 +49,21 @@ def fetch(opts = {}) return @opts[:default].try(:dup) if @parent.new? path = build_association_path -> { "#{@parent.request_path(@params)}#{@opts[:path]}" } - @klass.get(path, @params).tap do |result| + + # XXX: Depending on the case, we route through fetching a collection + # or a single resource (has_one/belongs_to) + request_association(path, @params).tap do |result| @cached_result = result unless @params.any? + return result + end + end + + # @private + def request_association(path, params) + if @opts[:default].is_a? Array + @klass.get_collection(path, @params) + else + @klass.get(path, @params) end end diff --git a/lib/her/model/attributes.rb b/lib/her/model/attributes.rb index 38759312..766b3615 100644 --- a/lib/her/model/attributes.rb +++ b/lib/her/model/attributes.rb @@ -179,9 +179,14 @@ def instantiate_record(klass, parsed_data) # # @private def instantiate_collection(klass, parsed_data = {}) - records = klass.extract_array(parsed_data).map do |record| + raw_data = klass.extract_array(parsed_data) + + # XXX: This method should always return a collection + raw_data = [] if raw_data.blank? + records = raw_data.map do |record| instantiate_record(klass, data: record) end + Her::Collection.new(records, parsed_data[:metadata], parsed_data[:errors]) end diff --git a/spec/model/associations_spec.rb b/spec/model/associations_spec.rb index 9cc56f39..d34ea875 100644 --- a/spec/model/associations_spec.rb +++ b/spec/model/associations_spec.rb @@ -279,6 +279,7 @@ before do spawn_model "Foo::User" do has_many :comments, class_name: "Foo::Comment" + has_many :feeds, class_name: "Foo::Feed" has_one :role, class_name: "Foo::Role" belongs_to :organization, class_name: "Foo::Organization" has_many :posts, inverse_of: :admin @@ -289,6 +290,10 @@ parse_root_in_json true end + spawn_model "Foo::Feed" do + belongs_to :user + end + spawn_model "Foo::Post" do belongs_to :admin, class_name: "Foo::User" end @@ -308,6 +313,7 @@ builder.adapter :test do |stub| stub.get("/users/1") { [200, {}, { id: 1, name: "Tobias Fünke", comments: [{ comment: { id: 2, body: "Tobias, you blow hard!", user_id: 1 } }, { comment: { id: 3, body: "I wouldn't mind kissing that man between the cheeks, so to speak", user_id: 1 } }], role: { id: 1, body: "Admin" }, organization: { id: 1, name: "Bluth Company" }, organization_id: 1 }.to_json] } stub.get("/users/1/comments") { [200, {}, [{ comment: { id: 4, body: "They're having a FIRESALE?" } }].to_json] } + stub.get("/users/1/feeds") { [204, {}, ''.to_json] } stub.get("/users/1/role") { [200, {}, { id: 3, body: "User" }.to_json] } stub.get("/users/1/posts") { [200, {}, [{ id: 1, body: "blogging stuff", admin_id: 1 }].to_json] } stub.get("/organizations/1") { [200, {}, { organization: { id: 1, name: "Bluth Company Foo" } }.to_json] } @@ -325,6 +331,12 @@ expect(user.comments.first.body).to eq("Tobias, you blow hard!") end + it "when a has_many doesn't have anything" do + expect(user.feeds).to eq([]) + expect(user.feeds.first).to eq(nil) + expect(user.feeds.length).to eq(0) + end + it "does not refetch the parents models data if they have been fetched before" do expect(user.comments.first.user.object_id).to eq(user.object_id) end