trailblazer 0.2.2 → 0.3.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 (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