riot-rails 0.1.0 → 0.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.gitignore +4 -0
  2. data/CHANGELOG +150 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.markdown +31 -0
  5. data/Rakefile +32 -10
  6. data/VERSION +1 -1
  7. data/lib/riot/action_controller/context_macros/asserts_response.rb +15 -0
  8. data/lib/riot/action_controller/context_middleware.rb +48 -0
  9. data/lib/riot/action_controller/http_methods.rb +19 -0
  10. data/lib/riot/action_controller.rb +4 -0
  11. data/lib/riot/active_record/assertion_macros.rb +3 -0
  12. data/lib/riot/active_record/context_middleware.rb +17 -0
  13. data/lib/riot/active_record/database_macros.rb +58 -0
  14. data/lib/riot/active_record/reflection_macros.rb +106 -0
  15. data/lib/riot/active_record/transactional_middleware.rb +28 -0
  16. data/lib/riot/active_record/validation_macros.rb +187 -0
  17. data/lib/riot/active_record.rb +4 -0
  18. data/lib/riot/rails.rb +1 -0
  19. data/rails/init.rb +1 -0
  20. data/riot-rails.gemspec +130 -0
  21. data/test/action_controller/context_macros/asserts_response_test.rb +35 -0
  22. data/test/action_controller/context_middleware_test.rb +66 -0
  23. data/test/action_controller/delete_request_test.rb +45 -0
  24. data/test/action_controller/get_request_test.rb +37 -0
  25. data/test/action_controller/post_request_test.rb +45 -0
  26. data/test/action_controller/put_request_test.rb +45 -0
  27. data/test/action_controller/restful_delete_request_test.rb +28 -0
  28. data/test/action_controller/restful_get_request_test.rb +25 -0
  29. data/test/action_controller/restful_post_request_test.rb +25 -0
  30. data/test/action_controller/restful_put_request_test.rb +28 -0
  31. data/test/active_record/allowing_values_test.rb +64 -0
  32. data/test/active_record/attribute_is_invalid_test.rb +20 -0
  33. data/test/active_record/belongs_to_test.rb +22 -0
  34. data/test/active_record/context_middleware_test.rb +18 -0
  35. data/test/active_record/has_and_belongs_to_many_test.rb +22 -0
  36. data/test/active_record/has_database_index_on_test.rb +73 -0
  37. data/test/active_record/has_many_test.rb +22 -0
  38. data/test/active_record/has_one_test.rb +22 -0
  39. data/test/active_record/validates_length_of_test.rb +31 -0
  40. data/test/active_record/validates_presence_of_test.rb +14 -0
  41. data/test/active_record/validates_uniqueness_of_test.rb +23 -0
  42. data/test/rails_root/app/controllers/gremlins_controller.rb +21 -0
  43. data/test/rails_root/app/controllers/parties_controller.rb +17 -0
  44. data/test/rails_root/app/controllers/rooms_controller.rb +22 -0
  45. data/{README.md → test/rails_root/app/views/rendered_templates/foo_bar.html.erb} +0 -0
  46. data/test/rails_root/config/database.yml +4 -0
  47. data/test/rails_root/config/environment.rb +46 -0
  48. data/test/rails_root/config/routes.rb +7 -0
  49. data/test/rails_root/db/schema.rb +8 -0
  50. data/test/teststrap.rb +84 -0
  51. data/test/transactional_middleware_test.rb +27 -0
  52. metadata +154 -26
  53. data/init.rb +0 -0
  54. data/install.rb +0 -0
  55. data/lib/generators/riot_rails/model/model_generator.rb +0 -22
  56. data/lib/generators/riot_rails/model/templates/fixtures.yml +0 -23
  57. data/lib/generators/riot_rails/model/templates/riot_test.rb +0 -5
  58. data/lib/generators/riot_rails.rb +0 -10
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ test/rails_root/log/*
2
+ pkg/*
3
+ riot-rails.tmproj
4
+ test/rails_root/db/*.db
data/CHANGELOG ADDED
@@ -0,0 +1,150 @@
1
+ *0.2.0.pre*
2
+
3
+ * Using latest middleware construct from Riot 0.11.1. Also using eigen-class for hijacking local_run in the transactional, active record middleware. [jaknowlden]
4
+
5
+ * Bumping version up arbitrarily in order to move it past the previous 0.1.0 that we hijacked from @dasch :) [jaknowlden]
6
+
7
+ *0.0.10.pre.3*
8
+
9
+ * Renamed to riot-rails and am hoping @dasch is kind of enough to let me be an owner on the gem he originally pushed. The ol' bait and and switch. [jaknowlden]
10
+
11
+ * Added asserts_response context macro. You can also provide method names to be called against the response. [jaknowlden]
12
+
13
+ context FoosController do
14
+ setup { get "/foos" }
15
+
16
+ asserts_response.exists
17
+ asserts_response(:status).equals(200)
18
+ asserts_response(:body).matches(/Bar baz/)
19
+ end # FoosController
20
+
21
+ * Added GET support for ActionController testing, a la Rack testing methods. [jaknowlden]
22
+
23
+ context MyController do
24
+ setup { get "/my/dashboard", "foo" => "bar" }
25
+ end # MyController
26
+
27
+ * Redoing support for ActionController. Dropped everything started from scratch. Also, moved the context-helper stuff into Riot and called it ContextMiddleware. Reimplemented helpers as middleware here. [jaknowlden]
28
+
29
+ *0.0.9.pre*
30
+
31
+ * To accommodate the last change, added concept of context helpers. It's so simple you can define your own. [jaknowlden]
32
+
33
+ module RiotRails
34
+ register_context_helper do
35
+ def prepare_context(original_description, context)
36
+ # ...
37
+ context.<do-something-before-the-context-runs-if-you-want-to>
38
+ # ...
39
+ end
40
+ end
41
+ end
42
+
43
+ * Well. Aren't we special? We don't have include ActiveRecord or ActionController code if we don't want to. But if you do: [jaknowlden]
44
+
45
+ # teststrap.rb
46
+ require 'riot/activerecord' # implicit riot/rails require
47
+ require 'riot/actioncontroller' # also an implicit riot/rails require
48
+
49
+ * Added asserts_request and asserts_response context macros. They also take a possible method to call.
50
+ [jaknowlden]
51
+
52
+ rails_context UsersController do
53
+ hookup { get :show, :id => 1 }
54
+
55
+ asserts_response.kind_of(ActionController::TestResponse)
56
+ asserts_response(:body).kind_of(String)
57
+
58
+ asserts_request.kind_of(ActionController::TestRequest)
59
+ asserts_request(:cookies).kind_of(Hash)
60
+ end
61
+
62
+ * Controller assertions now no longer assume the actual value is a controller instance. Instead, the actual
63
+ value is something that should quack like a response. [jaknowlden]
64
+
65
+ * Added asserts_assigned context macro [jaknowlden]
66
+
67
+ rails_context UsersController do
68
+ hookup { get :show, :id => 1 }
69
+
70
+ asserts_assigned(:user).kind_of(User)
71
+ end
72
+
73
+ * rails_context now recognizes controllers [jaknowlden]
74
+
75
+ rails_context RoomsController do
76
+ hookup { get :index }
77
+ asserts_controller.reponse_status :ok
78
+ end
79
+
80
+ Plus, you can only use controlling within a rails_context now
81
+
82
+ rails_context "rooms" do
83
+ controlling :rooms
84
+ hookup { get :index }
85
+ asserts_controller.reponse_status :ok
86
+ end
87
+
88
+ * controlling now lets you pass a controller class reference in [jaknowlden]
89
+
90
+ rails_context "rooms" do
91
+ controlling RoomsController
92
+ end
93
+
94
+ * Reflection macros now test the options [emschwar]
95
+
96
+ rails_context Room do
97
+ asserts_topic.has_one :floor, :class_name => "Surface"
98
+ asserts_topic.has_many :walls, :join_table => "foos"
99
+ end
100
+
101
+ * Added the has_one reflection macro [emschwar]
102
+
103
+ rails_context Room do
104
+ asserts_topic.has_one :floor
105
+ end
106
+
107
+ * Added the attribute_is_invalid validation macro [jaknowlden]
108
+
109
+ rails_context Room do
110
+ hookup { topic.location = nil }
111
+ asserts_topic.attribute_is_invalid(:location, "can't be blank")
112
+ end
113
+
114
+ * Added validates_length_of assertion macro (minimalistic) [jaknowlden]
115
+
116
+ rails_context Room do
117
+ asserts_topic.validates_length_of :name, (2..36)
118
+ end
119
+
120
+ * Added transactional support to RailsContext. Disabled by default [jaknowlden]
121
+
122
+ rails_context Room do
123
+ set :transactional, true
124
+ end
125
+
126
+ * Added option enabling to RailsContext [jaknowlden]
127
+
128
+ rails_context Room do
129
+ set :this, "that"
130
+ end
131
+
132
+ * Added initial #rails_context. Assumes ActiveRecord class for now [jaknowlden]
133
+
134
+ class Room < ActiveRecord::Base
135
+ # ...
136
+ end
137
+
138
+ # TEST
139
+
140
+ rails_context Room do
141
+ asserts_topic.belongs_to(:house)
142
+ asserts_topic.validates_presence_of(:house_id)
143
+ end
144
+
145
+ * Added the #belongs_to ActiveRecord assertion macro [jaknowlden]
146
+
147
+ context "a Room" do
148
+ setup { Room.new }
149
+ asserts_topic.belongs_to(:house)
150
+ end
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Justin Knowlden, Thumble Monks
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,31 @@
1
+ # Riot Rails
2
+
3
+ [Riot](http://github.com/thumblemonks/riot) macros for Rails application testing.
4
+
5
+ LOTS more to come ...
6
+
7
+ ## Installation
8
+
9
+ We're a gem! Install per the normal course of installing gems.
10
+
11
+ gem install riot_rails
12
+
13
+ ## Usage
14
+
15
+ Tons of documentation to come. Try looking at the [RDoc](http://rdoc.info/projects/thumblemonks/riot_rails) for now. As a note, you will likely put this in your `teststrap.rb` or `test_helper.rb`:
16
+
17
+ require 'riot/rails'
18
+
19
+ ### ActiveRecord
20
+
21
+ Awesome stuff in the works. Doc coming soon.
22
+
23
+ ### ActionController
24
+
25
+ Awesome stuff in the works. Doc coming soon.
26
+
27
+ ### ActionMailer
28
+
29
+ Awesome stuff coming soon. See [Shoulda Action Mailer](http://github.com/thumblemonks/shoulda_action_mailer) - which is also by us - in the meantime.
30
+
31
+ I told you we liked Shoulda.
data/Rakefile CHANGED
@@ -1,19 +1,41 @@
1
- begin
2
- require 'rubygems'
3
- rescue LoadError
4
- puts "Can't find RubyGems..."
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ task :default => [:test]
5
+
6
+ require 'rake/testtask'
7
+ Rake::TestTask.new(:test) do |test|
8
+ test.libs << 'lib' << 'test'
9
+ test.pattern = 'test/**/*_test.rb'
10
+ test.verbose = false
5
11
  end
6
12
 
13
+ desc "Run Flog against library (except tests)"
14
+ task :flog do
15
+ puts %x[find ./lib -name *.rb | xargs flog]
16
+ end
17
+
18
+ desc "Run Roodi against library (except tests)"
19
+ task :roodi do
20
+ puts %x[find ./lib -name *.rb | xargs roodi]
21
+ end
22
+
23
+ #
24
+ # Some monks like diamonds. I like gems.
25
+
7
26
  begin
8
27
  require 'jeweler'
9
28
  Jeweler::Tasks.new do |gem|
10
29
  gem.name = "riot-rails"
11
- gem.summary = "Test your Rails apps with Riot"
12
- gem.description = "Use the fast, expressive and concise Riot unit-testing framework to test your Rails apps with."
13
- gem.email = "daniel.schierbeck@gmail.com"
14
- gem.homepage = "http://github.com/dasch/riot-rails"
15
- gem.authors = ["Daniel Schierbeck"]
16
- gem.add_dependency 'riot'
30
+ gem.summary = "Riot specific test support for Rails apps"
31
+ gem.description = "Riot specific test support for Rails apps. Protest the slow app."
32
+ gem.email = "gus@gusg.us"
33
+ gem.homepage = "http://github.com/thumblemonks/riot-rails"
34
+ gem.authors = ["Justin 'Gus' Knowlden"]
35
+ gem.add_dependency("riot", ">= 0.11.0")
36
+ gem.add_dependency("rack-test", ">= 0.5.3")
37
+ gem.add_development_dependency("activerecord", ">= 3.0.0.beta3")
38
+ gem.add_development_dependency("actionpack", ">= 3.0.0.beta3")
17
39
  end
18
40
  Jeweler::GemcutterTasks.new
19
41
  rescue LoadError
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.2.0.pre
@@ -0,0 +1,15 @@
1
+ module RiotRails
2
+ module ActionController
3
+ module AssertsResponse
4
+
5
+ def asserts_response(method_name=nil)
6
+ if method_name
7
+ asserts("response ##{method_name.to_s}") { response.send(method_name) }
8
+ else
9
+ asserts("response") { response }
10
+ end
11
+ end
12
+
13
+ end # AssertsResponse
14
+ end # ActionController
15
+ end # RiotRails
@@ -0,0 +1,48 @@
1
+ module RiotRails
2
+ class ActionControllerMiddleware < Riot::ContextMiddleware
3
+ register
4
+
5
+ def call(context)
6
+ if handle?(context)
7
+ setup_context_macros(context)
8
+ setup_situation(context)
9
+ end
10
+ middleware.call(context)
11
+ end
12
+ private
13
+ def handle?(context)
14
+ description = context.description
15
+ description.kind_of?(Class) && description.ancestors.include?(::ActionController::Base)
16
+ end
17
+
18
+ def setup_context_macros(context)
19
+ context.class_eval do
20
+ include RiotRails::ActionController::AssertsResponse
21
+ end
22
+ end
23
+
24
+ def setup_situation(context)
25
+ context.helper(:app) { @app }
26
+ context.helper(:env) { @env }
27
+ context.helper(:controller) { env["action_controller.instance"] }
28
+
29
+ context.helper(:request) do
30
+ return controller.request if controller
31
+ raise Exception, "No request made yet"
32
+ end
33
+
34
+ context.helper(:response) do
35
+ return controller.response if controller
36
+ raise Exception, "No response since no request made yet"
37
+ end
38
+
39
+ context.setup(true) do
40
+ self.class_eval { include RiotRails::ActionController::HttpMethods }
41
+ @app = ::Rails.application
42
+ http_reset
43
+ context.description
44
+ end # context.setup(true)
45
+ end # call
46
+
47
+ end # ActionControllerMiddleware
48
+ end # RiotRails
@@ -0,0 +1,19 @@
1
+ module RiotRails
2
+ module ActionController
3
+ module HttpMethods
4
+ def http_reset; @env = {}; end
5
+ def get(uri, params={}) perform_request("GET", uri, params); end
6
+ def post(uri, params={}) perform_request("POST", uri, params); end
7
+ def put(uri, params={}); perform_request("PUT", uri, params); end
8
+ def delete(uri, params={}); perform_request("DELETE", uri, params); end
9
+ private
10
+ def perform_request(request_method, uri, params)
11
+ http_reset
12
+ params = params.inject({}) { |acc,(key,val)| acc[key] = val.to_s; acc }
13
+ @env = ::Rack::MockRequest.env_for(uri, {:params => params, :method => request_method}).merge(@env)
14
+ @env['action_dispatch.show_exceptions'] = false
15
+ @app.call(@env)
16
+ end
17
+ end # HttpMethods
18
+ end # ActionController
19
+ end # RiotRails
@@ -0,0 +1,4 @@
1
+ require 'riot/rails'
2
+ require 'riot/action_controller/context_middleware'
3
+ require 'riot/action_controller/context_macros/asserts_response'
4
+ require 'riot/action_controller/http_methods'
@@ -0,0 +1,3 @@
1
+ require 'riot/active_record/validation_macros'
2
+ require 'riot/active_record/reflection_macros'
3
+ require 'riot/active_record/database_macros'
@@ -0,0 +1,17 @@
1
+ module RiotRails
2
+ class ActiveRecordMiddleware < Riot::ContextMiddleware
3
+ register
4
+
5
+ def call(context)
6
+ if handle?(context)
7
+ context.setup(true) { context.description.new }
8
+ end
9
+ middleware.call(context)
10
+ end
11
+ private
12
+ def handle?(context)
13
+ description = context.description
14
+ description.kind_of?(Class) && description.ancestors.include?(::ActiveRecord::Base)
15
+ end
16
+ end # ActiveRecordMiddleware
17
+ end # RiotRails
@@ -0,0 +1,58 @@
1
+ module RiotRails
2
+ module ActiveRecord
3
+
4
+ # An ActiveRecord assertion macro that looks for an index on a given set of attributes in the table
5
+ # used by the model under test (aka: topic).
6
+ #
7
+ # asserts_topic.has_database_index_on :name
8
+ # asserts_topic.has_database_index_on :email, :group_name
9
+ #
10
+ # In the form used above, the assertion will pass if any index is found with the attributes listed
11
+ # (unique or not). To be specific about uniqueness, provide the +:unique+ option.
12
+ #
13
+ # asserts_topic.has_database_index_on :email, :unique => true
14
+ # asserts_topic.has_database_index_on :name, :unique => false
15
+ #
16
+ # The last example will require that the index not be a unique one.
17
+ class HasDatabaseIndexOnMacro < Riot::AssertionMacro
18
+ register :has_database_index_on
19
+
20
+ def initialize(database_connection=nil) # Good for testing :)
21
+ @database_connection = database_connection
22
+ end
23
+
24
+ def evaluate(actual, *attributes_and_options)
25
+ attributes, options = extract_options_from(attributes_and_options)
26
+ unique = options[:unique]
27
+
28
+ index = find_index(actual, attributes, unique)
29
+
30
+ static_message = "#{unique ? "unique" : ""} index on #{attributes.inspect}".strip
31
+ index.nil? ? fail("expected #{static_message}") : pass("has #{static_message}")
32
+ end
33
+ private
34
+ def database_connection; @database_connection || ::ActiveRecord::Base.connection; end
35
+
36
+ def find_index(model, attributes, uniqueness, &block)
37
+ database_connection.indexes(model.class.table_name).find do |the_index|
38
+ unique_enough?(uniqueness, the_index) && attributes_match?(attributes, the_index)
39
+ end
40
+ end
41
+
42
+ def unique_enough?(uniqueness, index)
43
+ return true if uniqueness.nil?
44
+ index.unique == uniqueness
45
+ end
46
+
47
+ def attributes_match?(attributes, index)
48
+ Set.new(attributes.map(&:to_s)) == Set.new(index.columns)
49
+ end
50
+
51
+ def extract_options_from(attributes)
52
+ options = attributes.last.kind_of?(Hash) ? attributes.pop : {}
53
+ [attributes, options]
54
+ end
55
+ end
56
+
57
+ end # ActiveRecord
58
+ end # RiotRails
@@ -0,0 +1,106 @@
1
+ module RiotRails
2
+ module ActiveRecord
3
+ protected
4
+
5
+ class ReflectionAssertionMacro < Riot::AssertionMacro
6
+ private
7
+ def reflection_match?(expected, reflection)
8
+ !reflection.nil? && (expected == reflection.macro.to_s)
9
+ end
10
+
11
+ def options_match_for_reflection?(reflection, options)
12
+ options.all? { |k, v| reflection.options[k] == v }
13
+ end
14
+
15
+ def assert_reflection(expected, record, attribute, options=nil)
16
+ options ||= {}
17
+ reflection = record.class.reflect_on_association(attribute)
18
+ if reflection_match?(expected, reflection)
19
+ if options_match_for_reflection?(reflection, options)
20
+ pass new_message(attribute).is_a.push(expected).association
21
+ else
22
+ fail expected_message.push("#{expected} #{attribute.inspect}").with(options)
23
+ end
24
+ else
25
+ fail new_message(attribute).is_not_a.push(expected).association
26
+ end
27
+ end
28
+ end
29
+
30
+ public
31
+
32
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a +has_many+
33
+ # association. Will fail if an association is not defined for the attribute or if the association is
34
+ # not +has_many+.
35
+ #
36
+ # context "a Room" do
37
+ # setup { Room.new }
38
+ #
39
+ # asserts_topic.has_many(:doors)
40
+ # asserts_topic.has_many(:floors) # should probably fail given our current universe :)
41
+ # end
42
+ class HasManyMacro < ReflectionAssertionMacro
43
+ register :has_many
44
+
45
+ def evaluate(actual, *expectings)
46
+ attribute, options = *expectings
47
+ assert_reflection("has_many", actual, attribute, options)
48
+ end
49
+ end
50
+
51
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a
52
+ # +has_one+ association. Will fail if an association is not defined for the attribute or if the
53
+ # association is not +has_one+.
54
+ #
55
+ # context "a Room" do
56
+ # setup { Room.new }
57
+ #
58
+ # asserts_topic.has_one(:floor)
59
+ # end
60
+ class HasOneMacro < ReflectionAssertionMacro
61
+ register :has_one
62
+
63
+ def evaluate(actual, *expectings)
64
+ attribute, options = *expectings
65
+ assert_reflection("has_one", actual, attribute, options)
66
+ end
67
+ end
68
+
69
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a
70
+ # +belongs_to+ association. Will fail if an association is not defined for the attribute or if the
71
+ # association is not +belongs_to+.
72
+ #
73
+ # context "a Room" do
74
+ # setup { Room.new }
75
+ #
76
+ # asserts_topic.belongs_to(:house)
77
+ # end
78
+ class BelongsToMacro < ReflectionAssertionMacro
79
+ register :belongs_to
80
+
81
+ def evaluate(actual, *expectings)
82
+ attribute, options = *expectings
83
+ assert_reflection("belongs_to", actual, attribute, options)
84
+ end
85
+ end
86
+
87
+ # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as a
88
+ # +has_and_belongs_to_many+ association. Will fail if an association is not defined for the attribute or
89
+ # if the association is not +has_and_belongs_to_many+.
90
+ #
91
+ # context "a Room" do
92
+ # setup { Room.new }
93
+ #
94
+ # asserts_topic.has_and_belongs_to_many(:walls)
95
+ # end
96
+ class HasAndBelongsToManyMacro < ReflectionAssertionMacro
97
+ register :has_and_belongs_to_many
98
+
99
+ def evaluate(actual, *expectings)
100
+ attribute, options = *expectings
101
+ assert_reflection("has_and_belongs_to_many", actual, attribute, options)
102
+ end
103
+ end
104
+
105
+ end # ActiveRecord
106
+ end # RiotRails
@@ -0,0 +1,28 @@
1
+ module RiotRails
2
+ class TransactionalMiddleware < Riot::ContextMiddleware
3
+ register
4
+
5
+ def call(context)
6
+ middleware.call(context)
7
+ hijack_local_run(context) if handle?(context)
8
+ end
9
+ private
10
+ def handle?(context)
11
+ context.option(:transactional) == true
12
+ end
13
+
14
+ # Don't you just love mr. metaclass?
15
+ def hijack_local_run(context)
16
+ (class << context; self; end).class_eval do
17
+ alias_method :transactionless_local_run, :local_run
18
+ def local_run(*args)
19
+ ::ActiveRecord::Base.transaction do
20
+ transactionless_local_run(*args)
21
+ raise ::ActiveRecord::Rollback
22
+ end
23
+ end # local_run
24
+ end # metaclass
25
+ end # hijack_local_run
26
+
27
+ end # TransactionalMiddleware
28
+ end # RiotRails