Skip to content

Commit

Permalink
internal refactors of Model::Find so you can use :query.
Browse files Browse the repository at this point in the history
  • Loading branch information
apotonick committed Jun 20, 2024
1 parent 484aa7c commit 4c7ad16
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 19 deletions.
86 changes: 68 additions & 18 deletions lib/trailblazer/macro/model/find.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,85 @@ module Macro
class Model
# New API for retrieving models by ID.
# Only handles keyword argument style.
def self.Find(model_class, positional_method = nil, params_key: nil, id: "model.find", not_found_terminus: false, **keyword_options, &block)
raise "unknown options #{keyword_options}" if keyword_options.size > 1

task =
if positional_method
finder_activity_for(
params_key: params_key || :id,
finder: Find::Positional.new(model_class: model_class, find_method: positional_method),
&block
)
else
find_method_name, column_key = keyword_options.to_a[0]
#
#
# DESIGN NOTES
# * params[:id] extraction and the actual query are two separate components in the final finder activity.
def self.Find(model_class, positional_method = nil, find_method: nil, id: "model.find", not_found_terminus: false, query: nil, **keyword_options, &block)
# 1. optional: translate kws/positionals into local kws
# 2. build :query
# 3. build find activity

params_key ||= column_key
# raise "unknown options #{keyword_options}" if keyword_options.size > 1

finder_activity_for(
params_key: params_key,
finder: Find::KeywordArguments.new(model_class: model_class, find_method: find_method_name, column_key: column_key),
&block
)
params_key, block, finder_step_options =
if positional_method
bla_explicit_positional(model_class, positional_method, **keyword_options, &block) # FIXME: test block
elsif find_method.nil? && query.nil? # translate_from_shorthand
bla_shorthand(model_class, **keyword_options, &block)
else # options passed explicitly, kws. this still means we need to translate find_method to query, or use user's query.
# TODO: sort out query: default it or take user's

if query.nil?
blubb_bla_keywords(model_class, find_method: find_method, **keyword_options, &block)# FIXME: test block
else
# raise "IMPLEMENT ME"
blubb_bla_query(model_class, query, **keyword_options, &block)
end
end

task = finder_activity_for(
params_key: params_key,
finder: finder_step_options,
&block
)

options = options_for(task, id: id)

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

options
end

def self.bla_shorthand(model_class, **keyword_options, &block)
# translate shorthand form.
find_method_name, column_key = keyword_options.to_a[0]

params_key = keyword_options.key?(:params_key) ? keyword_options[:params_key] : column_key # TODO: use method for this.

[
params_key,
block,
Find::KeywordArguments.new(model_class: model_class, find_method: find_method_name, column_key: column_key),
]
end

def self.bla_explicit_positional(model_class, positional_method, column_key: :id, params_key: column_key, **, &block)
[
params_key,
block,
Find::Positional.new(model_class: model_class, find_method: positional_method), # query
]
end

def self.blubb_bla_keywords(model_class, find_method:, column_key: :id, params_key: column_key, **keyword_options, &block) # FIXME: defaulting is redundant with bla_explicit_positional.
finder = Find::KeywordArguments.new(model_class: model_class, find_method: find_method, column_key: column_key, **keyword_options)

[params_key, block, finder]
end

def self.blubb_bla_query(model_class, query, column_key: :id, params_key: column_key, **, &block) # FIXME: defaulting is redundant with bla_explicit_positional.
query_on_model_class = ->(ctx, **kws) { model_class.instance_exec(ctx, **kws, &query) } # FIXME: we can only use procs here. what about methods, classes etc?

finder = Macro.task_adapter_for_decider(query_on_model_class, variable_name: :model) # FIXME: {:model} is hard-coded.

[
params_key,
block,
{task: finder} # circuit interface for the Task::Adapter.
]
end

def self.options_for(task, id:)
options = Activity::Railway.Subprocess(task).merge(id: id)

Expand Down
128 changes: 127 additions & 1 deletion test/docs/model/find_test.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
require "test_helper"

# step Model::Find(Song, find_method: :find_by, column_key: :id, params_key: :id)
# step Model::Find(Song, query: ->(ctx, params:, **) { where(id: params[:id_list]) })
# # syntax sugaring
# step Model::Find(Song, find_by: :id)
# step Model::Find(Song, :find)
# step Model::Find(query: ->(ctx, params:, **) { Song.where(id: params[:id_list]) })

class DocsModelFindTest < Minitest::Spec
Song = Struct.new(:id) do
def self.find_by(id:)
Expand All @@ -9,6 +16,124 @@ def self.find_by(id:)
end
end

# Explicit options
# find_method: :id
class Unit_ExplicitOptionsTest < Minitest::Spec
Song = Class.new(DocsModelFindTest::Song) do
def self.find_method(id:)
return if id.nil?
new(id)
end
end

#:find_method
module Song::Activity
class Update < Trailblazer::Activity::Railway
# explicit style:
step Model::Find(Song, find_method: :find_method) # if it's _really_ Song.find_method(...)
# step Model::Find(Song, find_method: :find_by)

step :validate
step :save
#~meths
include T.def_steps(:validate, :save)
#~meths end
end
end
#:find_method end

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

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

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

# TODO: put this test somewhere else
it "doesn't leak anything but {:model} to the outer world" do
signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {id: "1"}, seq: [])

assert_equal ctx.keys.inspect, %([:params, :seq, :model])
end
end

# NOTE: unit test
class FindByExplicitColumnKeyTest < Minitest::Spec
Song = Class.new(DocsModelFindTest::Song) do
def self.find_by(slug:)
return if slug.nil?
new(slug)
end
end

module Song::Activity
class Update < Trailblazer::Activity::Railway
# explicit style:
step Model::Find(Song, find_method: :find_by, column_key: :slug, params_key: :params_slug)
step :validate
step :save
#~meths
include T.def_steps(:validate, :save)
#~meths end
end
end

it do
signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {params_slug: "1"}, seq: [])
ctx[:model] #=> #<struct Song id=1>

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

it "fails" do
signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {params_slug: nil}, seq: [])
assert_equal ctx[:model].inspect, %{nil}
end
end

class FindByQueryTest < Minitest::Spec
Song = Class.new(DocsModelFindTest::Song) do
def self.where(id:, user:)
return if id.nil?
new([id, user])
end
end

module Song::Activity
class Update < Trailblazer::Activity::Railway
step Model::Find(Song, query: ->(ctx, id:, current_user:, **) { where(id: id, user: current_user) })
step :validate
step :save
#~meths
include T.def_steps(:validate, :save)
#~meths end
end
end

it do
current_user = Module

signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {id: "1"}, current_user: current_user, seq: [])
ctx[:model] #=> #<struct Song id=1>

assert_equal ctx[:model].inspect, %(#<struct FindByQueryTest::Song id=[\"1\", Module]>)
end

it "fails" do
signal, (ctx, _) = Trailblazer::Activity.(Song::Activity::Update, params: {id: nil}, seq: [])
assert_equal ctx[:model].inspect, %{nil}
end
end

# find_by: :id
class DocsModelFindByColumnTest < Minitest::Spec
Song = Class.new(DocsModelFindTest::Song)
Expand Down Expand Up @@ -62,6 +187,7 @@ def self.find_by(short_id:)
#:find_by_column
module Song::Activity
class Update < Trailblazer::Activity::Railway
puts "yoo"
step Model::Find(Song, find_by: :short_id) # Song.find_by(short_id: params[:short_id])
step :validate
step :save
Expand Down Expand Up @@ -89,7 +215,7 @@ class Update < Trailblazer::Activity::Railway
#~ctx_to_result end
end

class DocsModelFindByColumnAndDifferentParamsKeyTest < Minitest::Spec
class DocsModelFindByDifferentParamsKeyTest < Minitest::Spec
Song = Class.new(DocsModelFindTest::Song)

#:params_key
Expand Down

0 comments on commit 4c7ad16

Please sign in to comment.