trailblazer 1.1.1 → 2.1.0

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 (60) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
  4. data/.rubocop.yml +20 -0
  5. data/.rubocop_todo.yml +556 -0
  6. data/.travis.yml +6 -7
  7. data/CHANGES.md +224 -0
  8. data/COMM-LICENSE +62 -0
  9. data/CONTRIBUTING.md +179 -0
  10. data/Gemfile +0 -10
  11. data/LICENSE +9 -0
  12. data/README.md +68 -189
  13. data/Rakefile +3 -3
  14. data/lib/trailblazer/version.rb +3 -1
  15. data/lib/trailblazer.rb +3 -6
  16. data/test/test_helper.rb +32 -3
  17. data/trailblazer.gemspec +11 -15
  18. metadata +28 -132
  19. data/LICENSE.txt +0 -22
  20. data/TODO.md +0 -11
  21. data/doc/Trb-The-Stack.png +0 -0
  22. data/doc/trb.jpg +0 -0
  23. data/gemfiles/Gemfile.rails.lock +0 -130
  24. data/gemfiles/Gemfile.reform-2.0 +0 -6
  25. data/gemfiles/Gemfile.reform-2.1 +0 -7
  26. data/lib/trailblazer/autoloading.rb +0 -15
  27. data/lib/trailblazer/endpoint.rb +0 -31
  28. data/lib/trailblazer/operation/builder.rb +0 -26
  29. data/lib/trailblazer/operation/callback.rb +0 -53
  30. data/lib/trailblazer/operation/collection.rb +0 -6
  31. data/lib/trailblazer/operation/controller.rb +0 -72
  32. data/lib/trailblazer/operation/dispatch.rb +0 -3
  33. data/lib/trailblazer/operation/model/dsl.rb +0 -29
  34. data/lib/trailblazer/operation/model/external.rb +0 -34
  35. data/lib/trailblazer/operation/model.rb +0 -50
  36. data/lib/trailblazer/operation/module.rb +0 -29
  37. data/lib/trailblazer/operation/policy/guard.rb +0 -35
  38. data/lib/trailblazer/operation/policy.rb +0 -85
  39. data/lib/trailblazer/operation/representer.rb +0 -98
  40. data/lib/trailblazer/operation/resolver.rb +0 -30
  41. data/lib/trailblazer/operation/uploaded_file.rb +0 -77
  42. data/lib/trailblazer/operation.rb +0 -175
  43. data/test/callback_test.rb +0 -104
  44. data/test/collection_test.rb +0 -57
  45. data/test/model_test.rb +0 -148
  46. data/test/module_test.rb +0 -93
  47. data/test/operation/builder_test.rb +0 -41
  48. data/test/operation/contract_test.rb +0 -30
  49. data/test/operation/controller_test.rb +0 -111
  50. data/test/operation/dsl/callback_test.rb +0 -118
  51. data/test/operation/dsl/contract_test.rb +0 -104
  52. data/test/operation/dsl/representer_test.rb +0 -142
  53. data/test/operation/external_model_test.rb +0 -71
  54. data/test/operation/guard_test.rb +0 -152
  55. data/test/operation/policy_test.rb +0 -97
  56. data/test/operation/reject_test.rb +0 -34
  57. data/test/operation/resolver_test.rb +0 -83
  58. data/test/operation_test.rb +0 -275
  59. data/test/representer_test.rb +0 -238
  60. data/test/rollback_test.rb +0 -47
@@ -1,104 +0,0 @@
1
- require 'test_helper'
2
- require "trailblazer/operation/callback"
3
-
4
- # callbacks are tested in Disposable::Callback::Group.
5
- class OperationCallbackTest < MiniTest::Spec
6
- Song = Struct.new(:name)
7
-
8
- class Create < Trailblazer::Operation
9
- include Trailblazer::Operation::Callback
10
-
11
- contract do
12
- property :name
13
- end
14
-
15
- callback do
16
- on_change :notify_me!
17
- on_change :notify_you!
18
- end
19
-
20
- # TODO: always dispatch, pass params.
21
-
22
- def process(params)
23
- @model = Song.new
24
-
25
- validate(params, @model) do
26
- callback!
27
- end
28
- end
29
-
30
- def dispatched
31
- @dispatched ||= []
32
- end
33
-
34
- private
35
- def notify_me!(*)
36
- dispatched << :notify_me!
37
- end
38
-
39
- def notify_you!(*)
40
- dispatched << :notify_you!
41
- end
42
- end
43
-
44
-
45
- class Update < Create
46
- # TODO: allow skipping groups.
47
- # skip_dispatch :notify_me!
48
-
49
- callback do
50
- remove! :on_change, :notify_me!
51
- end
52
- end
53
-
54
-
55
- it "invokes all callbacks" do
56
- op = Create.({"name"=>"Keep On Running"})
57
- op.dispatched.must_equal [:notify_me!, :notify_you!]
58
- end
59
-
60
- it "does not invoke removed callbacks" do
61
- op = Update.({"name"=>"Keep On Running"})
62
- op.dispatched.must_equal [:notify_you!]
63
- end
64
- end
65
-
66
- # TODO: remove in 1.2.
67
- require "trailblazer/operation/dispatch"
68
- class OperationDeprecatedDispatchTest < MiniTest::Spec
69
- Song = Struct.new(:name)
70
-
71
- class Create < Trailblazer::Operation
72
- include Trailblazer::Operation::Dispatch
73
-
74
- contract do
75
- property :name
76
- end
77
-
78
- callback do
79
- on_change :notify_me!
80
- end
81
-
82
- def process(params)
83
- @model = Song.new
84
-
85
- validate(params, @model) do
86
- dispatch!
87
- end
88
- end
89
-
90
- def dispatched
91
- @dispatched ||= []
92
- end
93
-
94
- private
95
- def notify_me!(*)
96
- dispatched << :notify_me!
97
- end
98
- end
99
-
100
- it "invokes all callbacks [deprecated]" do
101
- op = Create.({"name"=>"Keep On Running"})
102
- op.dispatched.must_equal [:notify_me!]
103
- end
104
- end
@@ -1,57 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/collection"
3
- require "trailblazer/operation/model"
4
-
5
- class CollectionTest < MiniTest::Spec
6
- Song = Struct.new(:title, :id) do
7
- class << self
8
- attr_accessor :all_records
9
-
10
- def all
11
- all_records
12
- end
13
- end
14
- end
15
-
16
-
17
- class CreateOperation < Trailblazer::Operation
18
- include Model
19
- model Song
20
- action :create
21
-
22
- contract do
23
- property :title
24
- validates :title, presence: true
25
- end
26
-
27
- def process(params)
28
- validate(params[:song]) do |f|
29
- f.sync
30
- end
31
- end
32
- end
33
-
34
- class FetchCollectionOperation < CreateOperation
35
- include Trailblazer::Operation::Collection
36
-
37
- model Song
38
-
39
- contract do
40
- property :title
41
- end
42
-
43
- def model!(params)
44
- Song.all
45
- end
46
- end
47
-
48
- # ::present.
49
- it do
50
- Song.all_records = [
51
- CreateOperation.(song: {title: "Blue Rondo a la Turk"}).model,
52
- CreateOperation.(song: {title: "Mercy Day For Mr. Vengeance"}).model
53
- ]
54
- op = FetchCollectionOperation.present(user_id: 0)
55
- op.model.must_equal Song.all_records
56
- end
57
- end
data/test/model_test.rb DELETED
@@ -1,148 +0,0 @@
1
- require 'test_helper'
2
- require 'trailblazer/operation'
3
-
4
- class ModelTest < MiniTest::Spec
5
- Song = Struct.new(:title, :id) do
6
- class << self
7
- attr_accessor :find_result # TODO: eventually, replace with AR test.
8
- attr_accessor :all_records
9
-
10
- def find(id)
11
- find_result
12
- end
13
- end
14
- end
15
-
16
- class CreateOperation < Trailblazer::Operation
17
- include Model
18
- model Song
19
- action :create
20
-
21
- contract do
22
- property :title
23
- validates :title, presence: true
24
- end
25
-
26
- def process(params)
27
- validate(params[:song]) do |f|
28
- f.sync
29
- end
30
- end
31
- end
32
-
33
-
34
- # creates model for you.
35
- it { CreateOperation.(song: {title: "Blue Rondo a la Turk"}).model.title.must_equal "Blue Rondo a la Turk" }
36
- # exposes #model.
37
- it { CreateOperation.(song: {title: "Blue Rondo a la Turk"}).model.must_be_instance_of Song }
38
-
39
- class ModifyingCreateOperation < CreateOperation
40
- def process(params)
41
- model.instance_eval { def genre; "Punkrock"; end }
42
-
43
- validate(params[:song]) do |f|
44
- f.sync
45
- end
46
- end
47
- end
48
-
49
- # lets you modify model.
50
- it { ModifyingCreateOperation.(song: {title: "Blue Rondo a la Turk"}).model.title.must_equal "Blue Rondo a la Turk" }
51
- it { ModifyingCreateOperation.(song: {title: "Blue Rondo a la Turk"}).model.genre.must_equal "Punkrock" }
52
-
53
- # Update
54
- class UpdateOperation < CreateOperation
55
- action :update
56
- end
57
-
58
- # finds model and updates.
59
- it do
60
- song = CreateOperation.(song: {title: "Anchor End"}).model
61
- Song.find_result = song
62
-
63
- UpdateOperation.(id: song.id, song: {title: "The Rip"}).model.title.must_equal "The Rip"
64
- song.title.must_equal "The Rip"
65
- end
66
-
67
- # Find == Update
68
- class FindOperation < CreateOperation
69
- action :find
70
- end
71
-
72
- # finds model and updates.
73
- it do
74
- song = CreateOperation.(song: {title: "Anchor End"}).model
75
- Song.find_result = song
76
-
77
- FindOperation.(id: song.id, song: {title: "The Rip"}).model.title.must_equal "The Rip"
78
- song.title.must_equal "The Rip"
79
- end
80
-
81
-
82
- class DefaultCreateOperation < Trailblazer::Operation
83
- include Model
84
- model Song
85
-
86
- def process(params)
87
- self
88
- end
89
- end
90
-
91
- # uses :create as default if not set via ::action.
92
- it { DefaultCreateOperation.({}).model.must_equal Song.new }
93
-
94
- # model Song, :action
95
- class ModelUpdateOperation < CreateOperation
96
- model Song, :update
97
- end
98
-
99
- # allows ::model, :action.
100
- it do
101
- Song.find_result = song = Song.new
102
- ModelUpdateOperation.({id: 1, song: {title: "Mercy Day For Mr. Vengeance"}}).model.must_equal song
103
- end
104
-
105
-
106
-
107
- # Op#setup_model!
108
- class SetupModelOperation < CreateOperation
109
- def setup_model!(params)
110
- model.instance_eval { @params = params; def params; @params.to_s; end }
111
- end
112
- end
113
-
114
- it { SetupModelOperation.(song: {title: "Emily Kane"}).model.params.must_equal "{:song=>{:title=>\"Emily Kane\"}}" }
115
-
116
-
117
-
118
- # no call to ::model raises error.
119
- class NoModelOperation < Trailblazer::Operation
120
- include Model
121
-
122
- def process(params)
123
- self
124
- end
125
- end
126
-
127
- # uses :create as default if not set via ::action.
128
- it { assert_raises(RuntimeError){ NoModelOperation.({}) } }
129
-
130
- # allow passing validate(params, model, contract_class)
131
- class OperationWithPrivateContract < Trailblazer::Operation
132
- include Model
133
- model Song
134
-
135
- class Contract < Reform::Form
136
- property :title
137
- end
138
-
139
- def process(params)
140
- validate(params[:song], model, Contract) do |f|
141
- f.sync
142
- end
143
- end
144
- end
145
-
146
- # uses private Contract class.
147
- it { OperationWithPrivateContract.(song: {title: "Blue Rondo a la Turk"}).model.title.must_equal "Blue Rondo a la Turk" }
148
- end
data/test/module_test.rb DELETED
@@ -1,93 +0,0 @@
1
- require 'test_helper'
2
- require "trailblazer/operation/module"
3
- require "trailblazer/operation/dispatch"
4
-
5
- class OperationModuleTest < MiniTest::Spec
6
- Song = Struct.new(:name, :artist)
7
- Artist = Struct.new(:id, :full_name)
8
-
9
- class Create < Trailblazer::Operation
10
- include Trailblazer::Operation::Dispatch
11
-
12
- contract do
13
- property :name
14
- property :artist, populate_if_empty: Artist do
15
- property :id
16
- end
17
- end
18
-
19
- callback do
20
- on_change :notify_me!
21
- end
22
-
23
- def process(params)
24
- @model = Song.new
25
-
26
- validate(params, @model) do
27
- contract.sync
28
-
29
- dispatch!
30
- end
31
- end
32
-
33
- def dispatched
34
- @dispatched ||= []
35
- end
36
-
37
- private
38
- def notify_me!(*)
39
- dispatched << :notify_me!
40
- end
41
- end
42
-
43
-
44
- module SignedIn
45
- include Trailblazer::Operation::Module
46
-
47
- contract do
48
- property :artist, inherit: true do
49
- property :full_name
50
- end
51
- end
52
-
53
- callback do
54
- on_change :notify_you!
55
- end
56
-
57
- def notify_you!(*)
58
- dispatched << :notify_you!
59
- end
60
- end
61
-
62
-
63
- class Update < Create
64
- callback do
65
- on_change :notify_them!
66
- end
67
-
68
- include SignedIn
69
-
70
- def notify_them!(*)
71
- dispatched << :notify_them!
72
- end
73
- end
74
-
75
-
76
- it do
77
- op = Create.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
78
-
79
- op.dispatched.must_equal [:notify_me!]
80
- op.model.name.must_equal "Feelings"
81
- op.model.artist.id.must_equal 1
82
- op.model.artist.full_name.must_equal nil # property not declared.
83
- end
84
-
85
- it do
86
- op = Update.({name: "Feelings", artist: {id: 1, full_name: "The Offspring"}})
87
-
88
- op.dispatched.must_equal [:notify_me!, :notify_them!, :notify_you!]
89
- op.model.name.must_equal "Feelings"
90
- op.model.artist.id.must_equal 1
91
- op.model.artist.full_name.must_equal "The Offspring" # property declared via Module.
92
- end
93
- end
@@ -1,41 +0,0 @@
1
- require "test_helper"
2
-
3
- class OperationBuilderTest < MiniTest::Spec
4
- class ParentOperation < Trailblazer::Operation
5
- def process(params)
6
- end
7
-
8
- class Sub < self
9
- end
10
-
11
- builds do |params|
12
- Sub if params[:sub]
13
- end
14
- end
15
-
16
- it { ParentOperation.run({}).last.class.must_equal ParentOperation }
17
- it { ParentOperation.run({sub: true}).last.class.must_equal ParentOperation::Sub }
18
- it { ParentOperation.({}).class.must_equal ParentOperation }
19
- it { ParentOperation.({sub: true}).class.must_equal ParentOperation::Sub }
20
- end
21
-
22
- class OperationBuilderClassTest < MiniTest::Spec
23
- class SuperOperation < Trailblazer::Operation
24
- builds do |params|
25
- self::Sub if params[:sub] # Sub is defined in ParentOperation.
26
- end
27
- end
28
-
29
- class ParentOperation < Trailblazer::Operation
30
- def process(params)
31
- end
32
-
33
- class Sub < self
34
- end
35
-
36
- self.builder_class = SuperOperation.builder_class
37
- end
38
-
39
- it { ParentOperation.({}).class.must_equal ParentOperation }
40
- it { ParentOperation.({sub: true}).class.must_equal ParentOperation::Sub }
41
- end
@@ -1,30 +0,0 @@
1
- require "test_helper"
2
-
3
-
4
- class OperationContractTest < MiniTest::Spec
5
-
6
- class Operation < Trailblazer::Operation
7
- contract do
8
- property :id
9
- property :title
10
- property :length
11
- end
12
-
13
- def process(params)
14
- @model = Struct.new(:id, :title, :length).new
15
-
16
- contract.id = 1
17
- validate(params) do
18
- contract.length = 3
19
- end
20
- end
21
- end
22
-
23
- # allow using #contract before #validate.
24
- it do
25
- op = Operation.(title: "Beethoven")
26
- op.contract.id.must_equal 1
27
- op.contract.title.must_equal "Beethoven"
28
- op.contract.length.must_equal 3
29
- end
30
- end
@@ -1,111 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/controller"
3
-
4
- class ControllerTest < Minitest::Spec
5
- def self.controller!(&block)
6
- let (:_controller) {
7
- Class.new do
8
- include Trailblazer::Operation::Controller
9
-
10
- def initialize(params={})
11
- @params = params
12
- end
13
- attr_reader :params, :request
14
-
15
- class_eval(&block)
16
- self
17
- end
18
- }
19
- end
20
-
21
- def controller(params={})
22
- _controller.new(params)
23
- end
24
-
25
-
26
- User = Struct.new(:role)
27
-
28
- Comment = Struct.new(:body)
29
-
30
- class Comment::Update < Trailblazer::Operation
31
- def model!(params)
32
- Comment.new(params[:body])
33
- end
34
-
35
- def inspect
36
- super.sub(/:0x\w+/, "")
37
- end
38
- end
39
-
40
-
41
- describe "#present with options" do
42
- controller! do
43
- def show
44
- present Comment::Update, params: { current_user: User.new(:admin) }
45
- end
46
- end
47
-
48
- it do
49
- controller.show.inspect.must_equal "#<ControllerTest::Comment::Update @options={}, @valid=true, @params={:current_user=>#<struct ControllerTest::User role=:admin>}, @model=#<struct ControllerTest::Comment body=nil>>"
50
- end
51
- end
52
-
53
- describe "#params!" do
54
- controller! do
55
- def show
56
- present Comment::Update, params: "Cool!"
57
- end
58
-
59
- def params!(params)
60
- { body: params }
61
- end
62
- end
63
-
64
- it { controller.show.inspect.must_equal "#<ControllerTest::Comment::Update @options={}, @valid=true, @params={:body=>\"Cool!\"}, @model=#<struct ControllerTest::Comment body=\"Cool!\">>" }
65
- end
66
-
67
- describe "#form" do
68
- class Comment::Create < Trailblazer::Operation
69
- def model!(params)
70
- Comment.new
71
- end
72
-
73
- contract do
74
- def prepopulate!(options)
75
- @options = options
76
- end
77
- attr_reader :options
78
- end
79
- end
80
-
81
- describe "#prepopulate! options" do
82
- controller! do
83
- def show
84
- form Comment::Create
85
- end
86
- end
87
-
88
- it { controller(__body: "Great!").show.options.inspect.must_equal "{:params=>{:__body=>\"Great!\"}}" }
89
- end
90
-
91
- describe "with additional options" do
92
- controller! do
93
- def show
94
- form Comment::Create, admin: true
95
- end
96
- end
97
-
98
- it { controller(__body: "Great!").show.options.inspect.must_equal "{:admin=>true, :params=>{:__body=>\"Great!\"}}" }
99
- end
100
-
101
- describe "with options and :params" do
102
- controller! do
103
- def show
104
- form Comment::Create, admin: true, params: params.merge(user: User.new)
105
- end
106
- end
107
-
108
- it { controller(__body: "Great!").show.options.inspect.must_equal "{:admin=>true, :params=>{:__body=>\"Great!\", :user=>#<struct ControllerTest::User role=nil>}}" }
109
- end
110
- end
111
- end
@@ -1,118 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/dispatch"
3
-
4
-
5
- class DslCallbackTest < MiniTest::Spec
6
- module SongProcess
7
- def process(params)
8
- contract(OpenStruct.new).validate(params)
9
- dispatch!
10
- end
11
-
12
- def _invocations
13
- @_invocations ||= []
14
- end
15
-
16
- def self.included(includer)
17
- includer.contract do
18
- property :title
19
- end
20
- end
21
- end
22
-
23
- describe "inheritance across operations" do
24
- class Operation < Trailblazer::Operation
25
- include Dispatch
26
- include SongProcess
27
-
28
- callback do
29
- on_change :default!
30
- end
31
-
32
- class Admin < self
33
- callback do
34
- on_change :admin_default!
35
- end
36
-
37
- callback(:after_save) { on_change :after_save! }
38
-
39
- def admin_default!(*); _invocations << :admin_default!; end
40
- def after_save!(*); _invocations << :after_save!; end
41
-
42
- def process(*)
43
- super
44
- dispatch!(:after_save)
45
- end
46
- end
47
-
48
- def default!(*); _invocations << :default!; end
49
- end
50
-
51
- it { Operation.({"title"=> "Love-less"})._invocations.must_equal([:default!]) }
52
- it { Operation::Admin.({"title"=> "Love-less"})._invocations.must_equal([:default!, :admin_default!, :after_save!]) }
53
- end
54
-
55
- describe "Op.callback" do
56
- it { Operation.callback(:default).must_equal Operation.callbacks[:default][:group] }
57
- end
58
-
59
- describe "Op.callback :after_save, AfterSaveCallback" do
60
- class AfterSaveCallback < Disposable::Callback::Group
61
- on_change :after_save!
62
-
63
- def after_save!(twin, options)
64
- options[:operation]._invocations << :after_save!
65
- end
66
- end
67
-
68
- class OpWithExternalCallback < Trailblazer::Operation
69
- include Dispatch
70
- include SongProcess
71
- callback :after_save, AfterSaveCallback
72
-
73
- def process(params)
74
- contract(OpenStruct.new).validate(params)
75
- dispatch!(:after_save)
76
- end
77
- end
78
-
79
- it { OpWithExternalCallback.("title"=>"Thunder Rising")._invocations.must_equal([:after_save!]) }
80
- end
81
-
82
- describe "Op.callback :after_save, AfterSaveCallback do .. end" do
83
- class DefaultCallback < Disposable::Callback::Group
84
- on_change :default!
85
-
86
- def default!(twin, options)
87
- options[:operation]._invocations << :default!
88
- end
89
- end
90
-
91
- class OpUsingCallback < Trailblazer::Operation
92
- include Dispatch
93
- include SongProcess
94
- callback :default, DefaultCallback
95
- end
96
-
97
- class OpExtendingCallback < Trailblazer::Operation
98
- include Dispatch
99
- include SongProcess
100
- callback :default, DefaultCallback do
101
- on_change :after_save!
102
-
103
- def default!(twin, options)
104
- options[:operation]._invocations << :extended_default!
105
- end
106
-
107
- def after_save!(twin, options)
108
- options[:operation]._invocations << :after_save!
109
- end
110
- end
111
- end
112
-
113
- # this operation copies DefaultCallback and shouldn't run #after_save!.
114
- it { OpUsingCallback.(title: "Thunder Rising")._invocations.must_equal([:default!]) }
115
- # this operation copies DefaultCallback, extends it and runs #after_save!.
116
- it { OpExtendingCallback.(title: "Thunder Rising")._invocations.must_equal([:extended_default!, :after_save!]) }
117
- end
118
- end