trailblazer-macro-contract 2.1.1 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
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