trailblazer-macro-contract 2.1.1 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 523435b29f2464a1677f6b430e8285b62841e4b24f4ebfdf19bcab531239ba70
4
- data.tar.gz: 30cda42970110b3d8b280d002b785ada64445cb8212b1a6040432df5218f8fa4
3
+ metadata.gz: 44b6ac6ab9a1efc431188b24d22ce9882a9ca35ea3a726d14267be789bd66973
4
+ data.tar.gz: 77ad5f9fd06aa6b51117b2fa5d9ad037283e4107402b66cbc852a83423387a25
5
5
  SHA512:
6
- metadata.gz: b9922fdf9cfac45598fe3d1d4672e708bacf54e41779b38d35e28dbfbee78de92eafe29bdabcda01369bbcf96265917129d0cdc0d8abcc85b1bf3cb2c3c4bbf3
7
- data.tar.gz: bb8f91f57343237ac0995fa8f2516558ccabf799c9b0bf64c0c6024bae6a61285ba867bb97e1d10d2b99fc8e510ad611a6a523bf439d2df01f9ced40c6eeefa7
6
+ metadata.gz: 50d6644a6a4978bfe8ce4f49b973b36b38309221bcab324203cfd3fde5d542e348d52b03311a5b1d9e3ed959e83194921f51c4e0f66fa8aa33728235bcfdf9d9
7
+ data.tar.gz: a96051e482288a86001857773644f74c3a2749c975207016fe45efe09b5ec3f2b96edd491332f9baa9692c42eebf03bf265e09f15252dc43077c50ab664232de
@@ -0,0 +1,17 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
9
+ ruby: [2.6, 2.7, '3.0', jruby]
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: ${{ matrix.ruby }}
16
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
17
+ - run: bundle exec rake
@@ -26,6 +26,8 @@ Style/AndOr:
26
26
  EnforcedStyle: conditionals
27
27
  Style/AutoResourceCleanup:
28
28
  Enabled: true
29
+ Style/CollectionMethods:
30
+ Enabled: true
29
31
  Style/Documentation:
30
32
  Enabled: false
31
33
  Style/EmptyLiteral:
@@ -47,6 +49,13 @@ Style/NumericLiterals:
47
49
  Enabled: false
48
50
  Style/OptionHash:
49
51
  Enabled: true
52
+ Style/PercentLiteralDelimiters:
53
+ PreferredDelimiters:
54
+ "%w": "[]"
55
+ "%W": "[]"
56
+ "%i": "[]"
57
+ "%I": "[]"
58
+ "%r": "()"
50
59
  Style/ReturnNil:
51
60
  Enabled: true
52
61
  Style/SafeNavigation:
@@ -85,24 +94,27 @@ Lint/UnreachableCode:
85
94
  Enabled: false
86
95
  Lint/Void:
87
96
  Enabled: false
97
+ Layout/AlignHash:
98
+ EnforcedLastArgumentHashStyle: ignore_implicit
88
99
  Metrics/AbcSize:
89
100
  Max: 25
90
101
  Style/LambdaCall:
91
102
  Enabled: false
92
103
  Style/Semicolon:
93
104
  Enabled: false
94
- Naming/MethodParameterName:
105
+ Naming/UncommunicativeMethodParamName:
95
106
  Enabled: false
96
107
  Style/ClassAndModuleChildren:
97
108
  Enabled: false
98
109
  Layout/LeadingCommentSpace:
99
110
  Exclude:
100
111
  - 'test/docs/**/*'
101
- Layout/HashAlignment:
112
+ Layout/AlignHash:
102
113
  EnforcedHashRocketStyle: table
103
- EnforcedColonStyle: table
104
114
  Style/FrozenStringLiteralComment:
105
115
  Enabled: false
116
+ Layout/AlignHash:
117
+ EnforcedColonStyle: table
106
118
  SingleLineMethods:
107
119
  Enabled: false
108
120
  Style/Lambda:
@@ -120,8 +132,5 @@ Style/PercentLiteralDelimiters:
120
132
  "%r": '{}'
121
133
  "%w": '[]'
122
134
  "%": '{}'
123
- "%W": '[]'
124
- "%i": '[]'
125
- "%I": '[]'
126
135
  Style/HashSyntax:
127
136
  Enabled: false
data/CHANGES.md CHANGED
@@ -1,3 +1,14 @@
1
+ # 2.1.3
2
+
3
+ * Use `trailblazer-activity-dsl-linear` >= 1.0.0.
4
+ * Use Inject() API instead of `:inject`, `:input` etc in macros.
5
+
6
+ # 2.1.2
7
+
8
+ * Refactor `Contract::Build` to use TRB mechanics:
9
+ * `:input` and `:inject` to allow injection of the contract class.
10
+ * an `Option()` to wrap the builder code.
11
+
1
12
  # 2.1.1
2
13
 
3
14
  * Support for Ruby 3.0.
data/Gemfile CHANGED
@@ -9,7 +9,8 @@ gem "dry-matcher"
9
9
  # gem "trailblazer-macro", path: "../trailblazer-macro"
10
10
  # gem "trailblazer-activity", path: "../trailblazer-activity"
11
11
  # gem "trailblazer-activity-dsl-linear", path: "../trailblazer-activity-dsl-linear"
12
- # gem "reform", path: "../reform"
12
+ # gem "trailblazer-errors", path: "../trailblazer-errors"
13
+ # gem "trailblazer-developer", path: "../trailblazer-developer"
13
14
 
14
15
  gem "minitest-line"
15
16
 
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
  require "rubocop/rake_task"
4
4
 
5
- task :default => %i[test rubocop]
5
+ task :default => %i[test]
6
6
 
7
7
  Rake::TestTask.new(:test) do |test|
8
8
  test.libs << 'test'
@@ -2,41 +2,62 @@ require "reform"
2
2
 
3
3
  module Trailblazer
4
4
  module Macro
5
+ # This Circuit-task calls the {task} Option, then allows
6
+ # to run an arbitary block to process the option's result.
7
+ # @private
8
+ class CircuitTaskWithResultProcessing < Activity::TaskBuilder::Task # DISCUSS: extract to public?
9
+ def initialize(task, user_proc, block)
10
+ @block = block
11
+ super(task, user_proc)
12
+ end
13
+
14
+ def call_option(task_with_option_interface, (ctx, flow_options), **circuit_options)
15
+ result = super
16
+
17
+ @block.call(result, ctx)
18
+ end
19
+ end
20
+
5
21
  module Contract
6
22
  def self.Build(name: "default", constant: nil, builder: nil)
7
- task = lambda do |(options, flow_options), **circuit_options|
8
- result = Build.(options, circuit_options, name: name, constant: constant, builder: builder)
23
+ contract_path = :"contract.#{name}"
9
24
 
10
- return Activity::TaskBuilder.binary_signal_for(result, Activity::Right, Activity::Left),
11
- [options, flow_options]
12
- end
25
+ injections = {
26
+ Activity::Railway.Inject() => {
27
+ "#{contract_path}.class": ->(*) { constant }, # default to {constant} if not injected.
28
+ }
29
+ }
13
30
 
14
- {task: task, id: "contract.build"}
15
- end
31
+ # DISCUSS: can we force-default this via Inject()?
32
+ input = {
33
+ Activity::Railway.In() => ->(ctx, **) do
34
+ ctx.to_hash.merge(
35
+ constant: constant,
36
+ name: contract_path
37
+ )
38
+ end
39
+ }
16
40
 
17
- module Build
18
- # Build contract at runtime.
19
- def self.call(options, circuit_options, name: "default", constant: nil, builder: nil)
20
- # TODO: we could probably clean this up a bit at some point.
21
- contract_class = constant || options[:"contract.#{name}.class"] # DISCUSS: Injection possible here?
22
- model = options[:model]
23
- name = :"contract.#{name}"
24
-
25
- options[name] = if builder
26
- call_builder(options, circuit_options, builder: builder, constant: contract_class, name: name)
27
- else
28
- contract_class.new(model)
29
- end
30
- end
41
+ output = {
42
+ Activity::Railway.Out() => [contract_path]
43
+ }
31
44
 
32
- def self.call_builder(ctx, circuit_options, builder: raise, constant: raise, name: raise)
33
- tmp_options = ctx.to_hash.merge(
34
- constant: constant,
35
- name: name
36
- )
45
+ default_contract_builder = ->(ctx, model: nil, **) { ctx[:"#{contract_path}.class"].new(model) }
37
46
 
38
- Trailblazer::Option(builder).(ctx, keyword_arguments: tmp_options, **circuit_options) # TODO: why can't we build the {builder} at compile time?
39
- end
47
+ # proc is called via {Option()}.
48
+ task_option_proc = builder ? builder : default_contract_builder
49
+
50
+ # after the builder proc is run, assign its result to {:"contract.default"}.
51
+ ctx_assign_block = ->(result, ctx) { ctx[contract_path] = result }
52
+
53
+ task = CircuitTaskWithResultProcessing.new(Trailblazer::Option(task_option_proc), task_option_proc, ctx_assign_block)
54
+
55
+ {
56
+ task: task, id: "contract.build",
57
+ }.
58
+ merge(injections).
59
+ merge(input).
60
+ merge(output)
40
61
  end
41
62
 
42
63
  module DSL
@@ -6,15 +6,22 @@ module Trailblazer
6
6
  # Deviate to left track if optional key is not found in params.
7
7
  # Deviate to left if validation result falsey.
8
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?
9
- params_path = :"contract.#{name}.params" # extract_params! save extracted params here.
9
+ contract_path = :"contract.#{name}" # the contract instance
10
+ params_path = :"contract.#{name}.params" # extract_params! save extracted params here.
11
+ key_path = :"contract.#{name}.extract_key"
10
12
 
11
- extract = Validate::Extract.new(key: key, params_path: params_path).freeze
12
- validate = Validate.new(name: name, representer: representer, params_path: params_path, constant: constant).freeze
13
+ extract = Validate::Extract.new(key_path: key_path, params_path: params_path)
14
+ validate = Validate.new(name: name, representer: representer, params_path: params_path, contract_path: contract_path)
15
+
16
+ # These are defaulting dependency injection, more here
17
+ # https://trailblazer.to/2.1/docs/activity.html#activity-dependency-injection-inject-defaulting
18
+ extract_injections = {key_path => ->(*) { key }} # default to {key} if not injected.
19
+ validate_injections = {contract_path => ->(*) { constant }} # default the contract instance to {constant}, if not injected (or passed down from {Build()})
13
20
 
14
21
  # Build a simple Railway {Activity} for the internal flow.
15
22
  activity = Class.new(Activity::Railway(name: "Contract::Validate")) do
16
- step extract, id: "#{params_path}_extract", Output(:failure) => End(:extract_failure) unless skip_extract# || representer
17
- step validate, id: "contract.#{name}.call"
23
+ step extract, id: "#{params_path}_extract", Output(:failure) => End(:extract_failure), Activity::Railway.Inject() => extract_injections unless skip_extract# || representer
24
+ step validate, id: "contract.#{name}.call", Activity::Railway.Inject() => validate_injections
18
25
  end
19
26
 
20
27
  options = activity.Subprocess(activity)
@@ -32,17 +39,18 @@ module Trailblazer
32
39
  class Validate
33
40
  # Task: extract the contract's input from params by reading `:key`.
34
41
  class Extract
35
- def initialize(key: nil, params_path: nil)
36
- @key, @params_path = key, params_path
42
+ def initialize(key_path: nil, params_path: nil)
43
+ @key_path, @params_path = key_path, params_path
37
44
  end
38
45
 
39
46
  def call(ctx, params: {}, **)
40
- ctx[@params_path] = @key ? params[@key] : params
47
+ key = ctx[@key_path] # e.g. {:song}.
48
+ ctx[@params_path] = key ? params[key] : params
41
49
  end
42
50
  end
43
51
 
44
- def initialize(name: "default", representer: false, params_path: nil, constant: nil)
45
- @name, @representer, @params_path, @constant = name, representer, params_path, constant
52
+ def initialize(name: "default", representer: false, params_path: nil, contract_path: )
53
+ @name, @representer, @params_path, @contract_path = name, representer, params_path, contract_path
46
54
  end
47
55
 
48
56
  # Task: Validates contract `:name`.
@@ -54,20 +62,19 @@ module Trailblazer
54
62
  )
55
63
  end
56
64
 
57
- def validate!(options, representer: false, from: :document, params_path: nil)
58
- path = :"contract.#{@name}"
59
- contract = @constant || options[path]
65
+ def validate!(ctx, representer: false, from: :document, params_path: nil)
66
+ contract = ctx[@contract_path] # grab contract instance from "contract.default" (usually set in {Contract::Build()})
60
67
 
61
68
  # this is for 1.1-style compatibility and should be removed once we have Deserializer in place:
62
- options[:"result.#{path}"] = result =
69
+ ctx[:"result.#{@contract_path}"] = result =
63
70
  if representer
64
71
  # use :document as the body and let the representer deserialize to the contract.
65
72
  # this will be simplified once we have Deserializer.
66
73
  # translates to contract.("{document: bla}") { MyRepresenter.new(contract).from_json .. }
67
- contract.(options[from]) { |document| representer.new(contract).parse(document) }
74
+ contract.(ctx[from]) { |document| representer.new(contract).parse(document) }
68
75
  else
69
76
  # let Reform handle the deserialization.
70
- contract.(options[params_path])
77
+ contract.(ctx[params_path])
71
78
  end
72
79
 
73
80
  result.success?
@@ -2,7 +2,7 @@ module Trailblazer
2
2
  module Version
3
3
  module Macro
4
4
  module Contract
5
- VERSION = "2.1.1"
5
+ VERSION = "2.1.3"
6
6
  end
7
7
  end
8
8
  end
@@ -13,7 +13,5 @@ module Trailblazer
13
13
 
14
14
  # All macros sit in the {Trailblazer::Macro::Contract} namespace, where we forward calls from
15
15
  # operations and activities to.
16
- module Activity::DSL::Linear::Helper
17
- Contract = Macro::Contract
18
- end
16
+ Activity::DSL::Linear::Helper::Constants::Contract = Macro::Contract
19
17
  end
@@ -67,7 +67,11 @@ class DocsContractOverviewTest < Minitest::Spec
67
67
  | `-- End.failure
68
68
  `-- End.failure}
69
69
  end
70
+
71
+ # internal variables from {:builder} are excluded in public ctx.
72
+ it { Create.(params: {}).keys.inspect.must_equal %{[:params, :model, :\"result.model\", :\"contract.default\", :\"contract.default.params\", :\"representer.default.class\", :\"result.contract.default\"]} }
70
73
  end
74
+
71
75
  #---
72
76
  # contract MyContract
73
77
  class DocsContractExplicitTest < Minitest::Spec
@@ -257,6 +261,42 @@ class DocsContractKeyTest < Minitest::Spec
257
261
  end
258
262
  end
259
263
 
264
+ #---
265
+ #- Validate() with injected {:"contract.default.extract_key"}
266
+ class DocsContractInjectedKeyTest < Minitest::Spec
267
+ Song = Class.new(ContractConstantTest::Song)
268
+
269
+ module Song::Contract
270
+ Create = ContractConstantTest::Song::Contract::Create
271
+ end
272
+
273
+ #:inject-key-op
274
+ class Song::Create < Trailblazer::Operation
275
+ #~meths
276
+ step Model(Song, :new)
277
+ step Contract::Build(constant: Song::Contract::Create)
278
+ #~meths end
279
+ step Contract::Validate() # we don't define a key here! E.g. {key: "song"}
280
+ step Contract::Persist()
281
+ end
282
+ #:inject-key-op end
283
+
284
+ # empty {:params}, validation fails
285
+ it { Song::Create.(params: {}).inspect(:model, "result.contract.default.extract").must_equal %{<Result:false [#<struct DocsContractInjectedKeyTest::Song title=nil, length=nil>, nil] >} }
286
+ # no {:key} injected/defined, we don't find the data in {params}.
287
+ it { Song::Create.(params: {"song" => { title: "SVG", length: 13 }}).inspect(:model, "result.contract.default.extract").must_equal %{<Result:false [#<struct DocsContractInjectedKeyTest::Song title=nil, length=nil>, nil] >} }
288
+ # {:key} defined and everything works smoothly
289
+ it {
290
+ params = {"song" => { title: "SVG", length: 13 }}
291
+ #:inject-key-call
292
+ res = Song::Create.(
293
+ params: params,
294
+ "contract.default.extract_key": "song"
295
+ )
296
+ #:inject-key-call end
297
+ res.inspect(:model).must_equal %{<Result:true [#<struct DocsContractInjectedKeyTest::Song title=\"SVG\", length=13>] >} }
298
+ end
299
+
260
300
  #---
261
301
  #- Validate( key: :song ), Output(:extract_failure) => End(:my_new_end)
262
302
  class DocsContractKeyWithOutputTest < Minitest::Spec
@@ -297,6 +337,37 @@ class DocsContractKeyWithOutputTest < Minitest::Spec
297
337
  end
298
338
  end
299
339
 
340
+ #---
341
+ #- Validate() with injected {:"contract.default"} and no `Build()`.
342
+ class DocsContractInjectedContractTest < Minitest::Spec
343
+ Song = Class.new(ContractConstantTest::Song)
344
+
345
+ module Song::Contract
346
+ Create = ContractConstantTest::Song::Contract::Create
347
+ end
348
+
349
+ #:inject-contract-op
350
+ class Song::Create < Trailblazer::Operation
351
+ # we omit the {Model()} call as the run-time contract contains the model.
352
+ # we don't have a {Contract::Build()} step here.
353
+ step Contract::Validate(key: "song") # you could use an injection here, too!
354
+ step Contract::Persist()
355
+ end
356
+ #:inject-contract-op end
357
+
358
+ it {
359
+ params = {"song" => { title: "SVG", length: 13 }}
360
+ #:inject-contract-call
361
+ res = Song::Create.(
362
+ params: params,
363
+ "contract.default": Song::Contract::Create.new(Song.new) # we build the contract ourselves!
364
+ )
365
+ #:inject-contract-call end
366
+ res.inspect(:model).must_equal %{<Result:true [nil] >}
367
+ res[:"contract.default"].model.inspect.must_equal %{#<struct DocsContractInjectedContractTest::Song title=\"SVG\", length=13>}
368
+ }
369
+ end
370
+
300
371
  #---
301
372
  #- Validate( name: "default", invalid_data_terminus: true )
302
373
  class DocsContractInvalidEndTest < Minitest::Spec
@@ -377,24 +448,24 @@ class ContractInjectConstantTest < Minitest::Spec
377
448
  end
378
449
  #:di-constant-contract end
379
450
  #:di-constant
380
- class Create < Trailblazer::Operation
381
- step Model( Song, :new )
382
- step Contract::Build()
451
+ class Song::Create < Trailblazer::Operation
452
+ step Model(Song, :new)
453
+ step Contract::Build() # no constant provided here!
383
454
  step Contract::Validate()
384
- step Contract::Persist( method: :sync )
455
+ step Contract::Persist(method: :sync)
385
456
  end
386
457
  #:di-constant end
387
458
 
388
459
  it do
389
460
  #:di-contract-call
390
- Create.(
391
- params: { title: "Anthony's Song" },
392
- :"contract.default.class" => MyContract
461
+ Song::Create.(
462
+ params: { title: "Anthony's Song" },
463
+ "contract.default.class": MyContract # dependency injection!
393
464
  )
394
465
  #:di-contract-call end
395
466
  end
396
- it { Create.(params: { title: "A" }, :"contract.default.class" => MyContract).inspect(:model).must_equal %{<Result:false [#<struct ContractInjectConstantTest::Song id=nil, title=nil>] >} }
397
- it { Create.(params: { title: "Anthony's Song" }, :"contract.default.class" => MyContract).inspect(:model).must_equal %{<Result:true [#<struct ContractInjectConstantTest::Song id=nil, title="Anthony's Song">] >} }
467
+ it { Song::Create.(params: { title: "A" }, :"contract.default.class" => MyContract).inspect(:model).must_equal %{<Result:false [#<struct ContractInjectConstantTest::Song id=nil, title=nil>] >} }
468
+ it { Song::Create.(params: { title: "Anthony's Song" }, :"contract.default.class" => MyContract).inspect(:model).must_equal %{<Result:true [#<struct ContractInjectConstantTest::Song id=nil, title="Anthony's Song">] >} }
398
469
  end
399
470
 
400
471
  class DryValidationContractTest < Minitest::Spec
@@ -446,30 +517,47 @@ class DryValidationContractTest < Minitest::Spec
446
517
  it { Create.(params: { id: 1, title: "Y" }).inspect(:model).must_equal %{<Result:false [#<struct DryValidationContractTest::Song id=nil, title=nil>] >} }
447
518
  it { Create.(params: { id: 1, title: "Yo" }).inspect(:model).must_equal %{<Result:true [#<struct DryValidationContractTest::Song id=1, title="Yo">] >} }
448
519
 
449
- #---
520
+ ##---
450
521
  # Contract::Validate(constant: DrySchema)
451
- class OpWithSchema < Trailblazer::Operation
452
- Schema = Dry::Validation.Contract do
453
- params do
454
- required(:title).filled
522
+
523
+ #:dry-schema-contract
524
+ module Song::Operation
525
+ class Archive < Trailblazer::Operation
526
+ Schema = Dry::Validation.Contract do
527
+ params do
528
+ required(:id).filled
529
+ end
455
530
  end
456
- end
457
531
 
458
- step Model( Song, :new ) # FIXME.
459
- step Contract::Validate( constant: Schema, key: :song) # generic validate call for you.
532
+ # step Model(Song, :new) # You don't need {ctx[:model]}.
533
+ step Contract::Validate(constant: Schema, key: :song) # Your validation.
534
+ #~methods
535
+ # step Contract::Persist() # this is not possible!
536
+ #~methods end
537
+ end
460
538
  end
539
+ #:dry-schema-contract end
461
540
 
462
541
  # success
463
- it { OpWithSchema.(params: {song: { title: "SVG" }}).success?.must_equal true }
542
+ it { _(Song::Operation::Archive.(params: {song: {id: "SVG"}}).success?).must_equal true }
464
543
  # failure
465
- it { OpWithSchema.(params: {song: { title: nil }}).success?.must_equal false }
544
+ it { _(Song::Operation::Archive.(params: {song: {id: nil}}).success?).must_equal false }
545
+ # shows error messages
466
546
  it "shows error messages" do
467
- result = OpWithSchema.(params: {song: { title: nil }})
547
+ #:dry-contract-call
548
+ result = Song::Operation::Archive.(params: {song: {id: nil}})
549
+ #:dry-contract-call end
468
550
 
469
- result[:"result.contract.default"].errors.inspect.must_equal %{#<Dry::Validation::MessageSet messages=[#<Dry::Schema::Message text=\"must be filled\" path=[:title] predicate=:filled? input=nil>] options={:source=>[#<Dry::Schema::Message text=\"must be filled\" path=[:title] predicate=:filled? input=nil>], :hints=>false}>}
551
+ _(result[:"result.contract.default"].errors.inspect).must_equal %{#<Dry::Validation::MessageSet messages=[#<Dry::Schema::Message text=\"must be filled\" path=[:id] predicate=:filled? input=nil>] options={:source=>[#<Dry::Schema::Message text=\"must be filled\" path=[:id] predicate=:filled? input=nil>], :hints=>false}>}
552
+
553
+ # raise result[:"result.contract.default"].errors.messages[0].to_s.inspect
554
+ assert_equal result[:"result.contract.default"].errors[:id].inspect, %{["must be filled"]}
555
+ #:dry-contract-result
556
+ result[:"result.contract.default"].errors[:id] #=> ["must be filled"]
557
+ #:dry-contract-result end
470
558
  end
471
559
  # key not found
472
- it { OpWithSchema.(params: {}).success?.must_equal false }
560
+ it { _(Song::Operation::Archive.(params: {}).success?).must_equal false }
473
561
  end
474
562
 
475
563
  class DocContractBuilderTest < Minitest::Spec
@@ -505,6 +593,8 @@ class DocContractBuilderTest < Minitest::Spec
505
593
 
506
594
  it { Create.(params: {}).inspect(:model).must_equal %{<Result:false [#<struct DocContractBuilderTest::Song id=nil, title=nil>] >} }
507
595
  it { Create.(params: { title: "title"}, current_user: Module).inspect(:model).must_equal %{<Result:true [#<struct DocContractBuilderTest::Song id=nil, title="title">] >} }
596
+ # internal variables from {:builder} are excluded in public ctx.
597
+ it { Create.(params: {}).keys.inspect.must_equal %{[:params, :model, :\"result.model\", :\"contract.default\", :\"contract.default.params\", :\"representer.default.class\", :\"result.contract.default\"]} }
508
598
  end
509
599
 
510
600
  class DocContractTest < Minitest::Spec
@@ -547,3 +637,20 @@ class DocContractTest < Minitest::Spec
547
637
 
548
638
  it { Break.(params: { id:1, title: "Fame" }).inspect(:model).must_equal %{<Result:true [#<struct DocContractTest::Song id=1, title=nil>] >} }
549
639
  end
640
+
641
+ class ModelMissingTest < Minitest::Spec
642
+ class Create < Trailblazer::Operation
643
+ class MyContract < Reform::Form
644
+ property :duration, virtual: true
645
+ end
646
+
647
+ step Contract::Build(constant: MyContract)
648
+ step Contract::Validate()
649
+ end
650
+
651
+ it do
652
+ result = Create.(params: {duration: 18})
653
+ assert_equal true, result.success?
654
+ assert_equal 18, result[:"contract.default"].duration
655
+ end
656
+ end
@@ -23,15 +23,14 @@ Gem::Specification.new do |spec|
23
23
  spec.add_development_dependency "bundler"
24
24
  spec.add_development_dependency "dry-validation"
25
25
  spec.add_development_dependency "reform-rails", "~> 0.2.0.rc2"
26
- spec.add_development_dependency "trailblazer-operation", ">= 0.6.2"
27
- spec.add_development_dependency "trailblazer-macro", ">= 2.1.5"
26
+ spec.add_development_dependency "trailblazer-macro", ">= 2.1.9"
28
27
  spec.add_development_dependency "trailblazer-developer"
29
28
  spec.add_development_dependency "activemodel", "~> 6.0.0" # FIXME: we still don't support the Rails 6.1 errors object.
30
29
 
31
30
  spec.add_development_dependency "minitest"
32
31
  spec.add_development_dependency "rake"
33
32
 
34
- spec.add_dependency "trailblazer-activity-dsl-linear", ">= 0.4.0", "< 0.5.0"
33
+ spec.add_dependency "trailblazer-activity-dsl-linear", ">= 1.0.0", "< 1.1.0"
35
34
 
36
35
  spec.required_ruby_version = ">= 2.0.0"
37
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer-macro-contract
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-03 00:00:00.000000000 Z
11
+ date: 2022-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: reform
@@ -72,34 +72,20 @@ dependencies:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
74
  version: 0.2.0.rc2
75
- - !ruby/object:Gem::Dependency
76
- name: trailblazer-operation
77
- requirement: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: 0.6.2
82
- type: :development
83
- prerelease: false
84
- version_requirements: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - ">="
87
- - !ruby/object:Gem::Version
88
- version: 0.6.2
89
75
  - !ruby/object:Gem::Dependency
90
76
  name: trailblazer-macro
91
77
  requirement: !ruby/object:Gem::Requirement
92
78
  requirements:
93
79
  - - ">="
94
80
  - !ruby/object:Gem::Version
95
- version: 2.1.5
81
+ version: 2.1.9
96
82
  type: :development
97
83
  prerelease: false
98
84
  version_requirements: !ruby/object:Gem::Requirement
99
85
  requirements:
100
86
  - - ">="
101
87
  - !ruby/object:Gem::Version
102
- version: 2.1.5
88
+ version: 2.1.9
103
89
  - !ruby/object:Gem::Dependency
104
90
  name: trailblazer-developer
105
91
  requirement: !ruby/object:Gem::Requirement
@@ -162,20 +148,20 @@ dependencies:
162
148
  requirements:
163
149
  - - ">="
164
150
  - !ruby/object:Gem::Version
165
- version: 0.4.0
151
+ version: 1.0.0
166
152
  - - "<"
167
153
  - !ruby/object:Gem::Version
168
- version: 0.5.0
154
+ version: 1.1.0
169
155
  type: :runtime
170
156
  prerelease: false
171
157
  version_requirements: !ruby/object:Gem::Requirement
172
158
  requirements:
173
159
  - - ">="
174
160
  - !ruby/object:Gem::Version
175
- version: 0.4.0
161
+ version: 1.0.0
176
162
  - - "<"
177
163
  - !ruby/object:Gem::Version
178
- version: 0.5.0
164
+ version: 1.1.0
179
165
  description: Operation macros for form objects
180
166
  email:
181
167
  - apotonick@gmail.com
@@ -183,11 +169,11 @@ executables: []
183
169
  extensions: []
184
170
  extra_rdoc_files: []
185
171
  files:
172
+ - ".github/workflows/ci.yml"
186
173
  - ".gitignore"
187
174
  - ".rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml"
188
175
  - ".rubocop.yml"
189
176
  - ".rubocop_todo.yml"
190
- - ".travis.yml"
191
177
  - CHANGES.md
192
178
  - COMM-LICENSE
193
179
  - Gemfile
data/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- before_install: gem install bundler
4
- rvm:
5
- - 2.5.1
6
- - 2.4.4
7
- - 2.3.7
8
- # - 2.2.10