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
@@ -1,70 +0,0 @@
1
- require 'test_helper'
2
-
3
- # callbacks are tested in Disposable::Callback::Group.
4
- class OperationCallbackTest < MiniTest::Spec
5
- Song = Struct.new(:name)
6
-
7
- #---
8
- # with contract and disposable semantics
9
- class Create < Trailblazer::Operation
10
- extend Contract::DSL
11
-
12
- contract do
13
- property :name
14
- end
15
-
16
- step Model( Song, :new )
17
- step Contract::Build()
18
- step Contract::Validate()
19
- step Callback( :default )
20
-
21
-
22
- extend Callback::DSL
23
-
24
- callback do
25
- on_change :notify_me!
26
- on_change :notify_you!
27
- end
28
-
29
-
30
- # TODO: always dispatch, pass params.
31
-
32
- def dispatched
33
- self["dispatched"] ||= []
34
- end
35
-
36
- private
37
- def notify_me!(*)
38
- dispatched << :notify_me!
39
- end
40
-
41
- def notify_you!(*)
42
- dispatched << :notify_you!
43
- end
44
- end
45
-
46
-
47
- class Update < Create
48
- # TODO: allow skipping groups.
49
- # skip_dispatch :notify_me!
50
-
51
- callback do
52
- remove! :on_change, :notify_me!
53
- end
54
- end
55
-
56
- #---
57
- #- inheritance
58
- it { Update["pipetree"].inspect.must_equal %{[>operation.new,>model.build,>contract.build,>contract.default.validate,>callback.default]} }
59
-
60
-
61
- it "invokes all callbacks" do
62
- res = Create.({"name"=>"Keep On Running"})
63
- res["dispatched"].must_equal [:notify_me!, :notify_you!]
64
- end
65
-
66
- it "does not invoke removed callbacks" do
67
- res = Update.({"name"=>"Keep On Running"})
68
- res["dispatched"].must_equal [:notify_you!]
69
- end
70
- end
@@ -1,106 +0,0 @@
1
- require "test_helper"
2
-
3
- class DslCallbackTest < MiniTest::Spec
4
- module SongProcess
5
- def _invocations
6
- self["x"] ||= []
7
- end
8
-
9
- def self.included(includer)
10
- includer.extend Trailblazer::Operation::Contract::DSL
11
- includer.contract do
12
- property :title
13
- end
14
- includer.| Trailblazer::Operation::Model[OpenStruct, :new]
15
- includer.| Trailblazer::Operation::Contract::Build[includer["contract.default.class"]]
16
- includer.| Trailblazer::Operation::Contract::Validate[]
17
- includer.| Trailblazer::Operation::Callback[:default]
18
- end
19
- end
20
-
21
- describe "inheritance across operations" do
22
- class Operation < Trailblazer::Operation
23
- extend Callback::DSL
24
- include SongProcess
25
-
26
- callback do
27
- on_change :default!
28
- end
29
-
30
- class Admin < self
31
- callback do
32
- on_change :admin_default!
33
- end
34
-
35
- callback(:after_save) { on_change :after_save! }
36
-
37
- def admin_default!(*); _invocations << :admin_default!; end
38
- def after_save!(*); _invocations << :after_save!; end
39
-
40
- step Trailblazer::Operation::Callback[:after_save]
41
- end
42
-
43
- def default!(*); _invocations << :default!; end
44
- end
45
-
46
- it { Operation.({"title"=> "Love-less"})["x"].must_equal([:default!]) }
47
- it { Operation::Admin.({"title"=> "Love-less"})["x"].must_equal([:default!, :admin_default!, :after_save!]) }
48
- end
49
-
50
- describe "Op.callback :after_save, AfterSaveCallback" do
51
- class AfterSaveCallback < Disposable::Callback::Group
52
- on_change :after_save!
53
-
54
- def after_save!(twin, options)
55
- options[:operation]._invocations << :after_save!
56
- end
57
- end
58
-
59
- class OpWithExternalCallback < Trailblazer::Operation
60
- include SongProcess
61
- extend Callback::DSL
62
- callback :after_save, AfterSaveCallback
63
-
64
- step Callback[:after_save]
65
- end
66
-
67
- it { OpWithExternalCallback.("title"=>"Thunder Rising").must_equal([:after_save!]) }
68
- end
69
-
70
- describe "Op.callback :after_save, AfterSaveCallback do .. end" do
71
- class DefaultCallback < Disposable::Callback::Group
72
- on_change :default!
73
-
74
- def default!(twin, options)
75
- options[:operation]._invocations << :default!
76
- end
77
- end
78
-
79
- class OpUsingCallback < Trailblazer::Operation
80
- extend Callback::DSL
81
- include SongProcess
82
- callback :default, DefaultCallback
83
- end
84
-
85
- class OpExtendingCallback < Trailblazer::Operation
86
- extend Callback::DSL
87
- include SongProcess
88
- callback :default, DefaultCallback do
89
- on_change :after_save!
90
-
91
- def default!(twin, options)
92
- options[:operation]._invocations << :extended_default!
93
- end
94
-
95
- def after_save!(twin, options)
96
- options[:operation]._invocations << :after_save!
97
- end
98
- end
99
- end
100
-
101
- # this operation copies DefaultCallback and shouldn't run #after_save!.
102
- it { OpUsingCallback.(title: "Thunder Rising")["x"].must_equal([:default!]) }
103
- # this operation copies DefaultCallback, extends it and runs #after_save!.
104
- it { OpExtendingCallback.(title: "Thunder Rising")["x"].must_equal([:extended_default!, :after_save!]) }
105
- end
106
- end
@@ -1,36 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/params"
3
-
4
- class OperationParamsTest < MiniTest::Spec
5
- #---
6
- # in call(params) and process(params), argument is always self["params"]
7
- class Find < Trailblazer::Operation
8
- def process(params); self["eql?"] = self["params"].object_id==params.object_id end
9
- end
10
- class Seek < Trailblazer::Operation
11
- include Test::ReturnCall
12
- def call(params); self["eql?"] = self["params"].object_id==params.object_id end
13
- end
14
-
15
-
16
- it { Find.({ id: 1 })["eql?"].must_equal true }
17
- # self["params"] is what gets passed in.
18
- it { Find.({ id: 1 })["params"].must_equal({ id: 1 }) }
19
- it { Seek.({ id: 1 }).must_equal true }
20
-
21
-
22
- #---
23
- # change self["params"] via Operation#
24
- class Create < Trailblazer::Operation
25
- include Params
26
- def process(params) self[:x] = params end
27
- def params!(params); params.merge(garrett: "Rocks!") end
28
- end
29
-
30
- # allows you changing params in #setup_params!.
31
- it {
32
- result = Create.( id: 1 )
33
- result["params"].inspect.must_equal %{{:id=>1, :garrett=>\"Rocks!\"}}
34
- result["params.original"].inspect.must_equal %{{:id=>1}}
35
- }
36
- end
@@ -1,59 +0,0 @@
1
- require "test_helper"
2
-
3
- class PipedreamTest < Minitest::Spec
4
- Song = Struct.new(:title)
5
-
6
- class Create < Trailblazer::Operation
7
- class MyContract < Reform::Form
8
- property :title
9
- end
10
-
11
- class Auth
12
- def initialize(user, model); @user, @model = user, model end
13
- def user_and_model?; @user == Module && @model.class == Song end
14
- end
15
-
16
- # design principles:
17
- # * include as less code as possible into the op class.
18
- # * make the flow super explicit without making it cryptic (only 3 new operators)
19
- # * avoid including DSL modules in favor of passing those configurations directly to the "step".
20
-
21
-
22
-
23
- step Model[ Song, :new ] # model!)
24
- step Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
25
- step Contract[ MyContract]
26
- step Policy[ Auth, :user_and_model?]
27
- failure Contract[ MyContract]
28
-
29
- # step :model
30
- # step :guard
31
- # step :contract
32
-
33
-
34
- # ok Model[Song, :new] # model!)
35
- # ok Policy::Guard[ ->(options){ options["current_user"] == ::Module } ]
36
- # ok Contract[MyContract]
37
- # fail Contract[MyContract]
38
- # step> "contract"
39
-
40
- # | :bla
41
- # | ->
42
-
43
- end
44
-
45
- # TODO: test with contract constant (done).
46
- # test with inline contract.
47
- # test with override contract!.
48
-
49
- it do
50
- puts Create["pipetree"].inspect(style: :rows)
51
- result = Create.({}, { "current_user" => Module })
52
-
53
- result["model"].inspect.must_equal %{#<struct PipedreamTest::Song title=nil>}
54
- result["result.policy"].success?.must_equal true
55
- result["contract"].class.superclass.must_equal Reform::Form
56
-
57
-
58
- end
59
- end
@@ -1,104 +0,0 @@
1
- require "test_helper"
2
-
3
- # self["pipetree"] = ::Pipetree[
4
- # Trailblazer::Operation::New,
5
- # # SetupParams,
6
- # Trailblazer::Operation::Model::Build,
7
- # Trailblazer::Operation::Model::Assign,
8
- # Trailblazer::Operation::Call,
9
- # ]
10
-
11
-
12
-
13
- # def deserialize(*)
14
- # super
15
- # self.datetime = DateTime.parse("#{date} #{time}")
16
- # end
17
-
18
- class PipetreeTest < Minitest::Spec
19
- Song = Struct.new(:title)
20
-
21
- class Create < Trailblazer::Operation
22
- include Builder
23
- include Pipetree # this will add the functions, again, unfortunately. definitely an error source.
24
- end
25
-
26
- it { Create["pipetree"].inspect.must_equal %{[>>Build,>>New,>>Call,Result::Build,>>New,>>Call,Result::Build]} }
27
-
28
- #---
29
- # playground
30
- require "trailblazer/operation/policy"
31
- require "trailblazer/operation/guard"
32
-
33
- class Edit < Trailblazer::Operation
34
- include Builder
35
- include Policy::Guard
36
- include Contract::Step
37
- contract do
38
- property :title
39
- validates :title, presence: true
40
- end
41
-
42
-
43
- MyValidate = ->(input, options) { res= input.validate(options["params"]) { |f| f.sync } }
44
- # we can have a separate persist step and wrap in transaction. where do we pass contract, though?
45
- step MyValidate, before: Call #replace: Contract::ValidLegacySwitch
46
- #
47
- MyAfterSave = ->(input, options) { input["after_save"] = true }
48
- success MyAfterSave, after: MyValidate
49
-
50
- ValidateFailureLogger = ->(input, options) { input["validate fail"] = true }
51
- failure ValidateFailureLogger, after: MyValidate
52
-
53
- success ->(input, options) { input.process(options["params"]) }, replace: Call, name: "my.params"
54
-
55
- include Model
56
-
57
- LogBreach = ->(input, options) { input.log_breach! }
58
-
59
- failure LogBreach, after: Policy::Evaluate
60
-
61
- model Song
62
- policy ->(*) { self["current_user"] }
63
-
64
- def log_breach!
65
- self["breach"] = true
66
- end
67
-
68
- def process(params)
69
- self["my.valid"] = true
70
- end
71
-
72
- self["pipetree"]._insert(Contract::ValidLegacySwitch, {delete: true}, nil, nil)
73
- end
74
-
75
- puts Edit["pipetree"].inspect(style: :rows)
76
-
77
- it { Edit["pipetree"].inspect.must_equal %{[>>operation.build,>>operation.new,&model.build,&policy.guard.evaluate,<LogBreach,>contract.build,&MyValidate,<ValidateFailureLogger,>MyAfterSave,>my.params["params"]]} }
78
-
79
- # valid case.
80
- it {
81
- # puts "valid"
82
- # puts Edit["pipetree"].inspect(style: :rows)
83
- result = Edit.({ title: "Stupid 7" }, "current_user" => true)
84
- # puts "success! #{result.inspect}"
85
- result["my.valid"].must_equal true
86
- result["breach"].must_equal nil
87
- result["after_save"].must_equal true
88
- result["validate fail"].must_equal nil
89
- }
90
- # beach! i mean breach!
91
- it {
92
- # puts "beach"
93
- # puts Edit["pipetree"].inspect(style: :rows)
94
- result = Edit.({})
95
- # puts "@@@@@ #{result.inspect}"
96
- result["my.valid"].must_equal nil
97
- result["breach"].must_equal true
98
- result["validate fail"].must_equal true
99
- result["after_save"].must_equal nil
100
- }
101
- end
102
-
103
- # TODO: show the execution path in pipetree
104
- # unified result.contract, result.policy interface
@@ -1,24 +0,0 @@
1
- require "test_helper"
2
-
3
- require "trailblazer/operation/present"
4
-
5
- class PresentTest < Minitest::Spec
6
- class Create < Trailblazer::Operation
7
- include Test::ReturnCall
8
- include Present
9
-
10
- include Model::Builder
11
- def model!(*); Object end
12
-
13
- def call(params)
14
- "#call run!"
15
- end
16
- end
17
-
18
- it do
19
- result = Create.present
20
- result["model"].must_equal Object
21
- end
22
-
23
- it { Create.().must_equal "#call run!" }
24
- end
@@ -1,47 +0,0 @@
1
- # require "test_helper"
2
-
3
- # class ResolverTest < Minitest::Spec
4
- # Song = Struct.new(:id) do
5
- # def self.find(id); new(id) end
6
- # end
7
-
8
- # class Auth
9
- # def initialize(*args); @user, @model = *args end
10
- # def only_user?; @user == Module && @model.nil? end
11
- # def user_object?; @user == Object end
12
- # def user_and_model?; @user == Module && @model.class == Song end
13
- # def inspect; "<Auth: user:#{@user.inspect}, model:#{@model.inspect}>" end
14
- # end
15
-
16
- # class A < Trailblazer::Operation
17
- # extend Builder::DSL
18
- # builds ->(options) {
19
- # return P if options["params"] == { some: "params", id:1 }
20
- # return B if options["policy.default"].inspect == %{<Auth: user:Module, model:#<struct ResolverTest::Song id=3>>} # both user and model:id are set!
21
- # return M if options["model"].inspect == %{#<struct ResolverTest::Song id=9>}
22
- # }
23
-
24
- # step Model( Song, :update ), before: "operation.new"
25
- # step Policy::Pundit( Auth, :user_and_model? ), before: "operation.new"
26
- # require "trailblazer/operation/resolver"
27
- # step Resolver(), before: "operation.new"
28
-
29
- # step :process
30
-
31
- # class P < self; end
32
- # class B < self; end
33
- # class M < self; end
34
-
35
- # def process(*); self["x"] = self.class end
36
- # end
37
-
38
- # it { A["pipetree"].inspect.must_equal %{[&model.build,&policy.default.eval,>>builder.call,>>operation.new,&process]} }
39
-
40
- # it { r=A.({ some: "params", id: 1 }, { "current_user" => Module })
41
- # puts r.inspect
42
-
43
- # }
44
- # it { A.({ some: "params", id: 1 }, { "current_user" => Module })["x"].must_equal A::P }
45
- # it { A.({ id: 3 }, { "current_user" => Module })["x"].must_equal A::B }
46
- # it { A.({ id: 9 }, { "current_user" => Module })["x"].must_equal A::M }
47
- # end