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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -3
- data/CHANGES.md +33 -0
- data/Gemfile +4 -1
- data/README.md +171 -166
- data/Rakefile +10 -2
- data/gemfiles/Gemfile.rails.lock +42 -11
- data/lib/trailblazer/autoloading.rb +4 -3
- data/lib/trailblazer/endpoint.rb +40 -0
- data/lib/trailblazer/operation.rb +15 -8
- data/lib/trailblazer/operation/collection.rb +7 -0
- data/lib/trailblazer/operation/controller.rb +30 -22
- data/lib/trailblazer/operation/controller/active_record.rb +6 -2
- data/lib/trailblazer/operation/crud.rb +3 -5
- data/lib/trailblazer/operation/dispatch.rb +29 -0
- data/lib/trailblazer/operation/representer.rb +41 -5
- data/lib/trailblazer/operation/responder.rb +2 -2
- data/lib/trailblazer/operation/uploaded_file.rb +4 -4
- data/lib/trailblazer/operation/worker.rb +8 -10
- data/lib/trailblazer/rails/railtie.rb +10 -7
- data/lib/trailblazer/version.rb +1 -1
- data/test/collection_test.rb +56 -0
- data/test/crud_test.rb +23 -1
- data/test/dispatch_test.rb +63 -0
- data/test/operation_test.rb +84 -125
- data/test/rails/controller_test.rb +51 -0
- data/test/rails/endpoint_test.rb +86 -0
- data/test/rails/fake_app/cells.rb +2 -2
- data/test/rails/fake_app/config.rb +1 -1
- data/test/rails/fake_app/controllers.rb +27 -0
- data/test/rails/fake_app/models.rb +10 -1
- data/test/rails/fake_app/rails_app.rb +15 -1
- data/test/rails/fake_app/song/operations.rb +38 -2
- data/test/rails/fake_app/views/bands/index.html.erb +1 -0
- data/test/rails/fake_app/views/songs/another_view.html.erb +2 -0
- data/test/representer_test.rb +126 -0
- data/test/responder_test.rb +2 -4
- data/test/rollback_test.rb +47 -0
- data/test/test_helper.rb +43 -1
- data/test/uploaded_file_test.rb +4 -4
- data/test/worker_test.rb +13 -9
- data/trailblazer.gemspec +7 -3
- metadata +68 -29
@@ -18,9 +18,9 @@ module Trailblazer
|
|
18
18
|
path = persist!
|
19
19
|
|
20
20
|
hash = {
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
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(:
|
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
|
-
:
|
69
|
-
:
|
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!(:
|
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
|
-
:
|
78
|
-
:
|
79
|
-
:
|
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.
|
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
|
-
|
18
|
-
Trailblazer::Railtie.
|
19
|
-
|
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
|
data/lib/trailblazer/version.rb
CHANGED
@@ -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
|
-
|
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
|
data/test/operation_test.rb
CHANGED
@@ -1,27 +1,45 @@
|
|
1
|
-
require
|
1
|
+
require "test_helper"
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
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
|
-
|
22
|
-
it { OperationSetupParam.run({valid: true}).must_equal [true, {valid
|
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
|
33
|
-
|
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
|
-
|
65
|
+
include Inspect
|
66
|
+
end
|
51
67
|
|
52
68
|
# contract is inferred from self::contract_class.
|
53
|
-
|
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
|
-
#
|
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
|
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
|
-
#
|
68
|
-
it { Operation.
|
69
|
-
|
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
|
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
|
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
|
102
|
-
res.must_equal
|
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
|
-
|
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
|
-
#
|
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(
|
136
|
-
#
|
137
|
-
it { OperationWithoutValidateCall.(
|
138
|
-
|
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
|
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
|
-
#
|
197
|
-
|
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)
|
233
|
-
|
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 {
|
240
|
-
it {
|
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
|
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
|
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 {
|
291
|
-
it {
|
292
|
-
|
293
|
-
it {
|
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
|