trailblazer 0.3.3 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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