trailblazer 0.3.0 → 0.3.1
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/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
|