trailblazer 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02c7b4eafd8dfcbb9eb29aafd91a9213aa34d0e9
4
- data.tar.gz: 8720dec06b3af4b127f77ca140c25e71f7a68799
3
+ metadata.gz: 994926094dd0136c1436127773dd4c8786cd3cb3
4
+ data.tar.gz: ce869dcf9f8c01e2fe10847e3d4b54e460537960
5
5
  SHA512:
6
- metadata.gz: 20bd8a8b6b296bea95bd73a27269855af238c03b5617885c6a51015dc40b2ef7d1c5ea7ff394961e8871d0724fd614d1868227afb3112b74685f55f42eb6a9b1
7
- data.tar.gz: 79c3548b02e02789dc8a1ea55141d1786b710d53dd3213aae0cc0e28a1373c247debae6c1e845522e95235d7befd9fd30968f1ba92fbd7d4fdaaedc5641f9ae1
6
+ metadata.gz: 26367bea09297a1bbcf29506d589826f3e83fca5dc45e4bb6d7146a57f2984c60d324cda43f55a52cd08f3523ed5e76d5b0c56bbddd531a2509ccd4e81117861
7
+ data.tar.gz: ff56573c794feb3afbbc7bac40026f65f418153e9aaef9cbdb6967a01d37412ba1faa95fb4127bab82efd6ad0f5ef10a2ea359a8f94bd8749dbebd480b64d8a2
data/CHANGES.md CHANGED
@@ -1,3 +1,20 @@
1
+ # 0.2.0
2
+
3
+ ## API Changes
4
+
5
+ * `Controller#present` no longer calls `respond_to`, but lets you do the rendering. This will soon be re-introduced using `respond(present: true)`.
6
+ * `Controller#form` did not respect builders, this is fixed now.
7
+ * Use `request.body.read` in Unicorn/etc. environments in `Controller#respond`.
8
+
9
+ ## Stuff
10
+
11
+ * Autoloading changed, again. We now `require_dependency` in every request in dev.
12
+
13
+ # 0.1.3
14
+
15
+ * `crud_autoloading` now simply `require_dependency`s model files, then does the same for the CRUD operation file. This should fix random undefined constant problems in development.
16
+ * `Controller#form` did not use builders. This is fixed now.
17
+
1
18
  # 0.1.2
2
19
 
3
20
  * Add `crud_autoloading`.
data/README.md CHANGED
@@ -6,7 +6,7 @@ In a nutshell: Trailblazer makes you write **logicless models** that purely act
6
6
 
7
7
  ![](https://raw.githubusercontent.com/apotonick/trailblazer/master/doc/trb.jpg)
8
8
 
9
- Please sign up for my upcoming book [Trailblazer - A new architecture for Rails](https://leanpub.com/trailblazer), check out the free sample chapter and [let me know](http://twitter.com/apotonick) what you think!
9
+ 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.
10
10
 
11
11
  The [demo application](https://github.com/apotonick/gemgem-trbrb) implements what we discuss in the book.
12
12
 
@@ -173,7 +173,7 @@ Again, this will only run the operation's setup and provide the model in `@model
173
173
 
174
174
  For document-based APIs and request types that are not HTTP the operation will be advised to render the JSON or XML document using the operation's representer.
175
175
 
176
- Note that `#present` will also work instead of `#form` (allowing it to be used in `#new` and `#edit`, too) as the responder will _not_ trigger any rendering in those actions.
176
+ Note that `#present` will leave rendering up to you - `respond_to` is _not_ called.
177
177
 
178
178
  ### Controller API
179
179
 
@@ -415,18 +415,22 @@ Use our autoloading if you dislike explicit requires.
415
415
  You can just add
416
416
 
417
417
  ```ruby
418
- require 'trailblazer/autoloading'
418
+ require "trailblazer/autoloading"
419
419
  ```
420
420
 
421
- to `config/initializers/trailblazer.rb` and implementation files like `Operation` will be automatically loaded.
421
+ to `config/initializers/trailblazer.rb` and implementation classes like `Operation` will be automatically loaded.
422
422
 
423
- In case you structure your CRUD operations in the `app/concepts/thing/crud.rb` file layout we use in the book, you're gonna run into `Missing constant` trouble. As the `crud.rb` files are not found by Rails it is a good idea to enable CRUD autoloading.
423
+ ## Operation Autoloading
424
+
425
+ 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.
426
+
427
+ At the end of your `config/application.rb` file, add the following.
424
428
 
425
429
  ```ruby
426
- require trailblazer/crud_autoloading
430
+ require "trailblazer/rails/railtie"
427
431
  ```
428
432
 
429
- This will go through `app/concepts/`, find all the `crud.rb` files, autload their corresponding namespace (e.g. `Thing`, which is a model) and then load the `crud` file.
433
+ This will go through `app/concepts/`, find all the `crud.rb` files, autoload their corresponding namespace (e.g. `Thing`, which is a model) and then load the `crud.rb` file.
430
434
 
431
435
 
432
436
  ## Why?
@@ -5,7 +5,7 @@ private
5
5
  def form(operation_class, params=self.params) # consider private.
6
6
  process_params!(params)
7
7
 
8
- @operation = operation_class.new.present(params)
8
+ @operation = operation_class.present(params)
9
9
  @form = @operation.contract
10
10
  @model = @operation.model
11
11
 
@@ -19,7 +19,8 @@ private
19
19
  res, op = operation!(operation_class, params) { [true, operation_class.present(params)] }
20
20
 
21
21
  yield op if block_given?
22
- respond_with op
22
+ # respond_with op
23
+ # TODO: implement respond(present: true)
23
24
  end
24
25
 
25
26
  # full-on Op[]
@@ -64,8 +65,9 @@ private
64
65
  # this is what happens:
65
66
  # respond_with Comment::Update::JSON.run(params.merge(comment: request.body.string))
66
67
  concept_name = operation_class.model_class.to_s.underscore # this could be renamed to ::concept_class soon.
68
+ request_body = request.body.respond_to?(:string) ? request.body.string : request.body.read
67
69
 
68
- params.merge!(concept_name => request.body.string)
70
+ params.merge!(concept_name => request_body)
69
71
  end
70
72
 
71
73
  res, @operation = yield # Create.run(params)
@@ -0,0 +1,24 @@
1
+ module Trailblazer
2
+ class Railtie < Rails::Railtie
3
+ def self.autoload_crud_operations(app)
4
+ Dir.glob("app/concepts/**/crud.rb") do |f|
5
+ path = f.sub("app/concepts/", "")
6
+ model = path.sub("/crud.rb", "")
7
+
8
+ require_dependency "#{app.root}/app/models/#{model}" # load the model file, first (thing.rb).
9
+ require_dependency "#{app.root}/#{f}" # load app/concepts/{concept}/crud.rb (Thing::Create, Thing::Update, and so on).
10
+ end
11
+ end
12
+
13
+ # thank you, http://stackoverflow.com/a/17573888/465070
14
+ initializer 'trailblazer.install' do |app|
15
+ if Rails.configuration.cache_classes
16
+ Trailblazer::Railtie.autoload_crud_operations(app)
17
+ else
18
+ ActionDispatch::Reloader.to_prepare do
19
+ Trailblazer::Railtie.autoload_crud_operations(app)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,3 @@
1
1
  module Trailblazer
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -40,6 +40,8 @@ class OperationRunTest < MiniTest::Spec
40
40
 
41
41
  # return operation when ::call
42
42
  it { Operation.call(true).must_equal operation }
43
+ it { Operation.(true).must_equal operation }
44
+ # #[] is alias for .()
43
45
  it { Operation[true].must_equal operation }
44
46
 
45
47
  # ::[] raises exception when invalid.
@@ -88,7 +90,7 @@ class OperationRunTest < MiniTest::Spec
88
90
 
89
91
  # Operation#contract returns @contract
90
92
  let (:contract) { Operation::Contract.new }
91
- it { Operation[true].contract.must_equal contract }
93
+ it { Operation.(true).contract.must_equal contract }
92
94
  end
93
95
 
94
96
 
@@ -119,7 +121,7 @@ class OperationTest < MiniTest::Spec
119
121
  # ::run
120
122
  it { OperationWithoutValidateCall.run(Object).must_equal [true, Object] }
121
123
  # ::[]
122
- it { OperationWithoutValidateCall[Object].must_equal(Object) }
124
+ it { OperationWithoutValidateCall.(Object).must_equal(Object) }
123
125
  # ::run with invalid!
124
126
  it { OperationWithoutValidateCall.run(nil).must_equal [false, nil] }
125
127
  # ::run with block, invalid
@@ -155,7 +157,7 @@ class OperationTest < MiniTest::Spec
155
157
  end
156
158
 
157
159
  it { OperationWithValidateBlock.run(false).last.secret_contract.must_equal nil }
158
- it('zzz') { OperationWithValidateBlock[true].secret_contract.must_equal OperationWithValidateBlock.contract_class.new.extend(Comparable) }
160
+ it('zzz') { OperationWithValidateBlock.(true).secret_contract.must_equal OperationWithValidateBlock.contract_class.new.extend(Comparable) }
159
161
 
160
162
  # manually setting @valid
161
163
  class OperationWithManualValid < Trailblazer::Operation
@@ -168,7 +170,7 @@ class OperationTest < MiniTest::Spec
168
170
  # ::run
169
171
  it { OperationWithManualValid.run(Object).must_equal [false, Object] }
170
172
  # ::[]
171
- it { OperationWithManualValid[Object].must_equal(Object) }
173
+ it { OperationWithManualValid.(Object).must_equal(Object) }
172
174
 
173
175
 
174
176
  # re-assign params
@@ -281,8 +283,8 @@ class OperationBuilderTest < MiniTest::Spec
281
283
  it { Operation.run({}).last.must_equal "operation" }
282
284
  it { Operation.run({sub: true}).last.must_equal "sub:operation" }
283
285
 
284
- it { Operation[{}].must_equal "operation" }
285
- it { Operation[{sub: true}].must_equal "sub:operation" }
286
+ it { Operation.({}).must_equal "operation" }
287
+ it { Operation.({sub: true}).must_equal "sub:operation" }
286
288
  end
287
289
 
288
290
  # ::contract builds Reform::Form class
@@ -113,13 +113,29 @@ class ResponderRespondWithJSONTest < ActionController::TestCase
113
113
  end
114
114
  end
115
115
 
116
+ # TODO: merge with above tests on SongsController.
117
+ class ControllerRespondTest < ActionController::TestCase
118
+ tests BandsController
119
+
120
+ test "#respond with builds" do
121
+ post :create, band: {name: "SNFU"}, admin: true
122
+ assert_response 302
123
+ assert_equal "SNFU [ADMIN]", Band.last.name
124
+ end
125
+ end
126
+
116
127
 
117
128
  class ResponderRunTest < ActionController::TestCase
118
129
  tests BandsController
119
130
 
120
131
  test "[html/valid]" do
121
132
  put :update, {id: 1, band: {name: "Nofx"}}
122
- assert_equal "no block: Nofx, Essen", response.body
133
+ assert_equal "no block: Nofx, Essen, Band::Create", response.body
134
+ end
135
+
136
+ test "[html/valid] with builds" do
137
+ put :update, {id: 1, band: {name: "Nofx"}, admin: true}
138
+ assert_equal "no block: Nofx [ADMIN], Essen, Band::Create::Admin", response.body
123
139
  end
124
140
 
125
141
  test "with block [html/valid]" do
@@ -146,6 +162,7 @@ class ControllerPresentTest < ActionController::TestCase
146
162
  assert_equal "bands/show.html: Band,Band,true,Band::Update,Essen\n", response.body
147
163
  end
148
164
 
165
+ # TODO: this implicitely tests builds. maybe have separate test for that?
149
166
  test "#present [JSON]" do
150
167
  band = Band::Create[band: {name: "Nofx"}].model
151
168
 
@@ -171,4 +188,10 @@ class ControllerFormTest < ActionController::TestCase
171
188
 
172
189
  assert_select "b", "Band,Band,true,Band::Create,Essen"
173
190
  end
191
+
192
+ test "#form with builder" do
193
+ get :new, admin: true
194
+
195
+ assert_select "b", ",Band,true,Band::Create::Admin"
196
+ end
174
197
  end
@@ -49,7 +49,9 @@ class BandsController < ApplicationController
49
49
  present Band::Update do |op|
50
50
  @klass = op.model.class
51
51
  @locality = params[:band][:locality] unless params[:format] == "json"
52
- end # respond_to
52
+
53
+ render json: op.to_json if params[:format] == "json"
54
+ end # render :show
53
55
  end
54
56
 
55
57
  def new
@@ -82,7 +84,7 @@ ERB
82
84
  def update
83
85
  run Band::Create
84
86
 
85
- render text: "no block: #{@operation.model.name}, #{params[:band][:locality]}"
87
+ render text: "no block: #{@operation.model.name}, #{params[:band][:locality]}, #{@operation.class}"
86
88
  end
87
89
 
88
90
  def update_with_block
@@ -66,8 +66,25 @@ class Band < ActiveRecord::Base
66
66
  end
67
67
  end
68
68
 
69
+ class Admin < self
70
+ def process(params)
71
+ res = super
72
+ model.update_attribute :name, "#{model.name} [ADMIN]"
73
+ res
74
+ end
75
+ end
76
+
77
+ # TODO: wait for uber 0.0.10 and @dutow.
78
+ # builds -> (params)
79
+ # return JSON if params[:format] == "json"
80
+ # return Admin if params[:admin]
81
+ # end
69
82
  builds do |params|
70
- JSON if params[:format] == "json"
83
+ if params[:format] == "json"
84
+ JSON
85
+ elsif params[:admin]
86
+ Admin
87
+ end
71
88
  end
72
89
  end
73
90
 
data/trailblazer.gemspec CHANGED
@@ -28,4 +28,6 @@ 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 "rails"
32
+ spec.add_development_dependency "sqlite3"
31
33
  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: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-16 00:00:00.000000000 Z
11
+ date: 2015-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -128,6 +128,34 @@ dependencies:
128
128
  - - "~>"
129
129
  - !ruby/object:Gem::Version
130
130
  version: 3.1.0
131
+ - !ruby/object:Gem::Dependency
132
+ name: rails
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: sqlite3
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
131
159
  description: Trailblazer is a thin layer on top of Rails. It gently enforces encapsulation,
132
160
  an intuitive code structure and gives you an object-oriented architecture.
133
161
  email:
@@ -151,7 +179,6 @@ files:
151
179
  - gemfiles/Gemfile.rails.lock
152
180
  - lib/trailblazer.rb
153
181
  - lib/trailblazer/autoloading.rb
154
- - lib/trailblazer/crud_autoloading.rb
155
182
  - lib/trailblazer/operation.rb
156
183
  - lib/trailblazer/operation/controller.rb
157
184
  - lib/trailblazer/operation/crud.rb
@@ -159,6 +186,7 @@ files:
159
186
  - lib/trailblazer/operation/responder.rb
160
187
  - lib/trailblazer/operation/uploaded_file.rb
161
188
  - lib/trailblazer/operation/worker.rb
189
+ - lib/trailblazer/rails/railtie.rb
162
190
  - lib/trailblazer/version.rb
163
191
  - test/crud_test.rb
164
192
  - test/fixtures/apotomo.png
@@ -174,7 +202,6 @@ files:
174
202
  - test/rails/fake_app/song/operations.rb
175
203
  - test/rails/fake_app/views/bands/show.html.erb
176
204
  - test/rails/fake_app/views/songs/new.html.erb
177
- - test/rails/integration_test.rb
178
205
  - test/rails/test_helper.rb
179
206
  - test/responder_test.rb
180
207
  - test/test_helper.rb
@@ -220,7 +247,6 @@ test_files:
220
247
  - test/rails/fake_app/song/operations.rb
221
248
  - test/rails/fake_app/views/bands/show.html.erb
222
249
  - test/rails/fake_app/views/songs/new.html.erb
223
- - test/rails/integration_test.rb
224
250
  - test/rails/test_helper.rb
225
251
  - test/responder_test.rb
226
252
  - test/test_helper.rb
@@ -1,8 +0,0 @@
1
- # FIXME: this will only work for operations embedded in a MODEL namespace.
2
- # this is really hacky and assumes a lot about your structure, but it works for now. don't include it if you don't like it.
3
- Dir.glob("app/concepts/**/crud.rb") do |f|
4
- path = f.sub("app/concepts/", "")
5
-
6
- path.sub("/crud.rb", "").camelize.constantize # load the model first (Thing).
7
- require_dependency path # load model/crud.rb (Thing::Create, Thing::Update, and so on).
8
- end
@@ -1,23 +0,0 @@
1
- require 'test_helper'
2
-
3
- class IntegrationTest < ActionController::TestCase
4
- tests BandsController
5
-
6
- # test rendering JSON of populated band.
7
- test "#present [JSON]" do
8
- band = Band::Create[band: {name: "Nofx", songs: [{title: "Murder The Government"}, {title: "Eat The Meek"}]}].model
9
-
10
- get :show, id: band.id, format: :json
11
- assert_equal "{\"name\":\"Nofx\",\"songs\":[{\"title\":\"Murder The Government\"},{\"title\":\"Eat The Meek\"}]}", response.body
12
- end
13
-
14
- # parsing incoming complex document.
15
- test "create [JSON]" do
16
- post :create, "{\"name\":\"Nofx\",\"songs\":[{\"title\":\"Murder The Government\"},{\"title\":\"Eat The Meek\"}]}", format: :json
17
- assert_response 201
18
-
19
- band = Band.last
20
- assert_equal "Nofx", band.name
21
- assert_equal "Murder The Government", band.songs[0].title
22
- end
23
- end