Class: Trailblazer::Macro::Model

Inherits:
Object
  • Object
show all
Defined in:
lib/trailblazer/macro/model.rb,
lib/trailblazer/macro/model/find.rb

Defined Under Namespace

Modules: Find Classes: Builder

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.Build(model_class, method = :new, id: "model.build") ⇒ Object

Find



166
167
168
169
170
171
172
# File 'lib/trailblazer/macro/model/find.rb', line 166

def self.Build(model_class, method = :new, id: "model.build")
  activity = Class.new(Activity::Railway) do
    step Find::NoArgument.new(model_class: model_class, find_method: method)
  end

  options_for(activity, id: id)
end

.Find(model_class, positional_method = nil, find_method: nil, id: "model.find", not_found_terminus: false, query: nil, **keyword_options, &block) ⇒ Object

New API for retrieving models by ID. Only handles keyword argument style.

DESIGN NOTES

* params[:id] extraction and the actual query are two separate components in the final finder activity.


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/trailblazer/macro/model/find.rb', line 10

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

  # raise "unknown options #{keyword_options}" if keyword_options.size > 1

  params_key, block, finder_step_options =
    if positional_method
      for_explicit_positional(model_class, positional_method, **keyword_options, &block)
    elsif find_method.nil? && query.nil? # translate_from_shorthand
      for_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?
        for_keywords(model_class, find_method: find_method, **keyword_options, &block)
      else
        # raise "IMPLEMENT ME"
        for_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

.finder_activity_for(params_key:, finder:, &block) ⇒ Object

Finder activity consists of two steps: extract_id, and the finder code.

|-- model.build
|   |-- Start.default
|   |-- extract_id
|   |-- finder.Trailblazer::Macro::Model::Find::Positional
|   `-- End.success
|-- validate


118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/trailblazer/macro/model/find.rb', line 118

def self.finder_activity_for(params_key:, finder:, **, &block)
  id_from =
    if block
      block
    else
      ->(ctx, params: {}, **) { params[params_key] } # default id_from
    end

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

  Class.new(Activity::Railway) do
    step task: extract_id, id: :extract_id
    step finder,           id: "finder.#{finder.class}" # FIXME: discuss ID.
  end
end

.for_explicit_positional(model_class, positional_method, **options, &block) ⇒ Object



64
65
66
67
68
69
70
71
72
# File 'lib/trailblazer/macro/model/find.rb', line 64

def self.for_explicit_positional(model_class, positional_method, **options, &block)
  params_key, _ = normalize_keys(**options)

  [
    params_key,
    block,
    Find::Positional.new(model_class: model_class, find_method: positional_method), # query
  ]
end

.for_keywords(model_class, find_method:, **options, &block) ⇒ Object

FIXME: defaulting is redundant with bla_explicit_positional.



74
75
76
77
78
79
80
# File 'lib/trailblazer/macro/model/find.rb', line 74

def self.for_keywords(model_class, find_method:, **options, &block) # FIXME: defaulting is redundant with bla_explicit_positional.
  params_key, column_key = normalize_keys(**options)

  finder = Find::KeywordArguments.new(model_class: model_class, find_method: find_method, column_key: column_key)

  [params_key, block, finder]
end

.for_query(model_class, query, column_key: :id, params_key: column_key, &block) ⇒ Object

FIXME: defaulting is redundant with bla_explicit_positional.



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/trailblazer/macro/model/find.rb', line 82

def self.for_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

.for_shorthand(model_class, **options, &block) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/trailblazer/macro/model/find.rb', line 51

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

  params_key = options.key?(:params_key) ? 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

.normalize_keys(column_key: :id, params_key: column_key) ⇒ Object

Defaulting happening.



47
48
49
# File 'lib/trailblazer/macro/model/find.rb', line 47

def self.normalize_keys(column_key: :id, params_key: column_key, **)
  return params_key, column_key
end

.options_for(task, id:) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/trailblazer/macro/model/find.rb', line 94

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

  inject = {
    Activity::Railway.Inject() => [:params], # pass-through {:params} if it's in ctx.
  }

  out = { # TODO: use Outject once it is implemented.
    Activity::Railway.Out() => ->(ctx, **) { ctx.key?(:model) ? {model: ctx[:model]} : {} }
  }

  options = options.merge(inject)
  options = options.merge(out)
end

Instance Method Details

#call(ctx, params: {}) ⇒ Object



26
27
28
29
30
31
# File 'lib/trailblazer/macro/model.rb', line 26

def call(ctx, params: {}, **)
  builder = Builder.new
  model   = builder.call(ctx, params) or return

  ctx[:model] = model
end