trailblazer 1.1.2 → 2.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +10 -7
- data/CHANGES.md +108 -0
- data/COMM-LICENSE +91 -0
- data/Gemfile +18 -4
- data/LICENSE.txt +7 -20
- data/README.md +55 -15
- data/Rakefile +21 -2
- data/draft-1.2.rb +7 -0
- data/lib/trailblazer.rb +17 -4
- data/lib/trailblazer/dsl.rb +47 -0
- data/lib/trailblazer/operation/auto_inject.rb +47 -0
- data/lib/trailblazer/operation/builder.rb +18 -18
- data/lib/trailblazer/operation/callback.rb +31 -38
- data/lib/trailblazer/operation/contract.rb +46 -0
- data/lib/trailblazer/operation/controller.rb +45 -27
- data/lib/trailblazer/operation/guard.rb +24 -0
- data/lib/trailblazer/operation/model.rb +41 -33
- data/lib/trailblazer/operation/nested.rb +43 -0
- data/lib/trailblazer/operation/params.rb +13 -0
- data/lib/trailblazer/operation/persist.rb +13 -0
- data/lib/trailblazer/operation/policy.rb +26 -72
- data/lib/trailblazer/operation/present.rb +19 -0
- data/lib/trailblazer/operation/procedural/contract.rb +15 -0
- data/lib/trailblazer/operation/procedural/validate.rb +22 -0
- data/lib/trailblazer/operation/pundit.rb +42 -0
- data/lib/trailblazer/operation/representer.rb +25 -92
- data/lib/trailblazer/operation/rescue.rb +23 -0
- data/lib/trailblazer/operation/resolver.rb +18 -24
- data/lib/trailblazer/operation/validate.rb +50 -0
- data/lib/trailblazer/operation/wrap.rb +37 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/{operation/controller_test.rb → controller_test.rb} +8 -4
- data/test/docs/auto_inject_test.rb +30 -0
- data/test/docs/contract_test.rb +429 -0
- data/test/docs/dry_test.rb +31 -0
- data/test/docs/guard_test.rb +143 -0
- data/test/docs/nested_test.rb +117 -0
- data/test/docs/policy_test.rb +2 -0
- data/test/docs/pundit_test.rb +109 -0
- data/test/docs/representer_test.rb +268 -0
- data/test/docs/rescue_test.rb +153 -0
- data/test/docs/wrap_test.rb +174 -0
- data/test/gemfiles/Gemfile.ruby-1.9 +3 -0
- data/test/gemfiles/Gemfile.ruby-2.0 +12 -0
- data/test/gemfiles/Gemfile.ruby-2.3 +12 -0
- data/test/module_test.rb +22 -15
- data/test/operation/builder_test.rb +66 -18
- data/test/operation/callback_test.rb +70 -0
- data/test/operation/contract_test.rb +385 -15
- data/test/operation/dsl/callback_test.rb +18 -30
- data/test/operation/dsl/contract_test.rb +209 -19
- data/test/operation/dsl/representer_test.rb +42 -15
- data/test/operation/guard_test.rb +1 -147
- data/test/operation/model_test.rb +105 -0
- data/test/operation/params_test.rb +36 -0
- data/test/operation/persist_test.rb +44 -0
- data/test/operation/pipedream_test.rb +59 -0
- data/test/operation/pipetree_test.rb +104 -0
- data/test/operation/present_test.rb +24 -0
- data/test/operation/pundit_test.rb +104 -0
- data/test/{representer_test.rb → operation/representer_test.rb} +58 -42
- data/test/operation/resolver_test.rb +34 -70
- data/test/operation_test.rb +57 -189
- data/test/test_helper.rb +23 -3
- data/trailblazer.gemspec +8 -7
- metadata +91 -59
- data/gemfiles/Gemfile.rails.lock +0 -130
- data/gemfiles/Gemfile.reform-2.0 +0 -6
- data/gemfiles/Gemfile.reform-2.1 +0 -7
- data/lib/trailblazer/autoloading.rb +0 -15
- data/lib/trailblazer/endpoint.rb +0 -31
- data/lib/trailblazer/operation.rb +0 -175
- data/lib/trailblazer/operation/collection.rb +0 -6
- data/lib/trailblazer/operation/dispatch.rb +0 -3
- data/lib/trailblazer/operation/model/dsl.rb +0 -29
- data/lib/trailblazer/operation/model/external.rb +0 -34
- data/lib/trailblazer/operation/policy/guard.rb +0 -35
- data/lib/trailblazer/operation/uploaded_file.rb +0 -77
- data/test/callback_test.rb +0 -104
- data/test/collection_test.rb +0 -57
- data/test/model_test.rb +0 -148
- data/test/operation/external_model_test.rb +0 -71
- data/test/operation/policy_test.rb +0 -97
- data/test/operation/reject_test.rb +0 -34
- data/test/rollback_test.rb +0 -47
data/gemfiles/Gemfile.rails.lock
DELETED
@@ -1,130 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ../
|
3
|
-
specs:
|
4
|
-
trailblazer (0.2.2)
|
5
|
-
actionpack (>= 3.0.0)
|
6
|
-
reform (>= 1.2.0)
|
7
|
-
uber (>= 0.0.10)
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: http://rubygems.org/
|
11
|
-
specs:
|
12
|
-
actionmailer (4.1.1)
|
13
|
-
actionpack (= 4.1.1)
|
14
|
-
actionview (= 4.1.1)
|
15
|
-
mail (~> 2.5.4)
|
16
|
-
actionpack (4.1.1)
|
17
|
-
actionview (= 4.1.1)
|
18
|
-
activesupport (= 4.1.1)
|
19
|
-
rack (~> 1.5.2)
|
20
|
-
rack-test (~> 0.6.2)
|
21
|
-
actionview (4.1.1)
|
22
|
-
activesupport (= 4.1.1)
|
23
|
-
builder (~> 3.1)
|
24
|
-
erubis (~> 2.7.0)
|
25
|
-
activemodel (4.1.1)
|
26
|
-
activesupport (= 4.1.1)
|
27
|
-
builder (~> 3.1)
|
28
|
-
activerecord (4.1.1)
|
29
|
-
activemodel (= 4.1.1)
|
30
|
-
activesupport (= 4.1.1)
|
31
|
-
arel (~> 5.0.0)
|
32
|
-
activesupport (4.1.1)
|
33
|
-
i18n (~> 0.6, >= 0.6.9)
|
34
|
-
json (~> 1.7, >= 1.7.7)
|
35
|
-
minitest (~> 5.1)
|
36
|
-
thread_safe (~> 0.1)
|
37
|
-
tzinfo (~> 1.1)
|
38
|
-
arel (5.0.1.20140414130214)
|
39
|
-
builder (3.2.2)
|
40
|
-
celluloid (0.16.0)
|
41
|
-
timers (~> 4.0.0)
|
42
|
-
connection_pool (2.0.0)
|
43
|
-
disposable (0.0.9)
|
44
|
-
representable (~> 2.0)
|
45
|
-
uber
|
46
|
-
erubis (2.7.0)
|
47
|
-
hitimes (1.2.2)
|
48
|
-
i18n (0.6.11)
|
49
|
-
json (1.8.1)
|
50
|
-
mail (2.5.4)
|
51
|
-
mime-types (~> 1.16)
|
52
|
-
treetop (~> 1.4.8)
|
53
|
-
mime-types (1.25.1)
|
54
|
-
mini_portile (0.6.2)
|
55
|
-
minitest (5.4.2)
|
56
|
-
multi_json (1.11.0)
|
57
|
-
nokogiri (1.6.6.2)
|
58
|
-
mini_portile (~> 0.6.0)
|
59
|
-
polyglot (0.3.5)
|
60
|
-
rack (1.5.2)
|
61
|
-
rack-test (0.6.2)
|
62
|
-
rack (>= 1.0)
|
63
|
-
rails (4.1.1)
|
64
|
-
actionmailer (= 4.1.1)
|
65
|
-
actionpack (= 4.1.1)
|
66
|
-
actionview (= 4.1.1)
|
67
|
-
activemodel (= 4.1.1)
|
68
|
-
activerecord (= 4.1.1)
|
69
|
-
activesupport (= 4.1.1)
|
70
|
-
bundler (>= 1.3.0, < 2.0)
|
71
|
-
railties (= 4.1.1)
|
72
|
-
sprockets-rails (~> 2.0)
|
73
|
-
railties (4.1.1)
|
74
|
-
actionpack (= 4.1.1)
|
75
|
-
activesupport (= 4.1.1)
|
76
|
-
rake (>= 0.8.7)
|
77
|
-
thor (>= 0.18.1, < 2.0)
|
78
|
-
rake (10.3.2)
|
79
|
-
redis (3.1.0)
|
80
|
-
redis-namespace (1.5.1)
|
81
|
-
redis (~> 3.0, >= 3.0.4)
|
82
|
-
reform (1.2.6)
|
83
|
-
activemodel
|
84
|
-
disposable (~> 0.0.5)
|
85
|
-
representable (~> 2.1.0)
|
86
|
-
uber (~> 0.0.11)
|
87
|
-
representable (2.1.8)
|
88
|
-
multi_json
|
89
|
-
nokogiri
|
90
|
-
uber (~> 0.0.7)
|
91
|
-
responders (1.1.2)
|
92
|
-
railties (>= 3.2, < 4.2)
|
93
|
-
sidekiq (3.1.4)
|
94
|
-
celluloid (>= 0.15.2)
|
95
|
-
connection_pool (>= 2.0.0)
|
96
|
-
json
|
97
|
-
redis (>= 3.0.6)
|
98
|
-
redis-namespace (>= 1.3.1)
|
99
|
-
sprockets (3.1.0)
|
100
|
-
rack (~> 1.0)
|
101
|
-
sprockets-rails (2.3.1)
|
102
|
-
actionpack (>= 3.0)
|
103
|
-
activesupport (>= 3.0)
|
104
|
-
sprockets (>= 2.8, < 4.0)
|
105
|
-
sqlite3 (1.3.9)
|
106
|
-
thor (0.19.1)
|
107
|
-
thread_safe (0.3.4)
|
108
|
-
timers (4.0.1)
|
109
|
-
hitimes
|
110
|
-
treetop (1.4.15)
|
111
|
-
polyglot
|
112
|
-
polyglot (>= 0.3.1)
|
113
|
-
tzinfo (1.2.2)
|
114
|
-
thread_safe (~> 0.1)
|
115
|
-
uber (0.0.13)
|
116
|
-
|
117
|
-
PLATFORMS
|
118
|
-
ruby
|
119
|
-
|
120
|
-
DEPENDENCIES
|
121
|
-
activerecord
|
122
|
-
bundler
|
123
|
-
minitest
|
124
|
-
rails
|
125
|
-
railties
|
126
|
-
rake
|
127
|
-
responders
|
128
|
-
sidekiq (~> 3.1.0)
|
129
|
-
sqlite3
|
130
|
-
trailblazer!
|
data/gemfiles/Gemfile.reform-2.0
DELETED
data/gemfiles/Gemfile.reform-2.1
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
Trailblazer.class_eval do
|
2
|
-
autoload :NotAuthorizedError, "trailblazer/operation/policy"
|
3
|
-
end
|
4
|
-
|
5
|
-
Trailblazer::Operation.class_eval do
|
6
|
-
autoload :Controller, "trailblazer/operation/controller"
|
7
|
-
autoload :Model, "trailblazer/operation/model"
|
8
|
-
autoload :Collection, "trailblazer/operation/collection"
|
9
|
-
autoload :Dispatch, "trailblazer/operation/dispatch" # TODO: remove in 1.2.
|
10
|
-
autoload :Callback, "trailblazer/operation/callback"
|
11
|
-
autoload :Module, "trailblazer/operation/module"
|
12
|
-
autoload :Representer,"trailblazer/operation/representer"
|
13
|
-
autoload :Policy, "trailblazer/operation/policy"
|
14
|
-
autoload :Resolver, "trailblazer/operation/resolver"
|
15
|
-
end
|
data/lib/trailblazer/endpoint.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
module Trailblazer
|
2
|
-
# Encapsulates HTTP-specific logic needed before running an operation.
|
3
|
-
# Right now, all this does is #document_body! which figures out whether or not to pass the request body
|
4
|
-
# into params, so the operation can use a representer to deserialize the original document.
|
5
|
-
# To be used in Hanami, Roda, Rails, etc.
|
6
|
-
class Endpoint
|
7
|
-
def initialize(operation_class, params, request, options)
|
8
|
-
@operation_class = operation_class
|
9
|
-
@params = params
|
10
|
-
@request = request
|
11
|
-
@is_document = options[:is_document]
|
12
|
-
end
|
13
|
-
|
14
|
-
def call
|
15
|
-
document_body! if @is_document
|
16
|
-
yield @params# Create.run(params)
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
attr_reader :params, :operation_class, :request
|
21
|
-
|
22
|
-
def document_body!
|
23
|
-
# this is what happens:
|
24
|
-
# respond_with Comment::Update::JSON.run(params.merge(comment: request.body.string))
|
25
|
-
concept_name = operation_class.model_class.to_s.underscore # this could be renamed to ::concept_class soon.
|
26
|
-
request_body = request.body.respond_to?(:string) ? request.body.string : request.body.read
|
27
|
-
|
28
|
-
params.merge!(concept_name => request_body)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,175 +0,0 @@
|
|
1
|
-
require "reform"
|
2
|
-
|
3
|
-
module Trailblazer
|
4
|
-
class Operation
|
5
|
-
require "trailblazer/operation/builder"
|
6
|
-
extend Builder # imports ::builder_class and ::build_operation.
|
7
|
-
|
8
|
-
extend Uber::InheritableAttr
|
9
|
-
inheritable_attr :contract_class
|
10
|
-
self.contract_class = Reform::Form.clone
|
11
|
-
|
12
|
-
class << self
|
13
|
-
def run(params, &block) # Endpoint behaviour
|
14
|
-
res, op = build_operation(params).run
|
15
|
-
|
16
|
-
if block_given?
|
17
|
-
yield op if res
|
18
|
-
return op
|
19
|
-
end
|
20
|
-
|
21
|
-
[res, op]
|
22
|
-
end
|
23
|
-
|
24
|
-
# Like ::run, but yield block when invalid.
|
25
|
-
def reject(*args)
|
26
|
-
res, op = run(*args)
|
27
|
-
yield op if res == false
|
28
|
-
op
|
29
|
-
end
|
30
|
-
|
31
|
-
# ::call only returns the Operation instance (or whatever was returned from #validate).
|
32
|
-
# This is useful in tests or in irb, e.g. when using Op as a factory and you already know it's valid.
|
33
|
-
def call(params)
|
34
|
-
build_operation(params, raise_on_invalid: true).run.last
|
35
|
-
end
|
36
|
-
|
37
|
-
def [](*args) # TODO: remove in 1.2.
|
38
|
-
warn "[Trailblazer] Operation[] is deprecated. Please use Operation.() and have a nice day."
|
39
|
-
call(*args)
|
40
|
-
end
|
41
|
-
|
42
|
-
# Runs #setup! but doesn't process the operation.
|
43
|
-
def present(params)
|
44
|
-
build_operation(params)
|
45
|
-
end
|
46
|
-
|
47
|
-
# This is a DSL method. Use ::contract_class and ::contract_class= for the explicit version.
|
48
|
-
# Op.contract #=> returns contract class
|
49
|
-
# Op.contract do .. end # defines contract
|
50
|
-
# Op.contract CommentForm # copies (and subclasses) external contract.
|
51
|
-
# Op.contract CommentForm do .. end # copies and extends contract.
|
52
|
-
def contract(constant=nil, &block)
|
53
|
-
return contract_class unless constant or block_given?
|
54
|
-
|
55
|
-
self.contract_class= Class.new(constant) if constant
|
56
|
-
contract_class.class_eval(&block) if block_given?
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
|
61
|
-
def initialize(params, options={})
|
62
|
-
@options = options
|
63
|
-
@valid = true
|
64
|
-
|
65
|
-
setup!(params) # assign/find the model
|
66
|
-
end
|
67
|
-
|
68
|
-
# Operation.run(body: "Fabulous!") #=> [true, <Comment body: "Fabulous!">]
|
69
|
-
def run
|
70
|
-
process(@params)
|
71
|
-
|
72
|
-
[valid?, self]
|
73
|
-
end
|
74
|
-
|
75
|
-
attr_reader :model
|
76
|
-
|
77
|
-
def errors
|
78
|
-
contract.errors
|
79
|
-
end
|
80
|
-
|
81
|
-
def valid?
|
82
|
-
@valid
|
83
|
-
end
|
84
|
-
|
85
|
-
def contract(*args)
|
86
|
-
contract!(*args)
|
87
|
-
end
|
88
|
-
|
89
|
-
private
|
90
|
-
module Setup
|
91
|
-
attr_writer :model
|
92
|
-
|
93
|
-
def setup!(params)
|
94
|
-
params = assign_params!(params)
|
95
|
-
setup_params!(params)
|
96
|
-
build_model!(params)
|
97
|
-
params # TODO: test me.
|
98
|
-
end
|
99
|
-
|
100
|
-
def assign_params!(params)
|
101
|
-
@params = params!(params)
|
102
|
-
end
|
103
|
-
|
104
|
-
# Overwrite #params! if you need to change its structure, by returning a new params object
|
105
|
-
# from this method.
|
106
|
-
# This is helpful if you don't want to change the original via #setup_params!.
|
107
|
-
def params!(params)
|
108
|
-
params
|
109
|
-
end
|
110
|
-
|
111
|
-
def setup_params!(params)
|
112
|
-
end
|
113
|
-
|
114
|
-
def build_model!(*args)
|
115
|
-
assign_model!(*args) # @model = ..
|
116
|
-
setup_model!(*args)
|
117
|
-
end
|
118
|
-
|
119
|
-
def assign_model!(*args)
|
120
|
-
@model = model!(*args)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Implement #model! to find/create your operation model (if required).
|
124
|
-
def model!(params)
|
125
|
-
end
|
126
|
-
|
127
|
-
# Override to add attributes that can be infered from params.
|
128
|
-
def setup_model!(params)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
include Setup
|
132
|
-
|
133
|
-
def validate(params, model=nil, contract_class=nil)
|
134
|
-
contract!(model, contract_class)
|
135
|
-
|
136
|
-
if @valid = validate_contract(params)
|
137
|
-
yield contract if block_given?
|
138
|
-
else
|
139
|
-
raise!(contract)
|
140
|
-
end
|
141
|
-
|
142
|
-
@valid
|
143
|
-
end
|
144
|
-
|
145
|
-
def validate_contract(params)
|
146
|
-
contract.validate(params)
|
147
|
-
end
|
148
|
-
|
149
|
-
def invalid!(result=self)
|
150
|
-
@valid = false
|
151
|
-
result
|
152
|
-
end
|
153
|
-
|
154
|
-
# When using Op.(), an invalid contract will raise an exception.
|
155
|
-
def raise!(contract)
|
156
|
-
raise InvalidContract.new(contract.errors.to_s) if @options[:raise_on_invalid]
|
157
|
-
end
|
158
|
-
|
159
|
-
# Instantiate the contract, either by using the user's contract passed into #validate
|
160
|
-
# or infer the Operation contract.
|
161
|
-
def contract_for(model=nil, contract_class=nil)
|
162
|
-
model ||= self.model
|
163
|
-
contract_class ||= self.class.contract_class
|
164
|
-
|
165
|
-
contract_class.new(model)
|
166
|
-
end
|
167
|
-
|
168
|
-
def contract!(*args)
|
169
|
-
@contract ||= contract_for(*args)
|
170
|
-
end
|
171
|
-
|
172
|
-
class InvalidContract < RuntimeError
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
class Trailblazer::Operation
|
2
|
-
module Model
|
3
|
-
# Imports ::model and ::action into an operation.
|
4
|
-
module DSL
|
5
|
-
def self.extended(extender)
|
6
|
-
extender.extend Uber::InheritableAttr
|
7
|
-
extender.inheritable_attr :config
|
8
|
-
extender.config = {}
|
9
|
-
end
|
10
|
-
|
11
|
-
def model(name, action=nil)
|
12
|
-
self.config[:model] = name
|
13
|
-
action(action) if action # coolest line ever.
|
14
|
-
end
|
15
|
-
|
16
|
-
def action(name)
|
17
|
-
self.config[:action] = name
|
18
|
-
end
|
19
|
-
|
20
|
-
def action_name # considered private.
|
21
|
-
self.config[:action] or :create
|
22
|
-
end
|
23
|
-
|
24
|
-
def model_class # considered private.
|
25
|
-
self.config[:model] or raise "[Trailblazer] You didn't call Operation::model."
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require "trailblazer/operation/model"
|
2
|
-
|
3
|
-
class Trailblazer::Operation
|
4
|
-
module Model
|
5
|
-
# Builds (finds or creates) the model _before_ the operation is instantiated.
|
6
|
-
# Passes the model instance into the builder with the following signature.
|
7
|
-
#
|
8
|
-
# builds ->(model, params)
|
9
|
-
#
|
10
|
-
# The initializer will now expect you to pass the model in via options[:model]. This
|
11
|
-
# happens automatically when coming from a builder.
|
12
|
-
module External
|
13
|
-
def self.included(includer)
|
14
|
-
includer.extend Model::DSL
|
15
|
-
includer.extend Model::BuildModel
|
16
|
-
includer.extend ClassMethods
|
17
|
-
end
|
18
|
-
|
19
|
-
def assign_model!(*) # i don't like to "disable" the `@model =` like this but it's the simplest for now.
|
20
|
-
@model = @options[:model]
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
module ClassMethods
|
25
|
-
private
|
26
|
-
def build_operation(params, options={}) # TODO: merge with Resolver::build_operation.
|
27
|
-
model = model!(params)
|
28
|
-
build_operation_class(model, params). # calls builds->(model, params).
|
29
|
-
new(params, options.merge(model: model))
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|