trailblazer 0.3.3 → 1.0.0.rc1

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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGES.md +12 -0
  4. data/Gemfile +2 -1
  5. data/README.md +73 -38
  6. data/Rakefile +1 -1
  7. data/lib/trailblazer/autoloading.rb +4 -1
  8. data/lib/trailblazer/endpoint.rb +7 -13
  9. data/lib/trailblazer/operation.rb +54 -40
  10. data/lib/trailblazer/operation/builder.rb +26 -0
  11. data/lib/trailblazer/operation/collection.rb +1 -2
  12. data/lib/trailblazer/operation/controller.rb +36 -48
  13. data/lib/trailblazer/operation/dispatch.rb +11 -11
  14. data/lib/trailblazer/operation/model.rb +50 -0
  15. data/lib/trailblazer/operation/model/dsl.rb +29 -0
  16. data/lib/trailblazer/operation/model/external.rb +34 -0
  17. data/lib/trailblazer/operation/policy.rb +87 -0
  18. data/lib/trailblazer/operation/policy/guard.rb +34 -0
  19. data/lib/trailblazer/operation/representer.rb +33 -12
  20. data/lib/trailblazer/operation/resolver.rb +30 -0
  21. data/lib/trailblazer/operation/responder.rb +0 -1
  22. data/lib/trailblazer/operation/worker.rb +24 -7
  23. data/lib/trailblazer/version.rb +1 -1
  24. data/test/collection_test.rb +2 -1
  25. data/test/{crud_test.rb → model_test.rb} +17 -35
  26. data/test/operation/builder_test.rb +41 -0
  27. data/test/operation/dsl/callback_test.rb +108 -0
  28. data/test/operation/dsl/contract_test.rb +104 -0
  29. data/test/operation/dsl/representer_test.rb +143 -0
  30. data/test/operation/external_model_test.rb +71 -0
  31. data/test/operation/guard_test.rb +97 -0
  32. data/test/operation/policy_test.rb +97 -0
  33. data/test/operation/resolver_test.rb +83 -0
  34. data/test/operation_test.rb +7 -75
  35. data/test/rails/__respond_test.rb +20 -0
  36. data/test/rails/controller_test.rb +4 -102
  37. data/test/rails/endpoint_test.rb +7 -47
  38. data/test/rails/fake_app/controllers.rb +16 -21
  39. data/test/rails/fake_app/rails_app.rb +5 -0
  40. data/test/rails/fake_app/song/operations.rb +11 -4
  41. data/test/rails/respond_test.rb +95 -0
  42. data/test/responder_test.rb +6 -6
  43. data/test/rollback_test.rb +2 -2
  44. data/test/worker_test.rb +13 -9
  45. data/trailblazer.gemspec +2 -2
  46. metadata +38 -15
  47. data/lib/trailblazer/operation/crud.rb +0 -82
  48. data/lib/trailblazer/rails/railtie.rb +0 -34
  49. data/test/rails/fake_app/views/bands/show.html.erb +0 -1
@@ -0,0 +1,95 @@
1
+ require "test_helper"
2
+
3
+ class ResponderRespondTest < ActionController::TestCase
4
+ tests SongsController
5
+
6
+ # HTML
7
+ # #respond Create [valid]
8
+ test "Create [html/valid]" do
9
+ post :create, {song: {title: "You're Going Down"}}
10
+ assert_redirected_to song_path(Song.last)
11
+ end
12
+
13
+ test "Create [html/valid/location]" do
14
+ post :other_create, {song: {title: "You're Going Down"}}
15
+ assert_redirected_to other_create_songs_path
16
+ end
17
+
18
+ test "Create [html/invalid]" do
19
+ post :create, {song: {title: ""}}
20
+ assert_response 200
21
+ assert_equal @response.body, "{:title=&gt;[&quot;can&#39;t be blank&quot;]}"
22
+ end
23
+
24
+ test "Create [html/invalid/action]" do
25
+ post :other_create, {song: {title: ""}}
26
+ assert_response 200
27
+ assert_equal @response.body, "OTHER SONG\n{:title=&gt;[&quot;can&#39;t be blank&quot;]}\n"
28
+ assert_template "songs/another_view"
29
+ end
30
+
31
+ test "Delete [html/valid]" do
32
+ song = Song::Create[song: {title: "You're Going Down"}].model
33
+ delete :destroy, id: song.id
34
+ assert_redirected_to songs_path
35
+ # assert that model is deleted.
36
+ end
37
+
38
+ test "respond with block [html/valid]" do
39
+ post :create_with_block, {song: {title: "You're Going Down"}}
40
+ assert_response 200
41
+ assert_equal "block run, valid: true", response.body
42
+ end
43
+
44
+ test "respond with block [html/invalid]" do
45
+ post :create_with_block, {song: {title: ""}}
46
+ assert_response 200
47
+ assert_equal "block run, valid: false", response.body
48
+ end
49
+
50
+ # JSON
51
+ test "Delete [json/valid]" do
52
+ song = Song::Create[song: {title: "You're Going Down"}].model
53
+ delete :destroy, id: song.id, format: :json
54
+ assert_response 204 # no content.
55
+ end
56
+
57
+ # JS
58
+ test "Delete [js/valid]" do
59
+ song = Song::Create[song: {title: "You're Going Down"}].model
60
+ assert_raises ActionView::MissingTemplate do
61
+ # js wants to render destroy.js.erb
62
+ delete :destroy, id: song.id, format: :js
63
+ end
64
+ end
65
+
66
+ test "Delete with formats [js/valid]" do
67
+ song = Song::Create[song: {title: "You're Going Down"}].model
68
+
69
+ delete :destroy_with_formats, id: song.id, format: :js
70
+ assert_response 200
71
+ assert_equal "Song slayer!", response.body
72
+ end
73
+ end
74
+
75
+ class ResponderRespondWithJSONTest < ActionController::TestCase
76
+ tests BandsController
77
+
78
+ # JSON
79
+ test "Create [JSON/valid]" do
80
+ post :create, {name: "SNFU"}.to_json, format: :json
81
+ assert_response 201
82
+ assert_equal "SNFU", Band.last.name
83
+ end
84
+ end
85
+
86
+ # TODO: merge with above tests on SongsController.
87
+ class ControllerRespondTest < ActionController::TestCase
88
+ tests BandsController
89
+
90
+ test "#respond with builds" do
91
+ post :create, band: {name: "SNFU"}, admin: true
92
+ assert_response 302
93
+ assert_equal "SNFU [ADMIN]", Band.last.name
94
+ end
95
+ end
@@ -5,7 +5,7 @@ class Song
5
5
  extend ActiveModel::Naming
6
6
 
7
7
  class Operation < Trailblazer::Operation
8
- include CRUD
8
+ include Model
9
9
  model Song
10
10
  include Responder
11
11
 
@@ -20,7 +20,7 @@ module MyApp
20
20
  extend ActiveModel::Naming
21
21
 
22
22
  class Operation < Trailblazer::Operation
23
- include CRUD
23
+ include Model
24
24
  include Responder
25
25
  model Song
26
26
 
@@ -46,8 +46,8 @@ class ResponderTestForModelWithoutNamespace < MiniTest::Spec
46
46
  it { Song::Operation.model_name.singular_route_key.must_equal "song" }
47
47
 
48
48
  # #errors
49
- it { Song::Operation[true].errors.must_equal [] }
50
- it { Song::Operation[false].errors.must_equal [1] } # TODO: since we don't want responder to render anything, just return _one_ error. :)
49
+ it { Song::Operation.(true).errors.must_equal [] }
50
+ it { Song::Operation.(false).errors.must_equal [1] } # TODO: since we don't want responder to render anything, just return _one_ error. :)
51
51
 
52
52
  # TODO: integration test with Controller.
53
53
  end
@@ -68,8 +68,8 @@ class ResponderTestForModelWitNamespace < MiniTest::Spec
68
68
  it { MyApp::Song::Operation.model_name.singular_route_key.must_equal "my_app_song" } # "song" for AR.
69
69
 
70
70
  # #errors
71
- it { MyApp::Song::Operation[true].errors.must_equal [] }
72
- it { MyApp::Song::Operation[false].errors.must_equal [1] } # TODO: since we don't want responder to render anything, just return _one_ error. :)
71
+ it { MyApp::Song::Operation.(true).errors.must_equal [] }
72
+ it { MyApp::Song::Operation.(false).errors.must_equal [1] } # TODO: since we don't want responder to render anything, just return _one_ error. :)
73
73
 
74
74
  # TODO: integration test with Controller.
75
75
  end
@@ -6,11 +6,11 @@ require "test_helper"
6
6
  #
7
7
  # i am keen to try integrating https://github.com/collectiveidea/interactor organizers!
8
8
  module Trailblazer::Operation::Rollback
9
- def run(params)
9
+ def run
10
10
  begin
11
11
  super
12
12
  rescue
13
- rollback!(params, $!)
13
+ rollback!(@params, $!)
14
14
  [false, self]
15
15
  end
16
16
  end
@@ -13,10 +13,10 @@ class WorkerTest < MiniTest::Spec
13
13
  class Operation < Trailblazer::Operation
14
14
  include Worker
15
15
 
16
- def run(params)
16
+ def process(params)
17
17
  with_symbol = params[:title]
18
18
  with_string = params["title"]
19
- "I was working hard on #{params.inspect}. title:#{with_symbol} \"title\"=>#{with_string}"
19
+ @model = "I was working hard on #{params.inspect}. title:#{with_symbol} \"title\"=>#{with_string}"
20
20
  end
21
21
  end
22
22
 
@@ -27,16 +27,18 @@ class WorkerTest < MiniTest::Spec
27
27
  end
28
28
 
29
29
  # test basic worker functionality.
30
- describe "with sidekiq" do
31
- before { @res = Operation.run(title: "Dragonfly") }
30
+ describe "with sidekiq ss" do
31
+ it do
32
+ res = Operation.run(title: "Dragonfly")
32
33
 
33
- it { @res.kind_of?(String).must_equal true } # for now, we return the job from sidekiq.
34
- it { Operation.jobs[0]["args"].must_equal([{"title"=>"Dragonfly"}]) }
35
- it { Operation.perform_one.must_equal "I was working hard on {\"title\"=>\"Dragonfly\"}. title:Dragonfly \"title\"=>Dragonfly" }
34
+ res.kind_of?(String).must_equal true # for now, we return the job from sidekiq
35
+ Operation.jobs[0]["args"].must_equal([{"title"=>"Dragonfly"}])
36
+ Operation.perform_one.last.model.must_equal "I was working hard on {\"title\"=>\"Dragonfly\"}. title:Dragonfly \"title\"=>Dragonfly"
37
+ end
36
38
  end
37
39
 
38
40
  # without sidekiq, we don't have indifferent_access automatically.
39
- it { NoBackgroundOperation.run(title: "Dragonfly").must_equal "I was working hard on {:title=>\"Dragonfly\"}. title:Dragonfly \"title\"=>" }
41
+ it { NoBackgroundOperation.run(title: "Dragonfly").last.model.must_equal "I was working hard on {:title=>\"Dragonfly\"}. title:Dragonfly \"title\"=>" }
40
42
 
41
43
 
42
44
  # test manual serialisation (to be done with UploadedFile etc automatically).
@@ -57,11 +59,13 @@ class WorkerTest < MiniTest::Spec
57
59
 
58
60
  it { @res.kind_of?(String).must_equal true } # for now, we return the job from sidekiq.
59
61
  it { SerializingOperation.jobs[0]["args"].must_equal([{"wrap"=>{"title"=>"Dragonfly"}}]) }
60
- it { SerializingOperation.perform_one.must_equal "I was working hard on {\"title\"=>\"Dragonfly\"}. title:Dragonfly \"title\"=>Dragonfly" }
62
+ it { SerializingOperation.perform_one.last.model.must_equal "I was working hard on {\"title\"=>\"Dragonfly\"}. title:Dragonfly \"title\"=>Dragonfly" }
61
63
  end
62
64
  end
63
65
 
64
66
 
67
+ require "trailblazer/operation/uploaded_file"
68
+ require "action_dispatch/http/upload"
65
69
  class WorkerFileMarshallerTest < MiniTest::Spec
66
70
  def uploaded_file(name)
67
71
  tmp = Tempfile.new("bla")
@@ -18,14 +18,14 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
 
21
- spec.add_dependency "uber", ">= 0.0.10" # no builder inheritance.
21
+ spec.add_dependency "uber", ">= 0.0.15"
22
22
  # spec.add_dependency "representable", ">= 2.1.1", "<2.3.0" # Representable::apply.
23
23
  spec.add_dependency "reform", ">= 1.2.0"
24
24
 
25
25
  spec.add_development_dependency "bundler"
26
26
  spec.add_development_dependency "rake"
27
27
  spec.add_development_dependency "minitest"
28
- spec.add_development_dependency "sidekiq", "~> 3.1.0"
28
+ spec.add_development_dependency "sidekiq", ">= 3.1.0"
29
29
  spec.add_development_dependency "actionpack", '>= 3.0.0' # Rails is optional.
30
30
  spec.add_development_dependency "rails"
31
31
  spec.add_development_dependency "sqlite3"
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.3.3
4
+ version: 1.0.0.rc1
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-07-14 00:00:00.000000000 Z
11
+ date: 2015-09-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: uber
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.10
19
+ version: 0.0.15
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.10
26
+ version: 0.0.15
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: reform
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -84,14 +84,14 @@ dependencies:
84
84
  name: sidekiq
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: 3.1.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: 3.1.0
97
97
  - !ruby/object:Gem::Dependency
@@ -203,27 +203,41 @@ files:
203
203
  - lib/trailblazer/autoloading.rb
204
204
  - lib/trailblazer/endpoint.rb
205
205
  - lib/trailblazer/operation.rb
206
+ - lib/trailblazer/operation/builder.rb
206
207
  - lib/trailblazer/operation/collection.rb
207
208
  - lib/trailblazer/operation/controller.rb
208
209
  - lib/trailblazer/operation/controller/active_record.rb
209
- - lib/trailblazer/operation/crud.rb
210
210
  - lib/trailblazer/operation/dispatch.rb
211
+ - lib/trailblazer/operation/model.rb
212
+ - lib/trailblazer/operation/model/dsl.rb
213
+ - lib/trailblazer/operation/model/external.rb
211
214
  - lib/trailblazer/operation/module.rb
215
+ - lib/trailblazer/operation/policy.rb
216
+ - lib/trailblazer/operation/policy/guard.rb
212
217
  - lib/trailblazer/operation/representer.rb
218
+ - lib/trailblazer/operation/resolver.rb
213
219
  - lib/trailblazer/operation/responder.rb
214
220
  - lib/trailblazer/operation/uploaded_file.rb
215
221
  - lib/trailblazer/operation/worker.rb
216
- - lib/trailblazer/rails/railtie.rb
217
222
  - lib/trailblazer/version.rb
218
223
  - test/collection_test.rb
219
- - test/crud_test.rb
220
224
  - test/dispatch_test.rb
221
225
  - test/fixtures/apotomo.png
222
226
  - test/fixtures/cells.png
227
+ - test/model_test.rb
223
228
  - test/module_test.rb
229
+ - test/operation/builder_test.rb
224
230
  - test/operation/contract_test.rb
231
+ - test/operation/dsl/callback_test.rb
232
+ - test/operation/dsl/contract_test.rb
233
+ - test/operation/dsl/representer_test.rb
234
+ - test/operation/external_model_test.rb
235
+ - test/operation/guard_test.rb
236
+ - test/operation/policy_test.rb
225
237
  - test/operation/reject_test.rb
238
+ - test/operation/resolver_test.rb
226
239
  - test/operation_test.rb
240
+ - test/rails/__respond_test.rb
227
241
  - test/rails/controller_test.rb
228
242
  - test/rails/endpoint_test.rb
229
243
  - test/rails/fake_app/app-cells/.gitkeep
@@ -234,9 +248,9 @@ files:
234
248
  - test/rails/fake_app/rails_app.rb
235
249
  - test/rails/fake_app/song/operations.rb
236
250
  - test/rails/fake_app/views/bands/index.html.erb
237
- - test/rails/fake_app/views/bands/show.html.erb
238
251
  - test/rails/fake_app/views/songs/another_view.html.erb
239
252
  - test/rails/fake_app/views/songs/new.html.erb
253
+ - test/rails/respond_test.rb
240
254
  - test/rails/test_helper.rb
241
255
  - test/representer_test.rb
242
256
  - test/responder_test.rb
@@ -260,25 +274,34 @@ required_ruby_version: !ruby/object:Gem::Requirement
260
274
  version: '0'
261
275
  required_rubygems_version: !ruby/object:Gem::Requirement
262
276
  requirements:
263
- - - ">="
277
+ - - ">"
264
278
  - !ruby/object:Gem::Version
265
- version: '0'
279
+ version: 1.3.1
266
280
  requirements: []
267
281
  rubyforge_project:
268
- rubygems_version: 2.2.2
282
+ rubygems_version: 2.4.8
269
283
  signing_key:
270
284
  specification_version: 4
271
285
  summary: A new architecture for Rails.
272
286
  test_files:
273
287
  - test/collection_test.rb
274
- - test/crud_test.rb
275
288
  - test/dispatch_test.rb
276
289
  - test/fixtures/apotomo.png
277
290
  - test/fixtures/cells.png
291
+ - test/model_test.rb
278
292
  - test/module_test.rb
293
+ - test/operation/builder_test.rb
279
294
  - test/operation/contract_test.rb
295
+ - test/operation/dsl/callback_test.rb
296
+ - test/operation/dsl/contract_test.rb
297
+ - test/operation/dsl/representer_test.rb
298
+ - test/operation/external_model_test.rb
299
+ - test/operation/guard_test.rb
300
+ - test/operation/policy_test.rb
280
301
  - test/operation/reject_test.rb
302
+ - test/operation/resolver_test.rb
281
303
  - test/operation_test.rb
304
+ - test/rails/__respond_test.rb
282
305
  - test/rails/controller_test.rb
283
306
  - test/rails/endpoint_test.rb
284
307
  - test/rails/fake_app/app-cells/.gitkeep
@@ -289,9 +312,9 @@ test_files:
289
312
  - test/rails/fake_app/rails_app.rb
290
313
  - test/rails/fake_app/song/operations.rb
291
314
  - test/rails/fake_app/views/bands/index.html.erb
292
- - test/rails/fake_app/views/bands/show.html.erb
293
315
  - test/rails/fake_app/views/songs/another_view.html.erb
294
316
  - test/rails/fake_app/views/songs/new.html.erb
317
+ - test/rails/respond_test.rb
295
318
  - test/rails/test_helper.rb
296
319
  - test/representer_test.rb
297
320
  - test/responder_test.rb
@@ -1,82 +0,0 @@
1
- module Trailblazer
2
- class Operation
3
- # The CRUD module will automatically create/find models for the configured +action+.
4
- # It adds a public +Operation#model+ reader to access the model (after performing).
5
- module CRUD
6
- module Included
7
- def included(base)
8
- base.extend Uber::InheritableAttr
9
- base.inheritable_attr :config
10
- base.config = {}
11
-
12
- base.extend ClassMethods
13
- end
14
- end
15
- # this makes ::included overrideable, e.g. to add more featues like CRUD::ActiveModel.
16
- extend Included
17
-
18
-
19
- module ClassMethods
20
- def model(name, action=nil)
21
- self.config[:model] = name
22
- action(action) if action # coolest line ever.
23
- end
24
-
25
- def action(name)
26
- self.config[:action] = name
27
- end
28
-
29
- def action_name # considered private.
30
- self.config[:action] or :create
31
- end
32
-
33
- def model_class # considered private.
34
- self.config[:model] or raise "[Trailblazer] You didn't call Operation::model." # TODO: infer model name.
35
- end
36
- end
37
-
38
-
39
- # #validate no longer accepts a model since this module instantiates it for you.
40
- def validate(params, model=self.model, *args)
41
- super(params, model, *args)
42
- end
43
-
44
- private
45
- def model!(params)
46
- instantiate_model(params)
47
- end
48
-
49
- def instantiate_model(params)
50
- send("#{self.class.action_name}_model", params)
51
- end
52
-
53
- def create_model(params)
54
- self.class.model_class.new
55
- end
56
-
57
- def update_model(params)
58
- self.class.model_class.find(params[:id])
59
- end
60
-
61
- alias_method :find_model, :update_model
62
-
63
-
64
- # Rails-specific.
65
- # ActiveModel will automatically call Form::model when creating the contract and passes
66
- # the operation's +::model+, so you don't have to call it twice.
67
- # This assumes that the form class includes Form::ActiveModel, though.
68
- module ActiveModel
69
- def self.included(base)
70
- base.extend ClassMethods
71
- end
72
-
73
- module ClassMethods
74
- def contract(&block)
75
- super
76
- contract_class.model(model_class) # this assumes that Form::ActiveModel is mixed in.
77
- end
78
- end
79
- end
80
- end
81
- end
82
- end