trailblazer 2.0.7 → 2.1.0.beta1

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