Two problems with one-to-many associations in Hanami 1.1.0.beta1
erubin opened this issue · comments
I have a has_many/belongs_to association:
class EventRepository < Hanami::Repository
associations do
has_many :actions
...
end
class ActionRepository < Hanami::Repository
associations do
...
belongs_to :event
end
When I call this EventRepository
method:
def add_action(event, data)
assoc(:actions, event).add(data)
end
I get this error:
KeyError: key not found: :id # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/associations/has_many.rb:209:in `fetch' # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/associations/has_many.rb:209:in `_build_scope' # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/associations/has_many.rb:55:in `initialize' # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/association.rb:20:in `new' # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/association.rb:20:in `build' # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/repository.rb:472:in `assoc' # ./lib/backend/repositories/event_repository.rb:21:in `add_action' ...
I look into has_many.rb and found that the id
was present in subject
, but it gets removed when to_hash
is called in initialize
. I got around this by adding this to_hash
method to my entity:
class Event < Hanami::Entity
attr_reader :id
...
def to_hash
if id
super.merge(id: id)
else
super
end
end
When I call this EventRepository
method:
def create_with_actions(data)
assoc(:actions).create(data)
end
I get this this error:
Hanami::Model::Error: ... # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/associations/has_many.rb:73:in `rescue in create' # /home/eric/.gem/ruby/2.3.4/gems/hanami-model-1.1.0.beta1/lib/hanami/model/associations/has_many.rb:63:in `create' # ./lib/backend/repositories/event_repository.rb:16:in `create_with_actions' ... # ------------------ # --- Caused by: --- # NoMethodError: undefined method `to_hash' for #<ROM::Repository::CommandProxy:0x005625d3e176d0> Did you mean? to_s # /home/eric/.gem/ruby/2.3.4/gems/hanami-utils-1.1.0.beta1/lib/hanami/utils/hash.rb:145:in `initialize'
I can't figure out what's going on in this one...
@erubin Hi, I'm sorry about these problems. I tried to reproduce them, but I couldn't. Please check this demo project I created for you: https://github.com/jodosha/hanami-model-447
The only issue that I get is the returning value of #add_action
is ROM::Struct::Action
, instead of Action
entity. /cc @mereghost
Anything else works fine. Would you like to check the unit tests from the demo project and compare with your usage? Thanks.
I'm closing this as I manually verified that this isn't an issue. @erubin If I'm missing something, please reopen this ticket. Thanks!
If you add attributes to the Entity
class Event < Hanami::Entity
attributes do
attribute :title, Types::String
attribute :actions, Types::Array
end
end
When you call add_action
you get this error:
Failure/Error: assoc(:actions, event).add(data)
KeyError:
key not found: :id
# /var/lib/gems/2.3.0/gems/hanami-model-1.1.0.beta3/lib/hanami/model/associations/has_many.rb:189:in `fetch'
# /var/lib/gems/2.3.0/gems/hanami-model-1.1.0.beta3/lib/hanami/model/associations/has_many.rb:189:in `_build_scope'
# /var/lib/gems/2.3.0/gems/hanami-model-1.1.0.beta3/lib/hanami/model/associations/has_many.rb:45:in `initialize'
# /var/lib/gems/2.3.0/gems/hanami-model-1.1.0.beta3/lib/hanami/model/association.rb:20:in `new'
# /var/lib/gems/2.3.0/gems/hanami-model-1.1.0.beta3/lib/hanami/model/association.rb:20:in `build'
# /var/lib/gems/2.3.0/gems/hanami-model-1.1.0.beta3/lib/hanami/repository.rb:471:in `assoc'
# ./lib/backend/repositories/event_repository.rb:13:in `add_action'
# ./spec/backend/repositories/event_repository_spec.rb:27:in `block (3 levels) in <top (required)>'
@erubin Sorry for the late feedback, but I only had now the time to look at it again.
We require an entity to have an :id
in its attributes. It's a fundamental prerequisite for it to work.
Having an identity (ID) is the main difference between an entity and a value object, where the identity is determined by its values.
I'm sorry, but we can't support the absence of :id
for entities.
If you need to work with objects without an identity, my suggestion is to leave the ID to Event
and then to use a value object.
That means:
# lib/backend/entities/event.rb
class Event < Hanami::Entity
end
# lib/backend/value_objects/event_value.rb
require "dry-struct"
# Sorry for the silly name EventValue :D
class EventValue < Dry::Struct
attribute :name, Hanami::Model::Types::String
attribute :actions, Hanami::Model::Types::Array
end
# lib/backend/repositories/event_repository.rb
class EventRepository < Hanami::Repository
associations do
has_many :actions
end
def create_with_actions(data)
assoc(:actions).create(data)
end
def add_action(event, data)
assoc(:actions, event).add(data)
end
def find_event_value(id)
aggregate(:actions)
.where(id: id)
.limit(1)
.map_to(EventValue)
.one
end
end
irb(main):001:0> EventRepository.new.find_event_value(1)
[backend] [INFO] [2017-10-19 15:57:00 +0200] (0.000401s) SELECT "id", "name", "created_at", "updated_at" FROM "events" WHERE ("id" = 1) ORDER BY "events"."id" LIMIT 1
[backend] [INFO] [2017-10-19 15:57:00 +0200] (0.000412s) SELECT "actions"."id", "actions"."event_id", "actions"."name", "actions"."created_at", "actions"."updated_at" FROM "actions" INNER JOIN "events" ON ("events"."id" = "actions"."event_id") WHERE ("actions"."event_id" IN (1)) ORDER BY "actions"."id"
=> #<EventValue name="signup" actions=[{:id=>1, :event_id=>1, :name=>"visit", :created_at=>2017-10-05 15:25:26 UTC, :updated_at=>2017-10-05 15:25:26 UTC}]>