trailblazer 2.0.7 → 2.1.0.beta1

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +35 -1
  3. data/Gemfile +6 -12
  4. data/README.md +3 -1
  5. data/Rakefile +6 -17
  6. data/lib/trailblazer.rb +7 -4
  7. data/lib/trailblazer/deprecation/call.rb +46 -0
  8. data/lib/trailblazer/deprecation/context.rb +43 -0
  9. data/lib/trailblazer/operation/contract.rb +40 -9
  10. data/lib/trailblazer/operation/deprecations.rb +21 -0
  11. data/lib/trailblazer/operation/guard.rb +5 -5
  12. data/lib/trailblazer/operation/model.rb +15 -10
  13. data/lib/trailblazer/operation/nested.rb +56 -85
  14. data/lib/trailblazer/operation/persist.rb +4 -2
  15. data/lib/trailblazer/operation/policy.rb +16 -7
  16. data/lib/trailblazer/operation/pundit.rb +3 -3
  17. data/lib/trailblazer/operation/representer.rb +5 -0
  18. data/lib/trailblazer/operation/rescue.rb +12 -9
  19. data/lib/trailblazer/operation/validate.rb +36 -29
  20. data/lib/trailblazer/operation/wrap.rb +49 -11
  21. data/lib/trailblazer/task.rb +20 -0
  22. data/lib/trailblazer/version.rb +1 -1
  23. data/test/benchmark.rb +63 -0
  24. data/test/deprecation/call_test.rb +42 -0
  25. data/test/deprecation/context_test.rb +19 -0
  26. data/test/docs/contract_test.rb +73 -53
  27. data/test/docs/dry_test.rb +2 -2
  28. data/test/docs/fast_test.rb +133 -13
  29. data/test/docs/guard_test.rb +28 -35
  30. data/test/docs/macro_test.rb +1 -1
  31. data/test/docs/model_test.rb +13 -13
  32. data/test/docs/nested_test.rb +54 -122
  33. data/test/docs/operation_test.rb +42 -43
  34. data/test/docs/pundit_test.rb +16 -16
  35. data/test/docs/representer_test.rb +18 -18
  36. data/test/docs/rescue_test.rb +29 -29
  37. data/test/docs/trace_test.rb +82 -0
  38. data/test/docs/wrap_test.rb +59 -26
  39. data/test/module_test.rb +75 -75
  40. data/test/nested_test.rb +293 -0
  41. data/test/operation/contract_test.rb +23 -153
  42. data/test/operation/dsl/contract_test.rb +9 -9
  43. data/test/operation/dsl/representer_test.rb +169 -169
  44. data/test/operation/model_test.rb +15 -21
  45. data/test/operation/persist_test.rb +18 -11
  46. data/test/operation/pundit_test.rb +25 -23
  47. data/test/operation/representer_test.rb +254 -254
  48. data/test/test_helper.rb +5 -2
  49. data/test/variables_test.rb +158 -0
  50. data/trailblazer.gemspec +1 -1
  51. data/untitled +33 -0
  52. metadata +25 -27
  53. data/lib/trailblazer/operation/callback.rb +0 -35
  54. data/lib/trailblazer/operation/procedural/contract.rb +0 -15
  55. data/lib/trailblazer/operation/procedural/validate.rb +0 -22
  56. data/test/operation/callback_test.rb +0 -70
  57. data/test/operation/dsl/callback_test.rb +0 -106
  58. data/test/operation/params_test.rb +0 -36
  59. data/test/operation/pipedream_test.rb +0 -59
  60. data/test/operation/pipetree_test.rb +0 -104
  61. data/test/operation/present_test.rb +0 -24
  62. data/test/operation/resolver_test.rb +0 -47
  63. data/test/operation_test.rb +0 -143
@@ -8,8 +8,7 @@ Reform::Form.class_eval do
8
8
  include Reform::Form::ActiveModel::Validations
9
9
  end
10
10
 
11
- require "trailblazer/operation/callback"
12
- require "trailblazer/operation/procedural/validate"
11
+ # require "trailblazer/deprecation/context.rb"
13
12
 
14
13
  module Mock
15
14
  class Result
@@ -27,3 +26,7 @@ module Test
27
26
  end
28
27
  ReturnResult = ->(last, input, options) { input }
29
28
  end
29
+
30
+ require "pp"
31
+
32
+ Minitest::Spec::Operation = Trailblazer::Operation
@@ -0,0 +1,158 @@
1
+ require "test_helper"
2
+
3
+ class VariablesTest < Minitest::Spec
4
+ # Nested op copies values and modifies/amplifies some of them.
5
+ class Whistleblower < Trailblazer::Operation
6
+ step ->(options, public_opinion:, **) { options["edward.public_opinion"] = public_opinion.upcase } , id: "edward.public_opinion"
7
+ step ->(options, secret:, **) { options["edward.secret"] = secret } , id: "edward.secret"
8
+ step ->(options, rumours:, **) { rumours.nil? ? rumours : options["edward.rumours"] = rumours*2 } , id: "edward.test"
9
+ pass ->(options, **) { options["edward.public_knowledge"] = options["public_knowledge"] }, id: "edward.read.public_knowledge"
10
+ end
11
+
12
+ =begin
13
+ Everything public. options are simply passed on.
14
+
15
+ Both Org and Edward write and read from the same Context instance.
16
+ =end
17
+ class OpenOrganization < Trailblazer::Operation
18
+ step ->(options, **) { options["rumours"] = "Bla" }
19
+ step ->(options, **) { options["secret"] = "Psst!" }
20
+
21
+ step Nested( Whistleblower )
22
+
23
+ step ->(options, **) { options["org.rumours"] = options["edward.rumours"] } # what can we see from Edward?
24
+ step ->(options, **) { options["org.secret"] = options["edward.secret"] } # what can we see from Edward?
25
+ end
26
+
27
+ it do
28
+ result = OpenOrganization.("public_opinion" => "Freedom!", "public_knowledge" => true)
29
+
30
+ result.inspect("public_opinion", "rumours", "secret", "edward.public_opinion", "edward.secret", "edward.rumours", "edward.public_knowledge").
31
+ must_equal %{<Result:true ["Freedom!", "Bla", "Psst!", "FREEDOM!", "Psst!", "BlaBla", true] >}
32
+ end
33
+
34
+ #---
35
+ #- simply passes on the context
36
+ class ConsideringButOpenOrganization < Trailblazer::Operation
37
+ step ->(options, **) { options["rumours"] = "Bla" }
38
+ step ->(options, **) { options["secret"] = "Psst!" }
39
+
40
+ step Nested( Whistleblower, input: :input! )
41
+
42
+ step ->(options, **) { options["org.rumours"] = options["edward.rumours"] } # what can we see from Edward?
43
+ step ->(options, **) { options["org.secret"] = options["edward.secret"] } # what can we see from Edward?
44
+
45
+ def input!(options, **)
46
+ options
47
+ end
48
+ end
49
+
50
+ it do
51
+ result = ConsideringButOpenOrganization.("public_opinion" => "Freedom!")
52
+
53
+ result.inspect("public_opinion", "rumours", "secret", "edward.public_opinion", "edward.secret", "edward.rumours").
54
+ must_equal %{<Result:true ["Freedom!", "Bla", "Psst!", "FREEDOM!", "Psst!", "BlaBla"] >}
55
+ end
56
+
57
+ =begin
58
+ Explicitely passes allowed variables, only.
59
+
60
+ Edward can only read those three variables and can't see "public_knowledge" or others.
61
+ =end
62
+ class ProtectedOrganization < ConsideringButOpenOrganization
63
+ def input!(options, **)
64
+ {
65
+ "public_opinion" => options["public_opinion"],
66
+ "secret" => 0,
67
+ "rumours" => 0.0
68
+ }
69
+ end
70
+ end
71
+
72
+ it do
73
+ result = ProtectedOrganization.( {}, "public_opinion" => "Freedom!", "public_knowledge" => true )
74
+
75
+ result.inspect("public_opinion", "rumours", "secret", "edward.public_opinion", "edward.secret", "edward.rumours", "edward.public_knowledge").
76
+ must_equal %{<Result:true ["Freedom!", "Bla", "Psst!", "FREEDOM!", 0, 0.0, nil] >}
77
+ end
78
+
79
+ #---
80
+ #- new ctx
81
+ =begin
82
+ :input produces a differing value for an existing key "secret"
83
+ this should only be visible in the nested activity, and the original
84
+ "secret" must be what it was before (unless :output would change that.)
85
+
86
+ NOTE: i decided it's better to not even allow Context#merge because it
87
+ defeats the idea of hiding information via :input and overcomplicates
88
+ the scoping.
89
+ =end
90
+ class EncryptedOrganization < ConsideringButOpenOrganization
91
+ def input!(options, **)
92
+ options.merge( "secret" => options["secret"]+"XxX" )
93
+ end
94
+ end
95
+
96
+ it do
97
+ skip "no options.merge until we know we actually need it"
98
+ result = EncryptedOrganization.("public_opinion" => "Freedom!")
99
+
100
+ result.inspect("public_opinion", "rumours", "secret", "edward.public_opinion", "edward.secret", "edward.rumours").
101
+ must_equal %{<Result:true ["Freedom!", "Bla", "Psst!", "FREEDOM!", "Psst!XxX", "BlaBla"] >}
102
+ end
103
+
104
+ # TODO write to options and get error
105
+
106
+ =begin
107
+ Simply passes on the context to Edward, but applies an :output filter,
108
+ so that Org can't see several nested values such as "edward.public_knowledge"
109
+ or "edward.rumours" (see steps in #read-section).
110
+ =end
111
+ class DiscreetOrganization < Trailblazer::Operation
112
+ step ->(options, **) { options["rumours"] = "Bla" }, id: "set.rumours"
113
+ step ->(options, **) { options["secret"] = "Psst!" }, id: "set.secret"
114
+
115
+ step Nested( Whistleblower, input: :input!, output: :output! )
116
+
117
+ #read-section
118
+ pass ->(options, **) { options["org.rumours"] = options["edward.rumours"] }, id: "read.edward.rumours" # what can we see from Edward?
119
+ step ->(options, **) { options["org.secret"] = options["edward.secret"] }, id: "read.edward.secret" # what can we see from Edward?
120
+
121
+ def input!(options, **)
122
+ options
123
+ end
124
+
125
+ def output!(options, **)
126
+ {
127
+ "out.keys" => options.keys,
128
+ "out.rumours" => options["edward.rumours"].slice(0..2),
129
+ "out.secret" => options["edward.secret"].reverse,
130
+ }
131
+ end
132
+ end
133
+
134
+ it do
135
+ result = DiscreetOrganization.("public_opinion" => "Freedom!", "public_knowledge" => true)
136
+
137
+ result.inspect("public_opinion", "rumours", "secret", "edward.public_opinion", "edward.secret", "edward.rumours", "out.keys", "out.rumours", "out.secret", "org.rumours", "org.secret", "public_knowledge", "edward.public_knowledge").
138
+ must_equal %{<Result:false [\"Freedom!\", \"Bla\", \"Psst!\", nil, nil, nil, [\"edward.public_opinion\", \"edward.secret\", \"edward.rumours\", \"edward.public_knowledge\"], \"Bla\", \"!tssP\", nil, nil, true, nil] >}
139
+ end
140
+
141
+ it "with tracing" do
142
+ result = DiscreetOrganization.trace("public_opinion" => "Freedom!")
143
+
144
+ result.wtf.gsub(/0x\w+/, "").gsub(/\d+/, "").must_equal %{|-- #<Trailblazer::Activity::Start:>
145
+ |-- set.rumours
146
+ |-- set.secret
147
+ |-- Nested(VariablesTest::Whistleblower)
148
+ | |-- #<Trailblazer::Activity::Start:>
149
+ | |-- edward.public_opinion
150
+ | |-- edward.secret
151
+ | |-- edward.test
152
+ | |-- edward.read.public_knowledge
153
+ | `-- #<Trailblazer::Operation::Railway::End::Success:>
154
+ |-- read.edward.rumours
155
+ |-- read.edward.secret
156
+ `-- #<Trailblazer::Operation::Railway::End::Failure:>}
157
+ end
158
+ end
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
19
19
 
20
- spec.add_dependency "trailblazer-operation", ">= 0.0.12", "< 0.1.0"
20
+ spec.add_dependency "trailblazer-operation", ">= 0.1.2", "< 0.2.0"
21
21
  spec.add_dependency "reform", ">= 2.2.0", "< 3.0.0"
22
22
  spec.add_dependency "declarative"
23
23
 
@@ -0,0 +1,33 @@
1
+
2
+ __call__
3
+ :params => {}
4
+ :current_user => ,
5
+ "contract.default.class" =>
6
+
7
+
8
+
9
+
10
+
11
+ skills = Skill(container, container)
12
+ skill[:a]
13
+ READ-ONLY
14
+
15
+ ctx = Context(skills) => Context.new(Skill.new {}, skills)
16
+ ctx[:a] => <skill>
17
+ ctx[:a]= 1
18
+ ctx.merge => new Context
19
+
20
+ ctx.to_hash
21
+
22
+
23
+
24
+ step(Context options, **)
25
+
26
+
27
+ input(Context options, **, Hash::Immutable mutable:, Skill runtime:)
28
+
29
+
30
+
31
+ Hash::Immutable
32
+
33
+ Skills < Hash::Immutable
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.7
4
+ version: 2.1.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-08 00:00:00.000000000 Z
11
+ date: 2017-12-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: trailblazer-operation
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.12
19
+ version: 0.1.2
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: 0.1.0
22
+ version: 0.2.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: 0.0.12
29
+ version: 0.1.2
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: 0.1.0
32
+ version: 0.2.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: reform
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -169,25 +169,29 @@ files:
169
169
  - doc/operation-2017.png
170
170
  - doc/trb.jpg
171
171
  - lib/trailblazer.rb
172
+ - lib/trailblazer/deprecation/call.rb
173
+ - lib/trailblazer/deprecation/context.rb
172
174
  - lib/trailblazer/dsl.rb
173
175
  - lib/trailblazer/operation/auto_inject.rb
174
- - lib/trailblazer/operation/callback.rb
175
176
  - lib/trailblazer/operation/contract.rb
177
+ - lib/trailblazer/operation/deprecations.rb
176
178
  - lib/trailblazer/operation/guard.rb
177
179
  - lib/trailblazer/operation/model.rb
178
180
  - lib/trailblazer/operation/module.rb
179
181
  - lib/trailblazer/operation/nested.rb
180
182
  - lib/trailblazer/operation/persist.rb
181
183
  - lib/trailblazer/operation/policy.rb
182
- - lib/trailblazer/operation/procedural/contract.rb
183
- - lib/trailblazer/operation/procedural/validate.rb
184
184
  - lib/trailblazer/operation/pundit.rb
185
185
  - lib/trailblazer/operation/representer.rb
186
186
  - lib/trailblazer/operation/rescue.rb
187
187
  - lib/trailblazer/operation/test.rb
188
188
  - lib/trailblazer/operation/validate.rb
189
189
  - lib/trailblazer/operation/wrap.rb
190
+ - lib/trailblazer/task.rb
190
191
  - lib/trailblazer/version.rb
192
+ - test/benchmark.rb
193
+ - test/deprecation/call_test.rb
194
+ - test/deprecation/context_test.rb
191
195
  - test/docs/auto_inject_test.rb
192
196
  - test/docs/contract_test.rb
193
197
  - test/docs/dry_test.rb
@@ -201,28 +205,24 @@ files:
201
205
  - test/docs/pundit_test.rb
202
206
  - test/docs/representer_test.rb
203
207
  - test/docs/rescue_test.rb
208
+ - test/docs/trace_test.rb
204
209
  - test/docs/wrap_test.rb
205
210
  - test/gemfiles/Gemfile.ruby-1.9
206
211
  - test/gemfiles/Gemfile.ruby-2.0
207
212
  - test/gemfiles/Gemfile.ruby-2.3
208
213
  - test/module_test.rb
209
- - test/operation/callback_test.rb
214
+ - test/nested_test.rb
210
215
  - test/operation/contract_test.rb
211
- - test/operation/dsl/callback_test.rb
212
216
  - test/operation/dsl/contract_test.rb
213
217
  - test/operation/dsl/representer_test.rb
214
218
  - test/operation/model_test.rb
215
- - test/operation/params_test.rb
216
219
  - test/operation/persist_test.rb
217
- - test/operation/pipedream_test.rb
218
- - test/operation/pipetree_test.rb
219
- - test/operation/present_test.rb
220
220
  - test/operation/pundit_test.rb
221
221
  - test/operation/representer_test.rb
222
- - test/operation/resolver_test.rb
223
- - test/operation_test.rb
224
222
  - test/test_helper.rb
223
+ - test/variables_test.rb
225
224
  - trailblazer.gemspec
225
+ - untitled
226
226
  homepage: http://trailblazer.to
227
227
  licenses:
228
228
  - LGPL-3.0
@@ -238,9 +238,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
238
238
  version: 2.0.0
239
239
  required_rubygems_version: !ruby/object:Gem::Requirement
240
240
  requirements:
241
- - - ">="
241
+ - - ">"
242
242
  - !ruby/object:Gem::Version
243
- version: '0'
243
+ version: 1.3.1
244
244
  requirements: []
245
245
  rubyforge_project:
246
246
  rubygems_version: 2.6.8
@@ -248,6 +248,9 @@ signing_key:
248
248
  specification_version: 4
249
249
  summary: A high-level architecture for Ruby and Rails.
250
250
  test_files:
251
+ - test/benchmark.rb
252
+ - test/deprecation/call_test.rb
253
+ - test/deprecation/context_test.rb
251
254
  - test/docs/auto_inject_test.rb
252
255
  - test/docs/contract_test.rb
253
256
  - test/docs/dry_test.rb
@@ -261,24 +264,19 @@ test_files:
261
264
  - test/docs/pundit_test.rb
262
265
  - test/docs/representer_test.rb
263
266
  - test/docs/rescue_test.rb
267
+ - test/docs/trace_test.rb
264
268
  - test/docs/wrap_test.rb
265
269
  - test/gemfiles/Gemfile.ruby-1.9
266
270
  - test/gemfiles/Gemfile.ruby-2.0
267
271
  - test/gemfiles/Gemfile.ruby-2.3
268
272
  - test/module_test.rb
269
- - test/operation/callback_test.rb
273
+ - test/nested_test.rb
270
274
  - test/operation/contract_test.rb
271
- - test/operation/dsl/callback_test.rb
272
275
  - test/operation/dsl/contract_test.rb
273
276
  - test/operation/dsl/representer_test.rb
274
277
  - test/operation/model_test.rb
275
- - test/operation/params_test.rb
276
278
  - test/operation/persist_test.rb
277
- - test/operation/pipedream_test.rb
278
- - test/operation/pipetree_test.rb
279
- - test/operation/present_test.rb
280
279
  - test/operation/pundit_test.rb
281
280
  - test/operation/representer_test.rb
282
- - test/operation/resolver_test.rb
283
- - test/operation_test.rb
284
281
  - test/test_helper.rb
282
+ - test/variables_test.rb
@@ -1,35 +0,0 @@
1
- require "disposable/callback"
2
-
3
- class Trailblazer::Operation
4
- def self.Callback(group)
5
- step = ->(input, options) { Callback.(group, input, options) }
6
-
7
- [ step, name: "callback.#{group}" ]
8
- end
9
-
10
- module Callback
11
- def self.call(name=:default, operation, options)
12
- config = options["callback.#{name}.class"] || raise #.fetch(name) # TODO: test exception
13
- group = config[:group].new(options["contract.default"])
14
-
15
- options[:context] ||= (config[:context] == :operation ? operation : group)
16
- group.(options)
17
-
18
- options["result.callback.#{name}"] = group
19
- end
20
-
21
- module DSL
22
- def callback(name=:default, constant=nil, &block)
23
- heritage.record(:callback, name, constant, &block)
24
-
25
- # FIXME: make this nicer. we want to extend same-named callback groups.
26
- # TODO: allow the same with contract, or better, test it!
27
- extended = self["callback.#{name}.class"] && self["callback.#{name}.class"]
28
-
29
- path, group_class = Trailblazer::DSL::Build.new.({ prefix: :callback, class: Disposable::Callback::Group, container: self }, name, constant, block) { |extended| extended[:group] }
30
-
31
- self[path] = { group: group_class, context: constant ? nil : :operation }
32
- end
33
- end
34
- end
35
- end
@@ -1,15 +0,0 @@
1
- module Trailblazer::Operation::Procedural
2
- # THIS IS UNTESTED, PRIVATE API AND WILL BE REMOVED SOON.
3
- module Contract
4
- # Instantiate the contract, either by using the user's contract passed into #validate
5
- # or infer the Operation contract.
6
- def contract_for(model:self["model"], options:{}, contract_class:self["contract.default.class"])
7
- contract!(model: model, options: options, contract_class: contract_class)
8
- end
9
-
10
- # Override to construct your own contract.
11
- def contract!(model:nil, options:{}, contract_class:nil)
12
- contract_class.new(model, options)
13
- end
14
- end
15
- end
@@ -1,22 +0,0 @@
1
- module Trailblazer::Operation::Procedural
2
- module Validate
3
- def validate(params, contract:self["contract.default"], path:"contract.default") # :params
4
- # DISCUSS: should we only have path here and then look up contract ourselves?
5
- result = validate_contract(contract, params) # run validation. # FIXME: must be overridable.
6
-
7
- self["result.#{path}"] = result
8
-
9
- if valid = result.success? # FIXME: to_bool or success?
10
- yield result if block_given?
11
- else
12
- # self["errors.#{path}"] = result.errors # TODO: remove me
13
- end
14
-
15
- valid
16
- end
17
-
18
- def validate_contract(contract, params)
19
- contract.(params)
20
- end
21
- end
22
- end