trailblazer 1.1.1 → 2.1.0

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