Skip to content

Commit

Permalink
introduce new API for Model().
Browse files Browse the repository at this point in the history
```ruby
step Model(Song, find_by: :short_id, params_key: :slug)
```

This allows to use arbitrary finders and using a key in `params` different to
the column name.
  • Loading branch information
apotonick committed Jun 27, 2023
1 parent e76d090 commit 75e221f
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 3 deletions.
37 changes: 34 additions & 3 deletions lib/trailblazer/macro/model.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
module Trailblazer
module Macro

def self.Model(model_class = nil, action = :new, find_by_key = :id, id: 'model.build', not_found_terminus: false)
task = Activity::Circuit::TaskAdapter.for_step(Model.new)
# TODO: deprecate find_by_key in favor of `find_by: :id`
def self.Model(model_class = nil, action = :new, find_by_key = :id, id: "model.build", not_found_terminus: false, params_key: find_by_key, **options)
# {find_by: :slug}
if options.any?
raise "unknown options #{options}" if options.size > 1

id_from = ->(ctx, params:, **) { params[params_key] } # TODO: We can hand in other behavior here, Yogi!

extract_id = Macro.task_adapter_for_decider(id_from, variable_name: :id)

action, find_by_key = options.to_a[0]

task = Activity::Railway() do
step task: extract_id, id: :extract_id
step Model.method(:produce)
end

options = Activity::Railway.Subprocess(task)
else # old style, deprecate
task = Activity::Circuit::TaskAdapter.for_step(Model.new)

options = {task: task, id: id}
end

injections = {
Activity::Railway.Inject() => [:params], # pass-through {:params} if it's in ctx.
Expand All @@ -12,10 +33,12 @@ def self.Model(model_class = nil, action = :new, find_by_key = :id, id: 'model.b
:"model.class" => ->(*) { model_class },
:"model.action" => ->(*) { action },
:"model.find_by_key" => ->(*) { find_by_key },
:"model.id_from" => ->(*) { id_from }, # TODO: test me.
}
}

options = {task: task, id: id}.merge(injections)
options = options.merge(injections)


options = options.merge(Activity::Railway.Output(:failure) => Activity::Railway.End(:not_found)) if not_found_terminus

Expand All @@ -30,11 +53,19 @@ def call(ctx, params: {}, **)
ctx[:model] = model
end

def self.produce(ctx, id:, **)
model_class = ctx[:"model.class"]
find_by_key = ctx[:"model.find_by_key"]

ctx[:model] = model_class.find_by(find_by_key.to_sym => id)
end

class Builder
def call(ctx, params)
action = ctx[:"model.action"]
model_class = ctx[:"model.class"]
find_by_key = ctx[:"model.find_by_key"]
id_from = ctx[:"model.id_from"]
action = :pass_through unless %i[new find_by].include?(action)

send("#{action}!", model_class, params, ctx[:"model.action"], find_by_key)
Expand Down
38 changes: 38 additions & 0 deletions test/docs/model_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,44 @@ class Update < Trailblazer::Activity::Railway
#~ctx_to_result end
end

class DocsModelFindBy___Test < Minitest::Spec
Song = Struct.new(:id, :short_id) do
def self.find_by(short_id:)
return if short_id.nil?
new(1, short_id)
end
end

#:find_by_column
module Song::Activity
class Update < Trailblazer::Activity::Railway
step Model(Song, find_by: :short_id, params_key: :slug)
step :validate
step :save
#~meths
include T.def_steps(:validate, :save)
#~meths end
end
end
#:find_by_column end

#~ctx_to_result
it do
#:find_by_column-invoke
signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {slug: "1f396"}, seq: [])
ctx[:model] #=> #<struct Song id=2, slug="1f396">
#:find_by_column-invoke end

assert_equal ctx[:model].inspect, %{#<struct #{Song} id=1, short_id="1f396">}
end

it do
signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {slug: nil}, seq: [])
assert_equal ctx[:model].inspect, %{nil}
end
#~ctx_to_result end
end

class DocsModelAccessorTest < Minitest::Spec
Song = Class.new(DocsModelTest::Song)

Expand Down

0 comments on commit 75e221f

Please sign in to comment.