trailblazer 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +4 -0
- data/Gemfile +2 -2
- data/README.md +104 -31
- data/lib/trailblazer/autoloading.rb +1 -0
- data/lib/trailblazer/operation.rb +0 -1
- data/lib/trailblazer/version.rb +1 -1
- data/test/dispatch_test.rb +2 -1
- data/trailblazer.gemspec +2 -1
- metadata +16 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06e8559ba04b20c80f460c00d2fe76e94a7dceae
|
4
|
+
data.tar.gz: d16ed4ac3f269404152c316e2abcad2053c9f29c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2818fb4a0c7cfdc66b700df7feed6ac8a60dca747922ac901c311c92dffaf44dd0b3881ddfb46bf3eceebbc122d558bbd5c1acfc463a4ff2246824f519e475ee
|
7
|
+
data.tar.gz: 39daeb303ccadc8dd261cfa44576bf046d94174568abecceed193de66c902cd3fc1a5ce27d99814644f26585b800d7f7568b094f5c5ff044eb8fbbaf29ea1fa6
|
data/CHANGES.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -141,11 +141,58 @@ class Create < Trailblazer::Operation
|
|
141
141
|
self.contract_class = MyCommentForm
|
142
142
|
```
|
143
143
|
|
144
|
+
## Callbacks
|
145
|
+
|
146
|
+
Post-processing logic (also known as _callbacks_) is configured in operations.
|
147
|
+
|
148
|
+
Following the schema of your contract, you can define callbacks for events tracked by the form's twin.
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
class Create < Trailblazer::Operation
|
152
|
+
callback(:after_save) do
|
153
|
+
on_change :upload_file, property: :file
|
154
|
+
|
155
|
+
property :user do
|
156
|
+
on_create :notify_user!
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
The _Imperative Callback_ pattern then allows you to call this _callback group_ whereever you need it.
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
class Create < Trailblazer::Operation
|
165
|
+
|
166
|
+
def process(params)
|
167
|
+
validate(params) do
|
168
|
+
contract.save
|
169
|
+
dispatch!(:after_save)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
```
|
174
|
+
|
175
|
+
No magical triggering of unwanted logic anymore, but explicit invocations where you want it.
|
176
|
+
|
177
|
+
|
144
178
|
## Models
|
145
179
|
|
146
180
|
Models for persistence can be implemented using any ORM you fancy, for instance [ActiveRecord](https://github.com/rails/rails/tree/master/activerecord#active-record--object-relational-mapping-in-rails) or [Datamapper](http://datamapper.org/).
|
147
181
|
|
148
|
-
In Trailblazer, models are completely empty
|
182
|
+
In Trailblazer, models are completely empty. They solely contain associations and finders. No business logic is allowed in models.
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class Thing < ActiveRecord::Base
|
186
|
+
has_many :comments, -> { order(created_at: :desc) }
|
187
|
+
has_many :users, through: :authorships
|
188
|
+
has_many :authorships
|
189
|
+
|
190
|
+
scope :latest, lambda { all.limit(9).order("id DESC") }
|
191
|
+
end
|
192
|
+
```
|
193
|
+
|
194
|
+
Only operations and views/cells can access models directly.
|
195
|
+
|
149
196
|
|
150
197
|
## Views
|
151
198
|
|
@@ -153,9 +200,31 @@ View rendering can happen using the controller as known from Rails. This is abso
|
|
153
200
|
|
154
201
|
More complex UI logic happens in _View Models_ as found in [Cells](https://github.com/apotonick/cells). View models also replace helpers.
|
155
202
|
|
156
|
-
8. **HTTP API** Consuming and rendering API documents (e.g. JSON or XML) is done via [roar](https://github.com/apotonick/roar) and [representable](https://github.com/apotonick/representable). They usually inherit the schema from <em>Contract</em>s .
|
157
203
|
|
158
|
-
|
204
|
+
## Representers
|
205
|
+
|
206
|
+
Operations can use representers from [Roar](https://github.com/apotonick/roar) to serialize and parse JSON and XML documents for APIs.
|
207
|
+
|
208
|
+
Representers can be inferred automatically from your contract, then may be refined, e.g. with hypermedia or a format like `JSON-API`.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
class Create < Trailblazer::Operation
|
212
|
+
|
213
|
+
representer do
|
214
|
+
# inherited :body
|
215
|
+
include Roar::JSON::HAL
|
216
|
+
|
217
|
+
link(:self) { comment_path(represented.id) }
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
The operation can then parse incoming JSON documents in `validate` and render a document via `to_json`. The full [documentation is here](http://trailblazerb.org/gems/operation/representer.html) or in the [Trailblazer book](https://leanpub.com/trailblazer), chapter _Hypermedia APIs_.
|
222
|
+
|
223
|
+
## Tests
|
224
|
+
|
225
|
+
Subject to tests are mainly _Operation_s and _View Model_s, as they encapsulate endpoint behaviour of your app. As a nice side effect, factories are replaced by simple _Operation_ calls.
|
226
|
+
|
227
|
+
|
159
228
|
|
160
229
|
## Overview
|
161
230
|
|
@@ -165,6 +234,8 @@ Using the different layers is completely optional and up to you: Both Cells and
|
|
165
234
|
|
166
235
|
## Controller API
|
167
236
|
|
237
|
+
[Learn more](http://trailblazerb.org/gems/operation/controller.html)
|
238
|
+
|
168
239
|
Trailblazer provides four methods to present and invoke operations. But before that, you need to include the `Controller` module.
|
169
240
|
|
170
241
|
```ruby
|
@@ -294,24 +365,6 @@ In all three cases the following instance variables are assigned: `@operation`,
|
|
294
365
|
Named instance variables can be included, too. This is documented [here](#named-controller-instance-variables).
|
295
366
|
|
296
367
|
|
297
|
-
### Normalizing params
|
298
|
-
|
299
|
-
Override `#process_params!` to add or remove values to `params` before the operation is run. This is called in `#run`, `#respond` and `#present`.
|
300
|
-
|
301
|
-
```ruby
|
302
|
-
class CommentsController < ApplicationController
|
303
|
-
# ..
|
304
|
-
|
305
|
-
private
|
306
|
-
def process_params!(params)
|
307
|
-
params.merge!(current_user: current_user)
|
308
|
-
end
|
309
|
-
end
|
310
|
-
```
|
311
|
-
|
312
|
-
This centralizes params normalization and doesn't require you to do that in every action manually.
|
313
|
-
|
314
|
-
|
315
368
|
### Different Request Formats
|
316
369
|
|
317
370
|
In case you have document-API operations that use representers to deserialize the incoming JSON or XML: You can configure the controller to pass the original request body into the operation via `params["comment"]` - instead of the pre-parsed hash from Rails.
|
@@ -344,22 +397,22 @@ end
|
|
344
397
|
The simplest way of running an operation is the _call style_.
|
345
398
|
|
346
399
|
```ruby
|
347
|
-
op = Comment::Create
|
400
|
+
op = Comment::Create.(params)
|
348
401
|
```
|
349
402
|
|
350
|
-
|
403
|
+
The call style runs the operation and return the operation instance, only.
|
351
404
|
|
352
|
-
|
405
|
+
In case of an invalid operation, this will raise an exception.
|
353
406
|
|
354
|
-
|
355
|
-
let(:comment) { Comment::Create[valid_comment_params].model }
|
356
|
-
```
|
407
|
+
### Run style
|
357
408
|
|
358
|
-
|
409
|
+
The _run style_ will do the same as call, but won't raise an exception in case of an invalid result. Instead, it returns result _and_ the operation instance.
|
359
410
|
|
360
|
-
|
411
|
+
```ruby
|
412
|
+
result, op = Comment::Create.run(params)
|
413
|
+
```
|
361
414
|
|
362
|
-
|
415
|
+
Additionally, it accepts a block that's only run for a valid state.
|
363
416
|
|
364
417
|
```ruby
|
365
418
|
Comment::Create.run(params) do |op|
|
@@ -367,7 +420,27 @@ Comment::Create.run(params) do |op|
|
|
367
420
|
end
|
368
421
|
```
|
369
422
|
|
370
|
-
|
423
|
+
## Form API
|
424
|
+
|
425
|
+
Usually, an operation has a form object.
|
426
|
+
|
427
|
+
```ruby
|
428
|
+
class Create < Trailblazer::Operation
|
429
|
+
contract do
|
430
|
+
property :body
|
431
|
+
validates :body: presence: true, length: {max: 160}
|
432
|
+
end
|
433
|
+
```
|
434
|
+
|
435
|
+
A `::contract` block simply opens a new Reform class for you.
|
436
|
+
|
437
|
+
```ruby
|
438
|
+
contract do #=> Class.new(Reform::Form) do
|
439
|
+
```
|
440
|
+
|
441
|
+
This allows using Reform's API in the block.
|
442
|
+
|
443
|
+
When inheriting, the block is `class_eval`ed in the inherited class' context and allows adding, removing and customizing the sub contract.
|
371
444
|
|
372
445
|
### CRUD Semantics
|
373
446
|
|
data/lib/trailblazer/version.rb
CHANGED
data/test/dispatch_test.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'test_helper'
|
2
|
+
require "trailblazer/operation/dispatch"
|
2
3
|
|
3
4
|
# callbacks are tested in Disposable::Callback::Group.
|
4
5
|
class OperationCallbackTest < MiniTest::Spec
|
5
6
|
Song = Struct.new(:name)
|
6
7
|
|
7
8
|
class Create < Trailblazer::Operation
|
8
|
-
include Dispatch
|
9
|
+
include Trailblazer::Operation::Dispatch
|
9
10
|
|
10
11
|
contract do
|
11
12
|
property :name
|
data/trailblazer.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
21
|
+
|
22
22
|
spec.add_dependency "uber", ">= 0.0.10" # no builder inheritance.
|
23
23
|
# spec.add_dependency "representable", ">= 2.1.1", "<2.3.0" # Representable::apply.
|
24
24
|
spec.add_dependency "reform", ">= 1.2.0"
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "minitest"
|
29
29
|
# spec.add_development_dependency "minitest-spec-rails" # TODO: can anyone make this work (test/rails).
|
30
30
|
spec.add_development_dependency "sidekiq", "~> 3.1.0"
|
31
|
+
spec.add_development_dependency "actionpack", '>= 3.0.0' # Rails is optional.
|
31
32
|
spec.add_development_dependency "rails"
|
32
33
|
spec.add_development_dependency "sqlite3"
|
33
34
|
spec.add_development_dependency "responders"
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: actionpack
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 3.0.0
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 3.0.0
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: uber
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,6 +94,20 @@ dependencies:
|
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
96
|
version: 3.1.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: actionpack
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 3.0.0
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 3.0.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: rails
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|