trailblazer 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -3
  3. data/CHANGES.md +33 -0
  4. data/Gemfile +4 -1
  5. data/README.md +171 -166
  6. data/Rakefile +10 -2
  7. data/gemfiles/Gemfile.rails.lock +42 -11
  8. data/lib/trailblazer/autoloading.rb +4 -3
  9. data/lib/trailblazer/endpoint.rb +40 -0
  10. data/lib/trailblazer/operation.rb +15 -8
  11. data/lib/trailblazer/operation/collection.rb +7 -0
  12. data/lib/trailblazer/operation/controller.rb +30 -22
  13. data/lib/trailblazer/operation/controller/active_record.rb +6 -2
  14. data/lib/trailblazer/operation/crud.rb +3 -5
  15. data/lib/trailblazer/operation/dispatch.rb +29 -0
  16. data/lib/trailblazer/operation/representer.rb +41 -5
  17. data/lib/trailblazer/operation/responder.rb +2 -2
  18. data/lib/trailblazer/operation/uploaded_file.rb +4 -4
  19. data/lib/trailblazer/operation/worker.rb +8 -10
  20. data/lib/trailblazer/rails/railtie.rb +10 -7
  21. data/lib/trailblazer/version.rb +1 -1
  22. data/test/collection_test.rb +56 -0
  23. data/test/crud_test.rb +23 -1
  24. data/test/dispatch_test.rb +63 -0
  25. data/test/operation_test.rb +84 -125
  26. data/test/rails/controller_test.rb +51 -0
  27. data/test/rails/endpoint_test.rb +86 -0
  28. data/test/rails/fake_app/cells.rb +2 -2
  29. data/test/rails/fake_app/config.rb +1 -1
  30. data/test/rails/fake_app/controllers.rb +27 -0
  31. data/test/rails/fake_app/models.rb +10 -1
  32. data/test/rails/fake_app/rails_app.rb +15 -1
  33. data/test/rails/fake_app/song/operations.rb +38 -2
  34. data/test/rails/fake_app/views/bands/index.html.erb +1 -0
  35. data/test/rails/fake_app/views/songs/another_view.html.erb +2 -0
  36. data/test/representer_test.rb +126 -0
  37. data/test/responder_test.rb +2 -4
  38. data/test/rollback_test.rb +47 -0
  39. data/test/test_helper.rb +43 -1
  40. data/test/uploaded_file_test.rb +4 -4
  41. data/test/worker_test.rb +13 -9
  42. data/trailblazer.gemspec +7 -3
  43. metadata +68 -29
@@ -18,7 +18,7 @@ module Trailblazer::Operation::Responder
18
18
  [1]
19
19
  end
20
20
 
21
- def to_json(*)
22
- self.class.representer_class.new(model).to_json
21
+ def to_model
22
+ @model
23
23
  end
24
24
  end
@@ -18,9 +18,9 @@ module Trailblazer
18
18
  path = persist!
19
19
 
20
20
  hash = {
21
- :filename => @uploaded.original_filename,
22
- :type => @uploaded.content_type,
23
- :tempfile_path => path
21
+ filename: @uploaded.original_filename,
22
+ type: @uploaded.content_type,
23
+ tempfile_path: path
24
24
  }
25
25
 
26
26
  cleanup!
@@ -40,7 +40,7 @@ module Trailblazer
40
40
  file.close # TODO: can we test that?
41
41
  File.unlink(file)
42
42
 
43
- ActionDispatch::Http::UploadedFile.new(hash.merge(:tempfile => tmp))
43
+ ActionDispatch::Http::UploadedFile.new(hash.merge(tempfile: tmp))
44
44
  end
45
45
 
46
46
  private
@@ -1,10 +1,10 @@
1
1
  require 'sidekiq/worker'
2
- # require 'active_support/hash_with_indifferent_access'
3
2
  require 'active_support/core_ext/hash/indifferent_access'
4
3
 
5
4
 
6
5
  class Trailblazer::Operation
7
6
  # only kicks in when Operation::run, #run will still do it real-time
7
+ # Works with Reform 2, only.
8
8
  module Worker
9
9
  def self.included(base)
10
10
  base.send(:include, Sidekiq::Worker) # TODO: this will work with any bg gem.
@@ -61,22 +61,20 @@ class Trailblazer::Operation
61
61
  private
62
62
  module ClassMethods
63
63
  def file_marshaller_representer
64
- @file_marshaller_representer ||= contract_class.schema.apply do |dfn|
65
- dfn.delete!(:prepare)
66
-
64
+ @file_marshaller_representer ||= contract_class.schema(include: [Representable::Hash]).apply do |dfn|
67
65
  dfn.merge!(
68
- :getter => lambda { |*| self[dfn.name.to_sym] },
69
- :setter => lambda { |fragment, *| self[dfn.name.to_s] = fragment }
66
+ getter: lambda { |*| self[dfn.name.to_sym] },
67
+ setter: lambda { |fragment, *| self[dfn.name.to_s] = fragment }
70
68
  ) # FIXME: allow both sym and str.
71
69
 
72
- dfn.merge!(:class => Hash) and next if dfn[:form] # nested properties need a class for deserialization.
70
+ dfn.merge!(class: Hash) and next if dfn[:form] or dfn[:twin] # nested properties need a class for deserialization.
73
71
  next unless dfn[:file]
74
72
 
75
73
  # TODO: where do we set /tmp/uploads?
76
74
  dfn.merge!(
77
- :serialize => lambda { |file, *| Trailblazer::Operation::UploadedFile.new(file, :tmp_dir => "/tmp/uploads").to_hash },
78
- :deserialize => lambda { |object, hash, *| Trailblazer::Operation::UploadedFile.from_hash(hash) },
79
- :class => Hash
75
+ serialize: lambda { |file, *| Trailblazer::Operation::UploadedFile.new(file, tmp_dir: "/tmp/uploads").to_hash },
76
+ deserialize: lambda { |object, hash, *| Trailblazer::Operation::UploadedFile.from_hash(hash) },
77
+ class: Hash
80
78
  )
81
79
  end
82
80
  end
@@ -1,6 +1,6 @@
1
1
  module Trailblazer
2
2
  class Railtie < Rails::Railtie
3
- def self.autoload_crud_operations(app)
3
+ def self.autoload_operations(app)
4
4
  Dir.glob("app/concepts/**/crud.rb") do |f|
5
5
  path = f.sub("app/concepts/", "")
6
6
  model = path.sub("/crud.rb", "")
@@ -10,16 +10,19 @@ module Trailblazer
10
10
  end
11
11
  end
12
12
 
13
+ def self.autoload_cells(app)
14
+ Dir.glob("app/concepts/**/*cell.rb") do |f|
15
+ require_dependency "#{app.root}/#{f}" # load app/concepts/{concept}/cell.rb.
16
+ end
17
+ end
18
+
13
19
  # thank you, http://stackoverflow.com/a/17573888/465070
14
20
  initializer 'trailblazer.install', after: :load_config_initializers do |app|
15
21
  # the trb autoloading has to be run after initializers have been loaded, so we can tweak inclusion of features in
16
22
  # initializers.
17
- if Rails.configuration.cache_classes
18
- Trailblazer::Railtie.autoload_crud_operations(app)
19
- else
20
- ActionDispatch::Reloader.to_prepare do
21
- Trailblazer::Railtie.autoload_crud_operations(app)
22
- end
23
+ ActionDispatch::Reloader.to_prepare do
24
+ Trailblazer::Railtie.autoload_operations(app)
25
+ Trailblazer::Railtie.autoload_cells(app)
23
26
  end
24
27
  end
25
28
  end
@@ -1,3 +1,3 @@
1
1
  module Trailblazer
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,56 @@
1
+ require "test_helper"
2
+ require "trailblazer/operation/collection"
3
+
4
+ class CollectionTest < MiniTest::Spec
5
+ Song = Struct.new(:title, :id) do
6
+ class << self
7
+ attr_accessor :all_records
8
+
9
+ def all
10
+ all_records
11
+ end
12
+ end
13
+ end
14
+
15
+
16
+ class CreateOperation < Trailblazer::Operation
17
+ include CRUD
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
+ class FetchCollectionOperation < CreateOperation
34
+ include Trailblazer::Operation::Collection
35
+
36
+ model Song
37
+
38
+ contract do
39
+ property :title
40
+ end
41
+
42
+ def model!(params)
43
+ Song.all
44
+ end
45
+ end
46
+
47
+ # ::present.
48
+ it do
49
+ Song.all_records = [
50
+ CreateOperation.(song: {title: "Blue Rondo a la Turk"}).model,
51
+ CreateOperation.(song: {title: "Mercy Day For Mr. Vengeance"}).model
52
+ ]
53
+ op = FetchCollectionOperation.present(user_id: 0)
54
+ op.model.must_equal Song.all_records
55
+ end
56
+ end
data/test/crud_test.rb CHANGED
@@ -5,6 +5,7 @@ class CrudTest < MiniTest::Spec
5
5
  Song = Struct.new(:title, :id) do
6
6
  class << self
7
7
  attr_accessor :find_result # TODO: eventually, replace with AR test.
8
+ attr_accessor :all_records
8
9
 
9
10
  def find(id)
10
11
  find_result
@@ -102,6 +103,7 @@ class CrudTest < MiniTest::Spec
102
103
  end
103
104
 
104
105
 
106
+
105
107
  # Op#setup_model!
106
108
  class SetupModelOperation < CreateOperation
107
109
  def setup_model!(params)
@@ -141,4 +143,24 @@ class CrudTest < MiniTest::Spec
141
143
  end
142
144
 
143
145
  it { ContractKnowsModelNameOperation.present(song: {title: "Direct Hit"}).contract.class.model_name.to_s.must_equal "CrudTest::Song" }
144
- end
146
+
147
+
148
+ # allow passing validate(params, model, contract_class)
149
+ class OperationWithPrivateContract < Trailblazer::Operation
150
+ include CRUD
151
+ model Song
152
+
153
+ class Contract < Reform::Form
154
+ property :title
155
+ end
156
+
157
+ def process(params)
158
+ validate(params[:song], model, Contract) do |f|
159
+ f.sync
160
+ end
161
+ end
162
+ end
163
+
164
+ # uses private Contract class.
165
+ it { OperationWithPrivateContract.(song: {title: "Blue Rondo a la Turk"}).model.title.must_equal "Blue Rondo a la Turk" }
166
+ end
@@ -0,0 +1,63 @@
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
+ class Create < Trailblazer::Operation
8
+ include Dispatch
9
+
10
+ contract do
11
+ property :name
12
+ end
13
+
14
+ callback do
15
+ on_change :notify_me!
16
+ on_change :notify_you!
17
+ end
18
+
19
+ # TODO: always dispatch, pass params.
20
+
21
+ def process(params)
22
+ @model = Song.new
23
+
24
+ validate(params, @model) do
25
+ dispatch!
26
+ end
27
+ end
28
+
29
+ def dispatched
30
+ @dispatched ||= []
31
+ end
32
+
33
+ private
34
+ def notify_me!(*)
35
+ dispatched << :notify_me!
36
+ end
37
+
38
+ def notify_you!(*)
39
+ dispatched << :notify_you!
40
+ end
41
+ end
42
+
43
+
44
+ class Update < Create
45
+ # TODO: allow skipping groups.
46
+ # skip_dispatch :notify_me!
47
+
48
+ callback do
49
+ remove! :on_change, :notify_me!
50
+ end
51
+ end
52
+
53
+
54
+ it "invokes all callbacks" do
55
+ op = Create.({"name"=>"Keep On Running"})
56
+ op.dispatched.must_equal [:notify_me!, :notify_you!]
57
+ end
58
+
59
+ it "does not invoke removed callbacks" do
60
+ op = Update.({"name"=>"Keep On Running"})
61
+ op.dispatched.must_equal [:notify_you!]
62
+ end
63
+ end
@@ -1,27 +1,45 @@
1
- require 'test_helper'
1
+ require "test_helper"
2
2
 
3
- module Comparable
4
- # only used for test.
5
- def ==(b)
6
- self.class == b.class
3
+ module Inspect
4
+ def inspect
5
+ "<#{self.class.to_s.split("::").last} @model=#{@model}>"
7
6
  end
7
+ alias_method :to_s, :inspect
8
8
  end
9
9
 
10
10
  class OperationSetupParamsTest < MiniTest::Spec
11
11
  class OperationSetupParam < Trailblazer::Operation
12
12
  def process(params)
13
- params
13
+ @model = params
14
14
  end
15
15
 
16
16
  def setup_params!(params)
17
17
  params.merge!(garrett: "Rocks!")
18
18
  end
19
+
20
+ include Inspect
19
21
  end
20
22
 
21
- let (:operation) { OperationSetupParam.new }
22
- it { OperationSetupParam.run({valid: true}).must_equal [true, {valid: true, garrett: "Rocks!"}] }
23
+ # allows you changing params in #setup_params!.
24
+ it { OperationSetupParam.run({valid: true}).to_s.must_equal "[true, <OperationSetupParam @model={:valid=>true, :garrett=>\"Rocks!\"}>]" }
23
25
  end
24
26
 
27
+ # Operation#model.
28
+ class OperationModelTest < MiniTest::Spec
29
+ class Operation < Trailblazer::Operation
30
+ def process(params)
31
+ end
32
+
33
+ def model!(params)
34
+ params
35
+ end
36
+ end
37
+
38
+ # #model.
39
+ it { Operation.(Object).model.must_equal Object }
40
+ end
41
+
42
+
25
43
  class OperationRunTest < MiniTest::Spec
26
44
  class Operation < Trailblazer::Operation
27
45
  # allow providing your own contract.
@@ -29,15 +47,13 @@ class OperationRunTest < MiniTest::Spec
29
47
  def initialize(*)
30
48
  end
31
49
  def validate(params)
32
- return false if params == false # used in ::[] with exception test.
33
- "local #{params}"
50
+ return true if params == "yes, true"
51
+ false
34
52
  end
35
53
 
36
54
  def errors
37
55
  Struct.new(:to_s).new("Op just calls #to_s on Errors!")
38
56
  end
39
-
40
- include Comparable
41
57
  self
42
58
  end
43
59
 
@@ -45,68 +61,66 @@ class OperationRunTest < MiniTest::Spec
45
61
  model = Object
46
62
  validate(params, model)
47
63
  end
48
- end
49
64
 
50
- let (:operation) { Operation.new.extend(Comparable) }
65
+ include Inspect
66
+ end
51
67
 
52
68
  # contract is inferred from self::contract_class.
53
- it { Operation.run(true).must_equal ["local true", operation] }
69
+ # ::run returns result set when run without block.
70
+ it { Operation.run("not true").to_s.must_equal %{[false, <Operation @model=>]} }
71
+ it { Operation.run("yes, true").to_s.must_equal %{[true, <Operation @model=>]} }
54
72
 
55
- # return operation when ::call
56
- it { Operation.call(true).must_equal operation }
57
- it { Operation.(true).must_equal operation }
58
- # #[] is alias for .()
59
- it { Operation[true].must_equal operation }
60
-
61
- # ::[] raises exception when invalid.
73
+ # ::call raises exception when invalid.
62
74
  it do
63
- exception = assert_raises(Trailblazer::Operation::InvalidContract) { Operation[false] }
75
+ exception = assert_raises(Trailblazer::Operation::InvalidContract) { Operation.("not true") }
64
76
  exception.message.must_equal "Op just calls #to_s on Errors!"
65
77
  end
66
78
 
67
- # ::run without block returns result set.
68
- it { Operation.run(true).must_equal ["local true", operation] }
69
- it { Operation.run(false).must_equal [false, operation] }
79
+ # return operation when ::call
80
+ it { Operation.("yes, true").to_s.must_equal %{<Operation @model=>} }
81
+ # #[] is alias for .()
82
+ it { Operation["yes, true"].to_s.must_equal %{<Operation @model=>} }
83
+
70
84
 
71
85
  # ::run with block returns operation.
72
86
  # valid executes block.
73
87
  it "block" do
74
88
  outcome = nil
75
- res = Operation.run(true) do
89
+ res = Operation.run("yes, true") do
76
90
  outcome = "true"
77
91
  end
78
92
 
79
93
  outcome.must_equal "true" # block was executed.
80
- res.must_equal operation
94
+ res.to_s.must_equal %{<Operation @model=>}
81
95
  end
82
96
 
83
97
  # invalid doesn't execute block.
84
98
  it "block, invalid" do
85
99
  outcome = nil
86
- res = Operation.run(false) do
100
+ res = Operation.run("no, not true, false") do
87
101
  outcome = "true"
88
102
  end
89
103
 
90
104
  outcome.must_equal nil # block was _not_ executed.
91
- res.must_equal operation
105
+ res.to_s.must_equal %{<Operation @model=>}
92
106
  end
93
107
 
94
108
  # block yields operation
95
109
  it do
96
110
  outcome = nil
97
- res = Operation.run(true) do |op|
111
+ res = Operation.run("yes, true") do |op|
98
112
  outcome = op
99
113
  end
100
114
 
101
- outcome.must_equal operation # block was executed.
102
- res.must_equal operation
115
+ outcome.to_s.must_equal %{<Operation @model=>} # block was executed.
116
+ res.to_s.must_equal %{<Operation @model=>}
103
117
  end
104
118
 
105
- # Operation#contract returns @contract
106
- let (:contract) { Operation::Contract.new }
107
- it { Operation.(true).contract.must_equal contract }
119
+ # # Operation#contract returns @contract
120
+ it { Operation.("yes, true").contract.class.to_s.must_equal "OperationRunTest::Operation::Contract" }
108
121
  end
109
122
 
123
+
110
124
  class OperationTest < MiniTest::Spec
111
125
  class Operation < Trailblazer::Operation
112
126
  def process(params)
@@ -117,36 +131,20 @@ class OperationTest < MiniTest::Spec
117
131
  # contract is retrieved from ::contract_class.
118
132
  it { assert_raises(NoMethodError) { Operation.run({}) } } # TODO: if you call #validate without defining a contract, the error is quite cryptic.
119
133
 
120
- # no #process method defined.
121
- # DISCUSS: not sure if we need that.
122
- # class OperationWithoutProcessMethod < Trailblazer::Operation
123
- # end
124
-
125
- # it { OperationWithoutProcessMethod[{}].must_be_kind_of OperationWithoutProcessMethod }
126
-
127
- # #process and no validate.
134
+ # test #invalid!
128
135
  class OperationWithoutValidateCall < Trailblazer::Operation
129
136
  def process(params)
130
137
  params || invalid!(params)
131
138
  end
139
+
140
+ include Inspect
132
141
  end
133
142
 
134
143
  # ::run
135
- it { OperationWithoutValidateCall.run(Object).must_equal [true, Object] }
136
- # ::[]
137
- it { OperationWithoutValidateCall.(Object).must_equal(Object) }
138
- # ::run with invalid!
139
- it { OperationWithoutValidateCall.run(nil).must_equal [false, nil] }
140
- # ::run with block, invalid
141
- it do
142
- OperationWithoutValidateCall.run(false) { @outcome = "true" }.must_equal false
143
- @outcome.must_equal nil
144
- end
145
- # ::run with block, valid
146
- it do
147
- OperationWithoutValidateCall.run(true) { @outcome = "true" }.must_equal true
148
- @outcome.must_equal "true"
149
- end
144
+ it { OperationWithoutValidateCall.run(true).to_s.must_equal %{[true, <OperationWithoutValidateCall @model=>]} }
145
+ # invalid.
146
+ it { OperationWithoutValidateCall.run(false).to_s.must_equal %{[false, <OperationWithoutValidateCall @model=>]} }
147
+
150
148
 
151
149
  # #validate yields contract when valid
152
150
  class OperationWithValidateBlock < Trailblazer::Operation
@@ -162,7 +160,7 @@ class OperationTest < MiniTest::Spec
162
160
 
163
161
  def process(params)
164
162
  validate(params, Object.new) do |c|
165
- @secret_contract = c
163
+ @secret_contract = c.class
166
164
  end
167
165
  end
168
166
 
@@ -170,58 +168,15 @@ class OperationTest < MiniTest::Spec
170
168
  end
171
169
 
172
170
  it { OperationWithValidateBlock.run(false).last.secret_contract.must_equal nil }
173
- it('zzz') { OperationWithValidateBlock.(true).secret_contract.must_equal OperationWithValidateBlock.contract_class.new.extend(Comparable) }
171
+ it { OperationWithValidateBlock.(true).secret_contract.must_equal OperationWithValidateBlock::Contract }
174
172
 
175
- # manually setting @valid
176
- class OperationWithManualValid < Trailblazer::Operation
177
- def process(params)
178
- @valid = false
179
- params
180
- end
181
- end
182
-
183
- # ::run
184
- it { OperationWithManualValid.run(Object).must_equal [false, Object] }
185
- # ::[]
186
- it { OperationWithManualValid.(Object).must_equal(Object) }
187
-
188
- # re-assign params
189
- class OperationReassigningParams < Trailblazer::Operation
190
- def process(params)
191
- params = params[:title]
192
- params
193
- end
194
- end
195
173
 
196
- # ::run
197
- it { OperationReassigningParams.run({:title => "Day Like This"}).must_equal [true, "Day Like This"] }
198
-
199
- # #invalid!(result)
200
- class OperationCallingInvalid < Trailblazer::Operation
201
- def process(params)
202
- return 1 if params
203
- invalid!(2)
204
- end
205
- end
206
-
207
- it { OperationCallingInvalid.run(true).must_equal [true, 1] }
208
- it { OperationCallingInvalid.run(nil).must_equal [false, 2] }
209
-
210
- # #invalid! without result defaults to operation instance.
211
- class OperationCallingInvalidWithoutResult < Trailblazer::Operation
212
- include Comparable
213
- def process(params)
214
- invalid!
215
- end
216
- end
217
-
218
- it { OperationCallingInvalidWithoutResult.run(true).must_equal [false, OperationCallingInvalidWithoutResult.new] }
219
-
220
- # calling return from #validate block leaves result true.
221
- class OperationUsingReturnInValidate < Trailblazer::Operation
174
+ # test validate wit if/else
175
+ class OperationWithValidateAndIf < Trailblazer::Operation
222
176
  self.contract_class = class Contract
223
177
  def initialize(*)
224
178
  end
179
+
225
180
  def validate(params)
226
181
  params
227
182
  end
@@ -229,24 +184,31 @@ class OperationTest < MiniTest::Spec
229
184
  end
230
185
 
231
186
  def process(params)
232
- validate(params, Object) do
233
- return 1
187
+ if validate(params, Object.new)
188
+ @secret_contract = contract.class
189
+ else
190
+ @secret_contract = "so wrong!"
234
191
  end
235
- 2
236
192
  end
193
+
194
+ attr_reader :secret_contract
237
195
  end
238
196
 
239
- it { OperationUsingReturnInValidate.run(true).must_equal [true, 1] }
240
- it { OperationUsingReturnInValidate.run(false).must_equal [false, 2] }
197
+ it { OperationWithValidateAndIf.run(false).last.secret_contract.must_equal "so wrong!" }
198
+ it { OperationWithValidateAndIf.(true).secret_contract.must_equal OperationWithValidateAndIf::Contract }
199
+
200
+
201
+
241
202
 
242
203
  # unlimited arguments for ::run and friends.
243
204
  class OperationReceivingLottaArguments < Trailblazer::Operation
244
205
  def process(model, params)
245
- [model, params]
206
+ @model = [model, params]
246
207
  end
208
+ include Inspect
247
209
  end
248
210
 
249
- it { OperationReceivingLottaArguments.run(Object, {}).must_equal([true, [Object, {}]]) }
211
+ it { OperationReceivingLottaArguments.run(Object, {}).to_s.must_equal %{[true, <OperationReceivingLottaArguments @model=[Object, {}]>]} }
250
212
 
251
213
  # ::present only runs #setup! which runs #model!.
252
214
  class ContractOnlyOperation < Trailblazer::Operation
@@ -270,16 +232,13 @@ class OperationTest < MiniTest::Spec
270
232
  it { ContractOnlyOperation.present({}).contract._model.must_equal Object }
271
233
  end
272
234
 
235
+
273
236
  class OperationBuilderTest < MiniTest::Spec
274
- class Operation < Trailblazer::Operation
237
+ class ParentOperation < Trailblazer::Operation
275
238
  def process(params)
276
- "operation"
277
239
  end
278
240
 
279
241
  class Sub < self
280
- def process(params)
281
- "sub:operation"
282
- end
283
242
  end
284
243
 
285
244
  builds do |params|
@@ -287,13 +246,13 @@ class OperationBuilderTest < MiniTest::Spec
287
246
  end
288
247
  end
289
248
 
290
- it { Operation.run({}).last.must_equal "operation" }
291
- it { Operation.run({sub: true}).last.must_equal "sub:operation" }
292
-
293
- it { Operation.({}).must_equal "operation" }
294
- it { Operation.({sub: true}).must_equal "sub:operation" }
249
+ it { ParentOperation.run({}).last.class.must_equal ParentOperation }
250
+ it { ParentOperation.run({sub: true}).last.class.must_equal ParentOperation::Sub }
251
+ it { ParentOperation.({}).class.must_equal ParentOperation }
252
+ it { ParentOperation.({sub: true}).class.must_equal ParentOperation::Sub }
295
253
  end
296
254
 
255
+
297
256
  # ::contract builds Reform::Form class
298
257
  class OperationInheritanceTest < MiniTest::Spec
299
258
  class Operation < Trailblazer::Operation
@@ -357,4 +316,4 @@ class OperationErrorsTest < MiniTest::Spec
357
316
  res, op = Operation.run({})
358
317
  op.errors.to_s.must_equal "{:title=>[\"can't be blank\"]}"
359
318
  end
360
- end
319
+ end