Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entity to_h - nested attribute entity is not converted to hash #483

Open
iJackUA opened this issue Apr 19, 2018 · 4 comments
Open

Entity to_h - nested attribute entity is not converted to hash #483

iJackUA opened this issue Apr 19, 2018 · 4 comments
Labels

Comments

@iJackUA
Copy link

iJackUA commented Apr 19, 2018

Is it by design? Because I was expecting it to work a bit different.

Suppose I have two Entities

class User < Hanami::Entity
  attributes do
    attribute :id, Types::Int
    attribute :auth_data, Types::Entity(::UserAuthData)
  end
end
class UserAuthData < Hanami::Entity
  attributes do
    attribute :hi, Types::String
  end
end

I can initialize it that way

user_params = {id: 3, auth_data: {hi: "hello"}}
user = User.new(user_params)

now when I do user.to_h

{:id=>3,
 :auth_data=>#<UserAuthData:0x000055d8e7fcf600 @attributes={:hi=>"hello"}>}

User entity is serialized to hash, but enclosed UserAuthData is still an object, we did not return to the same user_params

The only way I see to do that is
Hanami::Utils::Hash.new(user).to_hash
or
Hanami::Utils::Hash.deep_serialize(user)
these methods return {:id=>3, :auth_data=>{:hi=>"hello"}} as expected

Maybe Entity object by itself need to have a helper to serialize itself to real nested hash?
Actually I would expect from entity_object.to_h to do this by default, but maybe I miss some consideration.

@cllns
Copy link
Member

cllns commented Apr 19, 2018

Duplicate of #470.

You noticed you can use Hanami::Utils::Hash.deep_serialize, which is the recommendation from that issue.

But we didn't really answer the question why this doesn't happen by default though. I agree it's surprising that it doesn't work that way. Any input @mereghost? I wonder if a workaround could be adding an optional param, like#to_h(deep: false) might make sense?

@mereghost
Copy link
Member

I vaguely remember a past issue of this up ahead in the framework. Also that's how ruby's #to_h works. It only operates on the instance leaving nested objects intact.

We could provide a way to make sure that the object is deep serialized into a hash for convenience, but that's already provided by hanami-utils which we depend on.

@iJackUA
Copy link
Author

iJackUA commented Apr 20, 2018

Thanks @cllns it seems I searched not very good before creating an issue.
There are really valid points (of why current behaviour is "as it is now"):

  • that is how ruby's #to_h works
  • for JSON API we need to use better serializers (what I actually do) than to_h

My potential use case to have recursive to_h serialization was in adopting Entity in Trailblazer's Operation. Input of the typical Operation is Hash of params from Action. Later on, an Entity is retrieved based on params and data from Entity is being manipulated during next steps of the Operation. Idea was to use Entity as DTO through these steps, so each step can modify Entity attribute (and nested also).

But at the last step of the Operation I want to persist this Entity to DB via Repository.
And Repository's update method takes Hash (of attributes) as a data param.

So I was searching a way to get back a Hash representation of "complex" Entity.
And yes Hanami::Utils::Hash.deep_serialize(user) can be used, but it took me 30 minutes to find it out.
Maybe a more straightforward way to this can be introduced?

P/S Actually my another and wider question first time I tried Hanami was "Oh great, I can get Entity as a "select" query result from Repo, but wait... why I can not put ready made Entity object as a data
in "insert/update" command in Repo?" :)

@mereghost
Copy link
Member

@iJackUA I'm sorry for the veeery late response.

We really need to up our game on the documentation side of things (contributions are always welcome ;)).

About the insert/update part: if you use the Repository#create method you can safely pass an entity. For the Repository#update method we always take the arguments provided as canon (as we don't do dirty tracking), so if you pass in an Entity instance as the second parameter (the first being the id) it will set whatever attributes are set there(even stuff like created_at etc).

Think of it as a poor man's changeset (which is a concept we might introduce in the future). You pass in what you want to update and we do it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants