Module: Trailblazer::Macro::Contract

Defined in:
lib/trailblazer/macro/contract.rb,
lib/trailblazer/macro/contract/build.rb,
lib/trailblazer/macro/contract/persist.rb,
lib/trailblazer/macro/contract/validate.rb

Defined Under Namespace

Modules: DSL Classes: Validate

Class Method Summary collapse

Class Method Details

.Build(name: "default", constant: nil, builder: nil) ⇒ Object



6
7
8
9
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
# File 'lib/trailblazer/macro/contract/build.rb', line 6

def self.Build(name: "default", constant: nil, builder: nil)
  contract_path = :"contract.#{name}"

  injections = {
    Activity::Railway.Inject("#{contract_path}.class") => ->(*) { constant }, # default to {constant} if not injected.
  }

  # DISCUSS: can we force-default this via Inject()?
  input = {
    Activity::Railway.In() => ->(ctx, **) do
      ctx.to_hash.merge(
        constant: constant,
        name:     contract_path
      )
    end
  }

  output = {
    Activity::Railway.Out() => [contract_path]
  }

  default_contract_builder = ->(ctx, model: nil, **) { ctx[:"#{contract_path}.class"].new(model) }

  # proc is called via {Option()}.
  task_option_proc = builder || default_contract_builder

  # after the builder proc is run, assign its result to {:"contract.default"}.
  task = Macro.task_adapter_for_decider(task_option_proc, variable_name: contract_path)

  {
    task:   task, id: "contract.build",
  }
    .merge(injections)
    .merge(input)
    .merge(output)
end

.Persist(method: :save, name: "default") ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
# File 'lib/trailblazer/macro/contract/persist.rb', line 4

def self.Persist(method: :save, name: "default")
  path = :"contract.#{name}"
  step = ->(ctx, **) { ctx[path].send(method) }

  task = Activity::Circuit::TaskAdapter.for_step(step)

  {
    task: task,
    id:   "persist.save"
  }
end

.Validate(skip_extract: false, name: "default", representer: false, key: nil, constant: nil, invalid_data_terminus: false) ⇒ Object

result.contract = .. result.contract.errors = .. Deviate to left track if optional key is not found in params. Deviate to left if validation result falsey.



8
9
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
45
46
47
# File 'lib/trailblazer/macro/contract/validate.rb', line 8

def self.Validate(skip_extract: false, name: "default", representer: false, key: nil, constant: nil, invalid_data_terminus: false) # DISCUSS: should we introduce something like Validate::Deserializer?
  contract_path = :"contract.#{name}" # the contract instance
  params_path   = :"contract.#{name}.params" # extract_params! save extracted params here.
  key_path      = :"contract.#{name}.extract_key"

  extract  = Validate::Extract.new(key_path: key_path, params_path: params_path)
  validate = Validate.new(name: name, representer: representer, params_path: params_path, contract_path: contract_path)

  # These are defaulting dependency injection, more here
  # https://trailblazer.to/2.1/docs/activity.html#activity-dependency-injection-inject-defaulting

  # Build a simple Railway {Activity} for the internal flow.
  activity = Activity::Railway(name: "Contract::Validate") do
    unless skip_extract
      step extract,
        id: "#{params_path}_extract",
        Output(:failure) => End(:extract_failure),
        Inject(key_path) => ->(*) { key } # default to {key} if not injected.
    end

    step validate,
      id: "contract.#{name}.call",
      Inject(contract_path) => ->(*) { constant } # default the contract instance to {constant}, if not injected (or passed down from {Build()})
  end

  options = activity.Subprocess(activity)
  options.merge!(id: "contract.#{name}.validate")

  # Deviate End.extract_failure to the standard failure track as a default. This can be changed from the user side.
  unless skip_extract
    options.merge!(activity.Output(:extract_failure) => activity.Track(:failure))
  end

  # Halt failure track to End with {contract.name.invalid}.
  if invalid_data_terminus
    options.merge!(activity.Output(:failure) => activity.End(:invalid_data))
  end

  options
end