diff --git a/lib/her/model/parse.rb b/lib/her/model/parse.rb index 51b4a31a..a03dd040 100644 --- a/lib/her/model/parse.rb +++ b/lib/her/model/parse.rb @@ -13,6 +13,11 @@ def to_params self.class.to_params(attributes, changes) end + # Convert into a hash of request parameters, based on `include_root_in_embedded_json`. + def to_embedded_params + self.class.to_params(attributes, changes, true) + end + module ClassMethods # Parse data before assigning it to a resource, based on `parse_root_in_json`. # @@ -31,7 +36,7 @@ def parse(data) end # @private - def to_params(attributes, changes = {}) + def to_params(attributes, changes = {}, embedded = false) filtered_attributes = attributes.each_with_object({}) do |(key, value), memo| case value when Her::Model @@ -48,11 +53,14 @@ def to_params(attributes, changes = {}) filtered_attributes.slice! *changes.keys.map(&:to_sym) end - if include_root_in_json? + include_element = embedded ? include_root_in_embedded_json? : include_root_in_json? + element = embedded ? included_embedded_root_element : included_root_element + + if include_element if json_api_format? - { included_root_element => [filtered_attributes] } + { element => [filtered_attributes] } else - { included_root_element => filtered_attributes } + { element => filtered_attributes } end else filtered_attributes @@ -61,17 +69,25 @@ def to_params(attributes, changes = {}) # @private def embeded_params(attributes) - associations.values.flatten.each_with_object({}) do |definition, hash| - value = case association = attributes[definition[:name]] - when Her::Collection, Array - association.map { |a| a.to_params }.reject(&:empty?) - when Her::Model - association.to_params - end - hash[definition[:data_key]] = value if value.present? + associations.keys.each_with_object({}) do |key, hash| + associations[key].flatten.each do |definition| + next if attributes[definition[:data_key]].present? && key.to_sym == :belongs_to + embeded_association(attributes, definition, hash) + end end end + # @private + def embeded_association(attributes, definition, hash) + value = case association = attributes[definition[:name]] + when Her::Collection, Array + association.map { |a| a.to_embedded_params }.reject(&:empty?) + when Her::Model + association.to_embedded_params + end + hash[definition[:data_key]] = value if value.present? + end + # Return or change the value of `include_root_in_json` # # @example @@ -84,6 +100,17 @@ def include_root_in_json(value, options = {}) @_her_include_root_in_json_format = options[:format] end + # Return or change the value of `include_root_in_embedded_json` + # + # @example + # class User + # include Her::Model + # include_root_in_embedded_json true + # end + def include_root_in_embedded_json(value) + @_her_include_root_in_embedded_json = value + end + # Return or change the value of `parse_root_in_json` # # @example @@ -151,6 +178,11 @@ def included_root_element include_root_in_json? == true ? root_element : include_root_in_json? end + # @private + def included_embedded_root_element + include_root_in_embedded_json? == true ? root_element : include_root_in_embedded_json? + end + # Extract an array from the request data # # @example @@ -212,6 +244,12 @@ def include_root_in_json? superclass.respond_to?(:include_root_in_json?) && superclass.include_root_in_json? end + # @private + def include_root_in_embedded_json? + return @_her_include_root_in_embedded_json unless @_her_include_root_in_embedded_json.nil? + superclass.respond_to?(:include_root_in_embedded_json?) && superclass.include_root_in_embedded_json? + end + # @private def parse_root_in_json? return @_her_parse_root_in_json unless @_her_parse_root_in_json.nil? diff --git a/spec/model/associations_spec.rb b/spec/model/associations_spec.rb index e44eb712..4b118abb 100644 --- a/spec/model/associations_spec.rb +++ b/spec/model/associations_spec.rb @@ -369,7 +369,7 @@ end it "includes belongs_to relationship in params by default" do - expect(user_params[:organization]).to be_kind_of(Hash) + expect(user_params[:organization]).to be_a(Foo::Organization) expect(user_params[:organization]).not_to be_empty end end @@ -687,7 +687,7 @@ end it "includes belongs_to relationship in params by default" do - expect(user_params[:organization]).to be_kind_of(Hash) + expect(user_params[:organization]).to be_a(Foo::Organization) expect(user_params[:organization]).not_to be_empty end end diff --git a/spec/model/parse_spec.rb b/spec/model/parse_spec.rb index 25b617a8..79ce2013 100644 --- a/spec/model/parse_spec.rb +++ b/spec/model/parse_spec.rb @@ -103,6 +103,101 @@ class User < Foo::Model; end end end + context "when include_root_in_embedded_json is set" do + before do + Her::API.setup url: "https://api.example.com" do |builder| + builder.use Her::Middleware::FirstLevelParseJSON + builder.use Faraday::Request::UrlEncoded + end + + Her::API.default_api.connection.adapter :test do |stub| + stub.post("/users") { |env| [200, {}, { user: { id: 1, fullname: params(env)[:user][:fullname] } }.to_json] } + stub.post("/users/admins") { |env| [200, {}, { user: { id: 1, fullname: params(env)[:user][:fullname] } }.to_json] } + end + end + + context "to true" do + before do + spawn_model "Foo::User" do + include_root_in_embedded_json true + parse_root_in_json true + custom_post :admins + end + end + + it "inherits attributes from parent class" do + spawn_model "Foo::ChildUser", super_class: Foo::User do + end + + expect(Foo::ChildUser).to be_include_root_in_embedded_json + end + + it "allows `include_root_in_embedded_json` to be set to `false` on a child model" do + spawn_model "Foo::ChildUser", super_class: Foo::User do + include_root_in_embedded_json false + end + + expect(Foo::ChildUser).to_not be_include_root_in_embedded_json + end + + it "wraps params in the element name in `to_embedded_params`" do + @new_user = Foo::User.new(fullname: "Tobias Fünke") + expect(@new_user.to_embedded_params).to eq(user: { fullname: "Tobias Fünke" }) + end + end + + context "to false" do + before do + spawn_model "Foo::User" do + include_root_in_embedded_json false + end + end + + it "inherits attributes from parent class" do + spawn_model "Foo::ChildUser", super_class: Foo::User do + end + + expect(Foo::ChildUser).to_not be_include_root_in_embedded_json + end + + it "allows `include_root_in_embedded_json` to be set to `true` on a child model" do + spawn_model "Foo::ChildUser", super_class: Foo::User do + include_root_in_embedded_json true + end + + expect(Foo::ChildUser).to be_include_root_in_embedded_json + end + end + + context "to a symbol" do + before do + spawn_model "Foo::User" do + include_root_in_embedded_json :person + parse_root_in_json :person + end + end + + it "wraps params in the specified value" do + @new_user = Foo::User.new(fullname: "Tobias Fünke") + expect(@new_user.to_embedded_params).to eq(person: { fullname: "Tobias Fünke" }) + end + end + + context "in the parent class" do + before do + spawn_model("Foo::Model") { include_root_in_embedded_json true } + + class User < Foo::Model; end + @spawned_models << :User + end + + it "wraps params with the class name" do + @new_user = User.new(fullname: "Tobias Fünke") + expect(@new_user.to_embedded_params).to eq(user: { fullname: "Tobias Fünke" }) + end + end + end + context "when `request_new_object_on_build` is set" do context "to true" do before do