trailblazer 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 706f11d67b247409077b6cf7dd236ee0cbb67490
4
- data.tar.gz: 02c05af733cceef4436e35f018a71e667ef45db5
3
+ metadata.gz: f2618bcea766e5192fadaa575315a4ad04170654
4
+ data.tar.gz: fe623b65e42ac232f9bc0ec45a723b5fa5da70e2
5
5
  SHA512:
6
- metadata.gz: 389626ff14345d9a37ca9159ecce584233d74ccb09248f34eb09a332af95bf9457b13dcf653a8579d23bc45ee92e778f8c19218665167c768e8b8961f4ef4dfb
7
- data.tar.gz: 5ec3b0a491cf5a56159f79463a15f88971ef53ef9e72984d9c2a3aec627fdbefd8c2de58eeed9e5cfc3a6747e2f7e59d4635bf1a9e736c7f6f586eef6617243e
6
+ metadata.gz: befbbee9f37e868da73002c26ce09b91f609ee0e1ded07ffc976dc56cd4440cfa23609911939394c48427d7e9a69bcf28839404a2fb411584d6215b11f79f8c2
7
+ data.tar.gz: e132220e955dc8b0338584b3fc1055d9abacd2100308277829cbf51bfa6b0ecb5bf2a605c7759bb9a36a191ac9985fe514c4a581ede512cd003a74728928c6e6
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 1.0.1
2
+
3
+ * Treat `:js` requests as non-document, too.
4
+ * `Controller#form` now returns the form object and not the operation.
5
+ * In `Controller`, `#form`, `#present`, `#run` and `#respond` now all have the same API: `run(constant, options)`. If you want to pass a custom params hash, use `run Comment::Create, params: {..}`.
6
+
1
7
  # 1.0.0
2
8
 
3
9
  * All Rails-relevant files are now in the `trailblazer-rails` gem. You have to include it should you be in a Rails environment.
data/README.md CHANGED
@@ -18,13 +18,13 @@ _Trailblazer is a thin layer on top of Rails. It gently enforces encapsulation,
18
18
 
19
19
  Trailblazer is designed to handle different contexts like user roles by applying [inheritance](#inheritance) between and [composing](#composing) of operations, form objects, policies, representers and callbacks.
20
20
 
21
-
21
+ Wanna see some code? Jump [right here](#controllers)!
22
22
 
23
23
  ## Mission
24
24
 
25
25
  While _Trailblazer_ offers you abstraction layers for all aspects of Ruby On Rails, it does _not_ missionize you. Wherever you want, you may fall back to the "Rails Way" with fat models, monolithic controllers, global helpers, etc. This is not a bad thing, but allows you to step-wise introduce Trailblazer's encapsulation in your app without having to rewrite it.
26
26
 
27
- Trailblazer is all about structure. It helps re-organize existing code into smaller components where different concerns are handled in separated classes. Forms go into form objects, views are object-oriented MVC controllers, the business logic happens in dedicated domain objects backed by completely decoupled persistence objects.
27
+ Trailblazer is all about structure. It helps re-organize existing code into smaller components where different concerns are handled in separated classes.
28
28
 
29
29
  Again, you can pick which layers you want. Trailblazer doesn't impose technical implementations, it offers mature solutions for recurring problems in all types of Rails applications.
30
30
 
@@ -61,75 +61,80 @@ The opposite is the case: Controller, view and model become lean endpoints for H
61
61
 
62
62
  ## Routing
63
63
 
64
- Trailblazer uses Rails routing to map URLs to controllers (we will add simplifications to routing soon).
64
+ Trailblazer uses Rails routing to map URLs to controllers, because it works.
65
+
66
+ ```ruby
67
+ Rails.application.routes.draw do
68
+ resources :comments
69
+ end
70
+ ```
65
71
 
66
72
  ## Controllers
67
73
 
68
- Controllers are lean endpoints for HTTP. They differentiate between request formats like HTML or JSON and do not contain any business logic. Actions immediately dispatch to an operation.
74
+ Controllers are lean endpoints for HTTP. They do not contain any business logic. Actions immediately dispatch to an operation.
69
75
 
70
76
  ```ruby
71
77
  class CommentsController < ApplicationController
72
78
  def create
73
- Comment::Create.(params)
79
+ run Comment::Create # Comment::Create is an operation class.
74
80
  end
75
81
  ```
76
82
 
77
- This can be simplified using the `run` method and allows you a simple conditional to handle failing operations.
83
+ The `#run` method invokes the operation. It allows you to run a conditional block of logic if the operation was successful.
78
84
 
79
85
  ```ruby
80
86
  class CommentsController < ApplicationController
81
87
  def create
82
88
  run Comment::Create do |op|
83
- return redirect_to(comment_path op.model) # success.
89
+ return redirect_to(comment_path op.model) # success!
84
90
  end
85
91
 
86
- render :new # re-render form.
92
+ render :new # invalid. re-render form.
87
93
  end
88
94
  ```
89
95
 
90
- Again, the controller only knows how to dispatch to the operation and what to do for success and invalid processing. While business affairs (e.g. logging or rollbacks) are to be handled in the operation, the controller invokes rendering or redirecting.
96
+ Again, the controller only dispatchs to the operation and handles successful/invalid processing on the HTTP level. For instance by redirecting, setting flash messages, or signing in a user.
91
97
 
92
- ## Operation
98
+ [Learn more.](http://trailblazerb.org/gems/operation/controller.html)
93
99
 
94
- The [API is documented here](http://trailblazerb.org/gems/operation/api.html).
100
+ ## Operation
95
101
 
96
- Operations encapsulate business logic and are the heart of a Trailblazer architecture. One operation per high-level domain _function_ is used. Different formats or environments are handled in subclasses. Operations don't know about HTTP or the environment.
102
+ Operations encapsulate business logic and are the heart of a Trailblazer architecture.
97
103
 
98
- An operation is not just a monolithic replacement for your business code. An operation is a simple orchestrator between the form object, models and your business code.
104
+ Operations don't know about HTTP or the environment. You could use an operation in Rails, Lotus, or Roda, it wouldn't know. This makes them an ideal replacement for test factories.
99
105
 
100
- You don't have to use the form/contract if you don't want it.
106
+ An operation is not just a monolithic replacement for your business code. It's a simple orchestrator between the form object, models and your business code.
101
107
 
102
108
  ```ruby
103
- class Comment < ActiveRecord::Base
104
- class Create < Trailblazer::Operation
105
- def process(params)
106
- # do whatever you feel like.
107
- end
109
+ class Comment::Create < Trailblazer::Operation
110
+ def process(params)
111
+ # do whatever you feel like.
108
112
  end
109
113
  end
110
114
  ```
111
115
 
112
116
  Operations only need to implement `#process` which receives the params from the caller.
113
117
 
118
+ [Learn more.](http://trailblazerb.org/gems/operation)
119
+
114
120
  ## Validations
115
121
 
116
- Operations usually have a form object which is simply a `Reform::Form` class. All the [API documented in Reform](https://github.com/apotonick/reform) can be applied and used.
122
+ In Trailblazer, an operation (usually) has a form object which is simply a `Reform::Form` class. All the [API documented in Reform](https://github.com/apotonick/reform) can be applied and used.
117
123
 
118
124
  The operation makes use of the form object using the `#validate` method.
119
125
 
120
126
  ```ruby
121
- class Comment < ActiveRecord::Base
122
- class Create < Trailblazer::Operation
123
- contract do
124
- property :body, validates: {presence: true}
125
- end
127
+ class Comment::Create < Trailblazer::Operation
128
+ contract do
129
+ # this is a Reform::Form class!
130
+ property :body, validates: {presence: true}
131
+ end
126
132
 
127
- def process(params)
128
- @model = Comment.new
133
+ def process(params)
134
+ @model = Comment.new
129
135
 
130
- validate(params[:comment], @model) do |f|
131
- f.save
132
- end
136
+ validate(params[:comment], @model) do |f|
137
+ f.save
133
138
  end
134
139
  end
135
140
  end
@@ -137,59 +142,43 @@ end
137
142
 
138
143
  The contract (aka _form_) is defined in the `::contract` block. You can implement nested forms, default values, validations, and everything else Reform provides.
139
144
 
140
- In case of a valid form the block for `#validate` is invoked. It receives the populated form object. You can use the form to save data or write your own logic. This is where your business logic is implemented, and in turn could be an invocation of service objects or organizers.
141
-
142
- Technically speaking, what really happens in `Operation#validate` is the following.
143
-
144
- ```ruby
145
- contract_class.new(@model).validate(params[:comment])
146
- ```
147
-
148
- This is a familiar work-flow from Reform. Validation does _not_ touch the model.
145
+ In the `#process` method you can define your business logic.
149
146
 
150
-
151
-
152
-
153
- If you prefer keeping your forms in separate classes or even files, you're free to do so.
154
-
155
- ```ruby
156
- class Create < Trailblazer::Operation
157
- self.contract_class = MyCommentForm
158
- ```
147
+ [Learn more.](http://trailblazerb.org/gems/operation/api.html)
159
148
 
160
149
  ## Callbacks
161
150
 
162
151
  Post-processing logic (also known as _callbacks_) is configured in operations.
163
152
 
164
- Following the schema of your contract, you can define callbacks for events tracked by the form's twin.
153
+ Callbacks can be defined in groups. They use the form object's state tracking to find out whether they should be run.
165
154
 
166
155
  ```ruby
167
- class Create < Trailblazer::Operation
156
+ class Comment::Create < Trailblazer::Operation
168
157
  callback(:after_save) do
169
- on_change :upload_file, property: :file
170
-
171
- property :user do
172
- on_create :notify_user!
173
- end
158
+ on_change :markdownize_body! # this is only run when the form object has changed.
174
159
  end
175
160
  ```
176
161
 
177
- The _Imperative Callback_ pattern then allows you to call this _callback group_ wherever you need it.
162
+ Callbacks are never triggered automatically, you have to invoke them! This is called _Imperative Callback_.
178
163
 
179
164
  ```ruby
180
- class Create < Trailblazer::Operation
181
-
165
+ class Comment::Create < Trailblazer::Operation
182
166
  def process(params)
183
167
  validate(params) do
184
168
  contract.save
185
- dispatch!(:after_save)
169
+ dispatch!(:after_save) # run markdownize_body!, but only if form changed.
186
170
  end
187
171
  end
172
+
173
+ def markdownize_body!(comment)
174
+ comment.body = Markdownize.(comment.body)
175
+ end
188
176
  end
189
177
  ```
190
178
 
191
179
  No magical triggering of unwanted logic anymore, but explicit invocations where you want it.
192
180
 
181
+ [Learn more.](http://trailblazerb.org/gems/operation/callback.html)
193
182
 
194
183
  ## Models
195
184
 
@@ -198,10 +187,8 @@ Models for persistence can be implemented using any ORM you fancy, for instance
198
187
  In Trailblazer, models are completely empty. They solely contain associations and finders. No business logic is allowed in models.
199
188
 
200
189
  ```ruby
201
- class Thing < ActiveRecord::Base
202
- has_many :comments, -> { order(created_at: :desc) }
203
- has_many :users, through: :authorships
204
- has_many :authorships
190
+ class Comment < ActiveRecord::Base
191
+ belongs_to :thing
205
192
 
206
193
  scope :latest, lambda { all.limit(9).order("id DESC") }
207
194
  end
@@ -211,14 +198,12 @@ Only operations and views/cells can access models directly.
211
198
 
212
199
  ## Policies
213
200
 
214
- The full documentation for [Policy is here](http://trailblazerb.org/gems/operation/policy.html).
215
-
216
201
  You can abort running an operation using a policy. "[Pundit](https://github.com/elabs/pundit)-style" policy classes define the rules.
217
202
 
218
203
  ```ruby
219
- class Thing::Policy
220
- def initialize(user, thing)
221
- @user, @thing = user, thing
204
+ class Comment::Policy
205
+ def initialize(user, comment)
206
+ @user, @comment = user, comment
222
207
  end
223
208
 
224
209
  def create?
@@ -230,31 +215,15 @@ end
230
215
  The rule is enabled via the `::policy` call.
231
216
 
232
217
  ```ruby
233
- class Thing::Create < Trailblazer::Operation
218
+ class Comment::Create < Trailblazer::Operation
234
219
  include Policy
235
220
 
236
- policy Thing::Policy, :create?
221
+ policy Comment::Policy, :create?
237
222
  ```
238
223
 
239
224
  The policy is evaluated in `#setup!`, raises an exception if `false` and suppresses running `#process`.
240
225
 
241
- ```ruby
242
- Thing::Create.(current_user: User.find(1), thing: {}) # raises exception.
243
- ```
244
-
245
- You can query the `policy` object at any point in your operation without raising an exception.
246
-
247
- To [use policies in your builders](http://trailblazerb.org/gems/operation/builder#resolver.html), please read the documentation.
248
-
249
- ```ruby
250
- class Thing::Create < Trailblazer::Operation
251
- include Resolver
252
-
253
- builder-> (model, policy, params) do
254
- return Admin if policy.admin?
255
- return SignedIn if params[:current_user]
256
- end
257
- ```
226
+ [Learn more.](http://trailblazerb.org/gems/operation/policy.html)
258
227
 
259
228
  ## Views
260
229
 
@@ -262,304 +231,63 @@ View rendering can happen using the controller as known from Rails. This is abso
262
231
 
263
232
  More complex UI logic happens in _View Models_ as found in [Cells](https://github.com/apotonick/cells). View models also replace helpers.
264
233
 
265
-
266
- ## Representers
267
-
268
- Operations can use representers from [Roar](https://github.com/apotonick/roar) to serialize and parse JSON and XML documents for APIs.
269
-
270
- Representers can be inferred automatically from your contract, then may be refined, e.g. with hypermedia or a format like `JSON-API`.
271
-
272
- ```ruby
273
- class Create < Trailblazer::Operation
274
-
275
- representer do
276
- # inherited :body
277
- include Roar::JSON::HAL
278
-
279
- link(:self) { comment_path(represented.id) }
280
- end
281
- ```
282
-
283
- 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_.
284
-
285
- ## Tests
286
-
287
- Subject to tests are mainly _Operation_s and _View Model_s, as they encapsulate endpoint behavior of your app. As a nice side effect, factories are replaced by simple _Operation_ calls.
288
-
289
-
290
-
291
- ## Overview
292
-
293
- Trailblazer is a collection of mature gems that have been developed over the past 10 years and are used in thousands of production apps.
294
-
295
- Using the different layers is completely optional and up to you: Both Cells and Reform can be excluded from your stack if you wish so.
296
-
297
- ## Controller API
298
-
299
- [Learn more](http://trailblazerb.org/gems/operation/controller.html)
300
-
301
- Trailblazer provides four methods to present and invoke operations. But before that, you need to include the `Controller` module.
234
+ The operation's form object can be rendered in views, too.
302
235
 
303
236
  ```ruby
304
237
  class CommentsController < ApplicationController
305
- include Trailblazer::Operation::Controller
306
- ```
307
-
308
- ### Running an operation
309
-
310
- If you do not intend to maintain different request formats, the easiest is to use `#run` to process incoming data using an operation.
311
-
312
- ```ruby
313
- def create
314
- run Comment::Create
315
- end
316
- ```
317
-
318
- This will simply run `Comment::Create[params]`.
319
-
320
-
321
-
322
-
323
-
324
- ----
325
-
326
- It's up to the operation's builder to decide which class to instantiate.
327
-
328
- ```ruby
329
- class Create < Trailblazer::Operation
330
- builds do |params|
331
- JSON if params[:format] == "json"
238
+ def new
239
+ form Comment::Create # will assign the form object to @form.
332
240
  end
333
- end
334
241
  ```
335
242
 
336
- [Note that this will soon be provided with a module.]
337
-
338
-
339
- ## Operation API
243
+ Since Reform objects can be passed to form builders, you can use the operation to render and process the form!
340
244
 
341
- ### Call style
342
-
343
- The simplest way of running an operation is the _call style_.
344
-
345
- ```ruby
346
- op = Comment::Create.(params)
245
+ ```haml
246
+ = simple_form_for @form do |f|
247
+ = f.input :body
347
248
  ```
348
249
 
349
- The call style runs the operation and return the operation instance, only.
350
-
351
- In case of an invalid operation, this will raise an exception.
352
-
353
- ### Run style
354
-
355
- 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.
356
-
357
- ```ruby
358
- result, op = Comment::Create.run(params)
359
- ```
360
-
361
- Additionally, it accepts a block that's only run for a valid state.
362
-
363
- ```ruby
364
- Comment::Create.run(params) do |op|
365
- # only run when valid.
366
- end
367
- ```
368
-
369
- ### Inheritance
370
-
371
- Operations fully support inheritance and will copy the contract, the callback groups and any methods from the original operation class.
372
-
373
- ```ruby
374
- class Create < Trailblazer::Operation
375
- contract do
376
- property :title
377
- end
378
250
 
379
- callback(:before_save) do
380
- on_change :notify!
381
- end
382
-
383
- def notify!(*)
384
- end
385
- end
386
- ```
387
-
388
- This happens with normal Ruby inheritance.
389
-
390
- ```ruby
391
- class Update < Create
392
- # inherited:
393
- # contract
394
- # callback(:before_save)
395
- # def notify!(*)
396
- end
397
- ```
398
-
399
- You can customize callback groups and contracts using the `:inherit` option, add and remove properties or add methods.
400
-
401
- [Learn more](http://trailblazerb.org/gems/operation/inheritance.html)
402
-
403
- ### Modules
404
-
405
- In case inheritance is not enough for you, use modules to share common functionality.
406
-
407
- ```ruby
408
- module ExtendedCreate
409
- include Trailblazer::Operation::Module
410
-
411
- contract do
412
- property :id
413
- end
414
-
415
- callback do
416
- on_update :update!
417
- end
418
-
419
- def update!(song)
420
- # do something
421
- end
422
- end
423
- ```
424
-
425
- Modules can be included and will simply run the declarations in the including class.
426
-
427
- ```ruby
428
- class Create::Admin < Create
429
- include ExtendedCreate
430
-
431
- # contract has :title and :id now.
432
- ```
433
-
434
- Modules are often used to modify an existing operation for admin or signed-in roles.
435
-
436
- [Learn more](http://trailblazerb.org/gems/operation/inheritance.html)
437
-
438
-
439
- ## Form API
440
-
441
- Usually, an operation has a form object.
442
-
443
- ```ruby
444
- class Create < Trailblazer::Operation
445
- contract do
446
- property :body
447
- validates :body: presence: true, length: {max: 160}
448
- end
449
- ```
450
-
451
- A `::contract` block simply opens a new Reform class for you.
452
-
453
- ```ruby
454
- contract do #=> Class.new(Reform::Form) do
455
- ```
456
-
457
- This allows using Reform's API in the block.
458
-
459
- When inheriting, the block is `class_eval`ed in the inherited class' context and allows adding, removing and customizing the sub contract.
460
-
461
- ### CRUD Semantics
462
-
463
- You can make Trailblazer find and create models for you using the `Model` module.
464
-
465
- ```ruby
466
- require 'trailblazer/operation/model'
467
-
468
- class Comment < ActiveRecord::Base
469
- class Create < Trailblazer::Operation
470
- include Model
471
- model Comment, :create
472
-
473
- contract do
474
- # ..
475
- end
476
-
477
- def process(params)
478
- validate(params[:comment]) do |f|
479
- f.save
480
- end
481
- end
482
- end
483
- end
484
- ```
485
-
486
- You have to tell `Model` the model class and what action to implement using `::model`.
487
-
488
- Note how you do not have to pass the `@model` to validate anymore. Also, the `@model` gets created automatically and is accessible using `Operation#model`.
489
-
490
- In inherited operations, you can override the action, only, using `::action`.
491
-
492
- ```ruby
493
- class Update < Create
494
- action :update
495
- end
496
- ```
497
-
498
- Another action is `:find` (which is currently doing the same as `:update`) to find a model by using `params[:id]`.
499
-
500
-
501
- ### Normalizing params
502
-
503
- Override `#setup_params!` to add or remove values to params before the operation is run.
504
-
505
- ```ruby
506
- class Create < Trailblazer::Operation
507
- def process(params)
508
- params #=> {show_all: true, admin: true, .. }
509
- end
510
-
511
- private
512
- def setup_params!(params)
513
- params.merge!(show_all: true) if params[:admin]
514
- end
515
- end
516
- end
517
- ```
518
-
519
- This centralizes params normalization and doesn't require you to do that manually in `#process`.
520
-
521
- ### Collections
522
-
523
- Operations can also be used to present (and soon to process) collections of objects, e.g. for an `Index` operation. This is [documented here](http://trailblazerb.org/gems/operation/collection.html).
251
+ ## Representers
524
252
 
525
- ### Background Processing
253
+ Operations can use representers from [Roar](https://github.com/apotonick/roar) to serialize and parse JSON and XML documents for APIs.
526
254
 
527
- To run an operation in Sidekiq (ActiveJob-support coming!) all you need to do is include the `Worker` module.
255
+ Representers can be inferred automatically from your contract, then may be refined, e.g. with hypermedia or a format like `JSON-API`.
528
256
 
529
257
  ```ruby
530
- require 'trailblazer/operation/worker'
531
-
532
- class Comment::Image::Crop < Trailblazer::Operation
533
- include Worker
258
+ class Comment::Create < Trailblazer::Operation
259
+ representer do
260
+ # inherited :body
261
+ include Roar::JSON::HAL
534
262
 
535
- def process(params)
536
- # will be run asynchronously.
263
+ link(:self) { comment_path(represented.id) }
537
264
  end
538
- end
539
265
  ```
540
266
 
267
+ The operation can then parse incoming JSON documents in `validate` and render a document via `to_json`.
541
268
 
542
- ### Worker::FileMarshaller: needs representable 2.1.1 (.schema)
543
-
269
+ [Learn more.](http://trailblazerb.org/gems/operation/representer.html)
544
270
 
545
- ### Testing Operations
546
-
547
- ## Autoloading
271
+ ## Tests
548
272
 
549
- Use our autoloading if you dislike explicit requires.
273
+ In Trailblazer, you only have operation unit tests and integration smoke tests to test the operation/controller wiring.
550
274
 
551
- You can just add
275
+ Operations completely replace the need for leaky factories.
552
276
 
553
277
  ```ruby
554
- require "trailblazer/autoloading"
278
+ describe Comment::Update do
279
+ let(:comment) { Comment::Create.(comment: {body: "[That](http://trailblazerb.org)!"}) }
555
280
  ```
556
281
 
557
- to `config/initializers/trailblazer.rb` and implementation classes like `Operation` will be automatically loaded.
282
+ ## More
558
283
 
559
- ## Operation Autoloading
284
+ Trailblazer has many more architectural features such as
560
285
 
561
- If you structure your CRUD operations using the `app/concepts/*/crud.rb` file layout we use in the book, the `crud.rb` files are not gonna be found by Rails automatically. It is a good idea to enable CRUD autoloading.
286
+ * Polymorphic builders and operations
287
+ * Inheritance and composition support
288
+ * Polymorphic views
562
289
 
290
+ Check the project website and the book.
563
291
 
564
292
  ## Installation
565
293
 
@@ -573,40 +301,11 @@ gem "cells"
573
301
 
574
302
  Cells is _not_ required per default! Add it if you use it, which is highly recommended.
575
303
 
576
- A few quirks are required at the moment as Rails autoloading is giving us a hard time. The setup of an app [is documented here](https://github.com/apotonick/gemgem-trbrb/wiki/Things-You-Should-Know).
577
-
578
- ## Undocumented Features
579
-
580
- (Please don't read this section!)
581
-
582
-
583
-
584
- ### Named Controller Instance Variables
585
-
586
- If you want to include named instance variables for you views you must include another ActiveRecord specific module.
587
-
588
- ```ruby
589
- require 'trailblazer/operation/controller/active_record'
590
-
591
- class ApplicationController < ActionController::Base
592
- include Trailblazer::Operation::Controller
593
- include Trailblazer::Operation::Controller::ActiveRecord
594
- end
595
- ```
596
-
597
- This will setup a named instance variable of your operation's model, for example `@song`.
598
-
599
304
  ## The Book
600
305
 
601
306
  ![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
602
307
 
603
- Please buy my book [Trailblazer - A new architecture for Rails](https://leanpub.com/trailblazer) and [let me know](http://twitter.com/apotonick) what you think! I am still working on the book but keep adding new chapters every other week. It will be about 300 pages and we're developing a real, full-blown Rails/Trb application.
308
+ Please buy it: [Trailblazer - A new architecture for Rails](https://leanpub.com/trailblazer).
604
309
 
605
310
  The [demo application](https://github.com/apotonick/gemgem-trbrb) implements what we discuss in the book.
606
311
 
607
- ## Why?
608
-
609
- * Grouping code, views and assets by concepts increases the **maintainability** of your apps. Developers will find their way faster into your structure as the file layout is more intuitive.
610
- * Finding bugs gets less frustrating as encapsulated layers allow **testing components** in total isolation. Once you know your form and your view are ok, it must be the parsing code.
611
- * The reusability of code increases drastically as Trailblazer gently pushes you towards encapsulation. No more redundant helpers but clean inheritance.
612
- * No more surprises from ActiveRecord's massive API. The separation between persistence and domain automatically results in smaller, less destructive APIs for your models.
@@ -1,3 +1,7 @@
1
+ Trailblazer.class_eval do
2
+ autoload :NotAuthorizedError, "trailblazer/operation/policy"
3
+ end
4
+
1
5
  Trailblazer::Operation.class_eval do
2
6
  autoload :Controller, "trailblazer/operation/controller"
3
7
  autoload :Model, "trailblazer/operation/model"
@@ -12,7 +12,7 @@ module Trailblazer
12
12
 
13
13
  def call
14
14
  document_body! if document_request?
15
- yield # Create.run(params)
15
+ yield @params# Create.run(params)
16
16
  end
17
17
 
18
18
  private
@@ -137,7 +137,7 @@ module Trailblazer
137
137
  result
138
138
  end
139
139
 
140
- # When using Op::[], an invalid contract will raise an exception.
140
+ # When using Op.(), an invalid contract will raise an exception.
141
141
  def raise!(contract)
142
142
  raise InvalidContract.new(contract.errors.to_s) if @options[:raise_on_invalid]
143
143
  end
@@ -5,13 +5,13 @@ private
5
5
  def form(*args)
6
6
  operation!(*args).tap do |op|
7
7
  op.contract.prepopulate! # equals to @form.prepopulate!
8
- end
8
+ end.contract
9
9
  end
10
10
 
11
11
  # Provides the operation instance, model and contract without running #process.
12
12
  # Returns the operation.
13
- def present(operation_class, params=self.params)
14
- operation!(operation_class, params, skip_form: true)
13
+ def present(operation_class, options={})
14
+ operation!(operation_class, skip_form: true)
15
15
  end
16
16
 
17
17
  def collection(*args)
@@ -20,8 +20,8 @@ private
20
20
  end
21
21
  end
22
22
 
23
- def run(operation_class, params=self.params, &block)
24
- res, op = operation_for!(operation_class, params) { operation_class.run(params) }
23
+ def run(operation_class, options={}, &block)
24
+ res, op = operation_for!(operation_class, options) { |params| operation_class.run(params) }
25
25
 
26
26
  yield op if res and block_given?
27
27
 
@@ -29,8 +29,9 @@ private
29
29
  end
30
30
 
31
31
  # The block passed to #respond is always run, regardless of the validity result.
32
- def respond(operation_class, options={}, params=self.params, &block)
33
- res, op = operation_for!(operation_class, params, options) { operation_class.run(params) }
32
+ def respond(operation_class, options={}, &block)
33
+ options[:___dont_deprecate] = 1 # TODO: remove in 1.1.
34
+ res, op = operation_for!(operation_class, options) { |params| operation_class.run(params) }
34
35
  namespace = options.delete(:namespace) || []
35
36
 
36
37
  return respond_with *namespace, op, options if not block_given?
@@ -38,8 +39,8 @@ private
38
39
  end
39
40
 
40
41
  private
41
- def operation!(operation_class, params=self.params, options={}) # or #model or #setup.
42
- res, op = operation_for!(operation_class, params, options) { [true, operation_class.present(params)] }
42
+ def operation!(operation_class, options={}) # or #model or #setup.
43
+ res, op = operation_for!(operation_class, options) { |params| [true, operation_class.present(params)] }
43
44
  op
44
45
  end
45
46
 
@@ -47,9 +48,13 @@ private
47
48
  end
48
49
 
49
50
  # Normalizes parameters and invokes the operation (including its builders).
50
- def operation_for!(operation_class, params, options={}, &block)
51
- # Per default, only treat :html as non-document.
52
- options[:is_document] ||= request.format == :html ? false : true
51
+ def operation_for!(operation_class, options, &block)
52
+ options = deprecate_positional_params_argument!(options)
53
+
54
+ # Per default, only treat :html and js as non-document.
55
+ default_options = {is_document: ![:html, :js].include?(request.format.to_sym)}
56
+ options = default_options.merge(options)
57
+ params = options.delete(:params) || self.params # TODO: test params: parameter properly in all 4 methods.
53
58
 
54
59
  process_params!(params)
55
60
  res, op = Trailblazer::Endpoint.new(operation_class, params, request, options).(&block)
@@ -58,6 +63,19 @@ private
58
63
  [res, op]
59
64
  end
60
65
 
66
+ def deprecate_positional_params_argument!(options) # TODO: remove in 1.1.
67
+ return options if options.has_key?(:skip_form)
68
+ return options if options.has_key?(:is_document)
69
+ return options if options.has_key?(:params)
70
+ return options if options.has_key?(:namespace)
71
+ return options if options.has_key?(:___dont_deprecate)
72
+ return options if options.size == 0
73
+
74
+ warn "[Trailblazer] The positional params argument for #run, #present, #form and #respond is deprecated and will be removed in 1.1.
75
+ Please provide a custom params via `run Comment::Create, params: {..}` and have a nice day."
76
+ {params: options}
77
+ end
78
+
61
79
  def setup_operation_instance_variables!(operation, options)
62
80
  @operation = operation
63
81
  @model = operation.model
@@ -33,6 +33,7 @@ module Trailblazer::Operation::Representer
33
33
  options_from: :deserializer, # use :instance etc. in deserializer.
34
34
  superclass: Representable::Decorator,
35
35
  representer_from: lambda { |inline| inline.representer_class },
36
+ exclude_options: [:default, :populator] # TODO: test with populator: in an operation.
36
37
  )
37
38
  end
38
39
  end
@@ -1,3 +1,3 @@
1
1
  module Trailblazer
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trailblazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.0.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-09-25 00:00:00.000000000 Z
11
+ date: 2015-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uber
@@ -237,4 +237,3 @@ test_files:
237
237
  - test/representer_test.rb
238
238
  - test/rollback_test.rb
239
239
  - test/test_helper.rb
240
- has_rdoc: