Skip to content

Commit

Permalink
Handle aliased assocs and struct builder properly
Browse files Browse the repository at this point in the history
  • Loading branch information
solnic committed Dec 2, 2023
1 parent f35f110 commit 8756826
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 34 deletions.
6 changes: 4 additions & 2 deletions lib/rom/factory/attributes/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ class ManyToOne < Core
# @api private
# rubocop:disable Metrics/AbcSize
def call(attrs, persist: true)
return if attrs.key?(name) && attrs[name].nil?

assoc_data = attrs.fetch(name, EMPTY_HASH)

if assoc_data && assoc_data[assoc.source.primary_key] && !attrs[foreign_key]
if assoc_data.is_a?(Hash) && assoc_data[assoc.target.primary_key] && !attrs[foreign_key]
assoc.associate(attrs, attrs[name])
elsif assoc_data.is_a?(ROM::Struct)
assoc.associate(attrs, assoc_data)
elsif !(attrs.key?(name) && attrs[name].nil?) && !attrs[foreign_key]
elsif !attrs[foreign_key]
parent = if persist
builder.persistable.create(*parent_traits, **assoc_data)
else
Expand Down
24 changes: 14 additions & 10 deletions lib/rom/factory/tuple_evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ class TupleEvaluator
# @api private
attr_reader :traits

# @api private
attr_reader :model

# @api private
attr_reader :sequence

Expand All @@ -26,10 +23,13 @@ def initialize(attributes, relation, traits = {})
@attributes = attributes
@relation = relation.with(auto_struct: true)
@traits = traits
@model = @relation.combine(*assoc_names).mapper.model
@sequence = Sequences[relation]
end

def model(traits)
@relation.combine(*assoc_names(traits)).mapper.model
end

# @api private
def defaults(traits, attrs, **opts)
mergeable_attrs = select_mergeable_attrs(traits, attrs)
Expand All @@ -51,7 +51,8 @@ def struct(*traits, **attrs)

attributes.merge!(materialized_callables)

associations = assoc_names
associations = assoc_names(traits)
.reject { |name| attrs.key?(name) && attrs[name].nil? }
.map { |key|
if (assoc = @attributes[key]) && assoc.count.positive?
[key, build_assoc_attrs(key, attributes[relation.primary_key], attributes[key])]
Expand All @@ -63,7 +64,7 @@ def struct(*traits, **attrs)
attributes = relation.output_schema[attributes]
attributes.update(associations)

model.new(attributes)
model(traits).new(attributes)
end

def build_assoc_attrs(key, fk, value)
Expand Down Expand Up @@ -137,19 +138,22 @@ def evaluate_traits(trait_list, attrs, opts)

traits_attrs = self.traits.select { |key, _value| traits[key] }.values.flat_map(&:elements)
registry = AttributeRegistry.new(traits_attrs)

self.class.new(registry, relation).defaults([], attrs, **opts)
end

# @api private
def evaluate_associations(traits, attrs, opts)
assocs(traits).associations.each_with_object({}) do |assoc, h|
if assoc.dependency?(relation)
h[assoc.name] = ->(parent, call_opts) do
assocs(traits).associations.each_with_object({}) do |assoc, memo|
if attrs.key?(assoc.name) && attrs[assoc.name].nil?
memo
elsif assoc.dependency?(relation)
memo[assoc.name] = ->(parent, call_opts) do
assoc.call(parent, **opts, **call_opts)
end
else
result = assoc.(attrs, **opts)
h.update(result) if result
memo.update(result) if result
end
end
end
Expand Down
95 changes: 73 additions & 22 deletions spec/integration/rom/factory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,39 +84,90 @@
end

context "many-to-one" do
before do
factories.define(:task) do |f|
f.title { "Foo" }
f.association(:user)
context "with an association that is not aliased" do
before do
factories.define(:task) do |f|
f.title { "Foo" }
f.association(:user)
end

factories.define(:user) do |f|
f.first_name "Jane"
f.last_name "Doe"
f.email "janjiss@gmail.com"
f.timestamps

f.association(:tasks)
end
end

factories.define(:user) do |f|
f.first_name "Jane"
f.last_name "Doe"
f.email "janjiss@gmail.com"
f.timestamps
it "creates a struct with associated parent" do
task = factories.structs[:task, title: "Bar"]

f.association(:tasks)
expect(task.title).to eql("Bar")
expect(task.user.first_name).to eql("Jane")
end
end

it "creates a struct with associated parent" do
task = factories.structs[:task, title: "Bar"]
it "does not build associated struct if it's set to nil explicitly" do
task = factories.structs[:task, user: nil]

expect(task.title).to eql("Bar")
expect(task.user.first_name).to eql("Jane")
end
expect(task.user).to be_nil
end

it "does not persist associated struct if it's set to nil explicitly" do
task = factories[:task, user: nil]

expect(task.user).to be_nil
end

it "does not build associated struct if it's set to nil explicitly" do
task = factories[:task, user: nil]
it "creates the associated record with provided attributes" do
task = factories[:task, user: {first_name: "John"}]

expect(task.user).to be_nil
expect(task.user.first_name).to eql("John")
end
end

it "creates the associated record with provided attributes" do
task = factories[:task, user: {first_name: "John"}]
context "with an aliased association" do
before do
factories.define(:task) do |f|
f.title { "Foo" }
f.association(:author)
end

factories.define(:user) do |f|
f.first_name "Jane"
f.last_name "Doe"
f.email "janjiss@gmail.com"
f.timestamps

f.association(:tasks)
end
end

it "creates a struct with associated parent" do
task = factories.structs[:task, title: "Bar"]

expect(task.title).to eql("Bar")
expect(task.author.first_name).to eql("Jane")
end

it "does not build associated struct if it's set to nil explicitly" do
task = factories.structs[:task, author: nil]

expect(task.author).to be_nil
end

it "does not persist associated struct if it's set to nil explicitly" do
task = factories[:task, author: nil]

expect(task.user.first_name).to eql("John")
expect(task.author).to be_nil
end

it "creates the associated record with provided attributes" do
task = factories[:task, author: {first_name: "John"}]

expect(task.author.first_name).to eql("John")
end
end
end

Expand Down
1 change: 1 addition & 0 deletions spec/shared/relations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
schema(infer: true) do
associations do
belongs_to :user
belongs_to :user, as: :author
end
end
end
Expand Down

0 comments on commit 8756826

Please sign in to comment.