riot_rails 0.0.8 → 0.0.9.pre

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 (40) hide show
  1. data/CHANGELOG +122 -0
  2. data/MIT-LICENSE +1 -1
  3. data/README.markdown +22 -7
  4. data/Rakefile +3 -2
  5. data/VERSION +1 -1
  6. data/lib/riot/action_controller/assertion_macros.rb +48 -34
  7. data/lib/riot/action_controller/context_helper.rb +12 -0
  8. data/lib/riot/action_controller/context_macros.rb +58 -12
  9. data/lib/riot/action_controller/situation_macros.rb +73 -6
  10. data/lib/riot/action_controller.rb +2 -0
  11. data/lib/riot/active_record/assertion_macros.rb +3 -158
  12. data/lib/riot/active_record/context_helper.rb +19 -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/validation_macros.rb +187 -0
  16. data/lib/riot/active_record.rb +2 -0
  17. data/lib/riot/rails.rb +1 -2
  18. data/lib/riot/rails_context.rb +84 -0
  19. data/riot_rails.gemspec +35 -9
  20. data/test/action_controller/controller_context_test.rb +39 -9
  21. data/test/action_controller/redirected_to_test.rb +6 -11
  22. data/test/action_controller/renders_template_test.rb +7 -7
  23. data/test/action_controller/renders_test.rb +6 -6
  24. data/test/action_controller/response_status_test.rb +11 -11
  25. data/test/active_record/allowing_values_test.rb +9 -9
  26. data/test/active_record/attribute_is_invalid_test.rb +20 -0
  27. data/test/active_record/belongs_to_test.rb +22 -0
  28. data/test/active_record/has_and_belongs_to_many_test.rb +22 -0
  29. data/test/active_record/has_database_index_on_test.rb +73 -0
  30. data/test/active_record/has_many_test.rb +7 -3
  31. data/test/active_record/has_one_test.rb +22 -0
  32. data/test/active_record/validates_length_of_test.rb +31 -0
  33. data/test/active_record/validates_presence_of_test.rb +1 -1
  34. data/test/active_record/validates_uniqueness_of_test.rb +3 -3
  35. data/test/rails_context_test.rb +103 -0
  36. data/test/rails_root/config/environment.rb +46 -0
  37. data/test/rails_root/config/routes.rb +3 -4
  38. data/test/rails_root/db/schema.rb +1 -2
  39. data/test/teststrap.rb +44 -69
  40. metadata +39 -6
@@ -1,158 +1,3 @@
1
- module RiotRails
2
- module ActiveRecord
3
-
4
- class AssertionMacro < ::Riot::AssertionMacro
5
- def error_from_writing_value(model, attribute, value)
6
- model.write_attribute(attribute, value)
7
- model.valid?
8
- model.errors.on(attribute)
9
- end
10
- end
11
-
12
- # An ActiveRecord assertion that expects to fail when a given attribute is validated after a nil value
13
- # is provided to it.
14
- #
15
- # context "a User" do
16
- # setup { User.new }
17
- # topic.validates_presence_of(:name)
18
- # end
19
- class ValidatesPresenceOfMacro < AssertionMacro
20
- register :validates_presence_of
21
-
22
- def evaluate(actual, attribute)
23
- if error_from_writing_value(actual, attribute, nil)
24
- pass("validates presence of #{attribute.inspect}")
25
- else
26
- fail("expected to validate presence of #{attribute.inspect}")
27
- end
28
- end
29
- end
30
-
31
- # An ActiveRecord assertion that expects to pass with a given value or set of values for a given
32
- # attribute.
33
- #
34
- # context "a User" do
35
- # setup { User.new }
36
- # topic.allows_values_for :email, "a@b.cd"
37
- # topic.allows_values_for :email, "a@b.cd", "e@f.gh"
38
- # end
39
- class AllowsValuesForMacro < AssertionMacro
40
- register :allows_values_for
41
-
42
- def evaluate(actual, attribute, *values)
43
- bad_values = []
44
- values.each do |value|
45
- bad_values << value if error_from_writing_value(actual, attribute, value)
46
- end
47
- failure_msg = "expected %s to allow value(s) %s"
48
- bad_values.empty? ? pass : fail(failure_msg % [attribute.inspect, bad_values.inspect])
49
- end
50
- end
51
-
52
- # An ActiveRecord assertion that expects to fail with a given value or set of values for a given
53
- # attribute.
54
- #
55
- # context "a User" do
56
- # setup { User.new }
57
- # topic.does_not_allow_values_for :email, "a"
58
- # topic.does_not_allow_values_for :email, "a@b", "e f@g.h"
59
- # end
60
- class DoesNotAllowValuesForMacro < AssertionMacro
61
- register :does_not_allow_values_for
62
- def evaluate(actual, attribute, *values)
63
- good_values = []
64
- values.each do |value|
65
- good_values << value unless error_from_writing_value(actual, attribute, value)
66
- end
67
- failure_msg = "expected %s not to allow value(s) %s"
68
- good_values.empty? ? pass : fail(failure_msg % [attribute.inspect, good_values.inspect])
69
- end
70
- end
71
-
72
- # An ActiveRecord assertion that expects to fail with invalid value for an attribute. Optionally, the
73
- # error message can be provided as the exact string or as regular expression.
74
- #
75
- # context "a User" do
76
- # setup { User.new }
77
- # topic.is_invalid_when :email, "fake", "is invalid"
78
- # topic.is_invalid_when :email, "another fake", /invalid/
79
- # end
80
- class IsInvalidWhenMacro < AssertionMacro
81
- register :is_invalid_when
82
-
83
- def evaluate(actual, attribute, value, expected_error=nil)
84
- actual_errors = Array(error_from_writing_value(actual, attribute, value))
85
- if actual_errors.empty?
86
- fail("expected #{attribute.inspect} to be invalid when value is #{value.inspect}")
87
- elsif expected_error && !has_error_message?(expected_error, actual_errors)
88
- message = "expected %s to be invalid with error message %s"
89
- fail(message % [attribute.inspect, expected_error.inspect])
90
- else
91
- pass("attribute #{attribute.inspect} is invalid")
92
- end
93
- end
94
- private
95
- def has_error_message?(expected, errors)
96
- return true unless expected
97
- expected.kind_of?(Regexp) ? errors.any? {|e| e =~ expected } : errors.any? {|e| e == expected }
98
- end
99
- end
100
-
101
- # An ActiveRecord assertion that expects to fail with an attribute is not valid for record because the
102
- # value of the attribute is not unique. Requires the topic of the context to be a created record; one
103
- # that returns false for a call to +new_record?+.
104
- #
105
- # context "a User" do
106
- # setup { User.create(:email => "a@b.cde", ... ) }
107
- # topic.validates_uniqueness_of :email
108
- # end
109
- class ValidatesUniquenessOfMacro < AssertionMacro
110
- register :validates_uniqueness_of
111
-
112
- def evaluate(actual, attribute)
113
- actual_record = actual
114
- if actual_record.new_record?
115
- fail("topic is not a new record when testing uniqueness of #{attribute.inspect}")
116
- else
117
- copied_model = actual_record.class.new
118
- actual_record.attributes.each do |dup_attribute, dup_value|
119
- copied_model.write_attribute(dup_attribute, dup_value)
120
- end
121
- copied_value = actual_record.read_attribute(attribute)
122
- if error_from_writing_value(copied_model, attribute, copied_value)
123
- pass("#{attribute.inspect} is unique")
124
- else
125
- fail("expected to fail because #{attribute.inspect} is not unique")
126
- end
127
- end
128
- end
129
- end
130
-
131
- # An ActiveRecord assertion macro that expects to pass when a given attribute is defined as +has_many+
132
- # association. Will fail if an association is not defined for the attribute and if the association is
133
- # not +has_many.jekyll
134
- #
135
- # context "a Room" do
136
- # setup { Room.new }
137
- #
138
- # topic.has_many(:doors)
139
- # topic.has_many(:floors) # should probably fail given our current universe :)
140
- # end
141
- class HasManyMacro < AssertionMacro
142
- register :has_many
143
-
144
- def evaluate(actual, attribute)
145
- reflection = actual.class.reflect_on_association(attribute)
146
- static_msg = "expected #{attribute.inspect} to be a has_many association, but was "
147
- if reflection.nil?
148
- fail(static_msg + "not")
149
- elsif "has_many" != reflection.macro.to_s
150
- fail(static_msg + "a #{reflection.macro} instead")
151
- else
152
- pass("has many #{attribute.inspect}")
153
- end
154
- end
155
- end
156
-
157
- end # ActiveRecord
158
- end # RiotRails
1
+ require 'riot/active_record/validation_macros'
2
+ require 'riot/active_record/reflection_macros'
3
+ require 'riot/active_record/database_macros'
@@ -0,0 +1,19 @@
1
+ module RiotRails
2
+ register_context_helper do
3
+ def prepare_context(context)
4
+ context.premium_setup { context.description.new } if active_record_context?(context)
5
+
6
+ context.transaction do |&original_block|
7
+ ::ActiveRecord::Base.transaction do
8
+ original_block.call
9
+ raise ::ActiveRecord::Rollback
10
+ end
11
+ end
12
+ end
13
+ private
14
+ def active_record_context?(context)
15
+ description = context.description
16
+ description.kind_of?(Class) && description.ancestors.include?(::ActiveRecord::Base)
17
+ end
18
+ end
19
+ 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,187 @@
1
+ module RiotRails
2
+ module ActiveRecord
3
+ protected
4
+
5
+ class ValidationAssertionMacro < Riot::AssertionMacro
6
+ private
7
+ def errors_from_writing_value(model, attribute, value)
8
+ # TODO: Fix me to not use __send__
9
+ model.__send__(:write_attribute, attribute, value)
10
+ model.valid?
11
+ model.errors[attribute]
12
+ end
13
+
14
+ def errors_from_writing_value?(*args)
15
+ errors_from_writing_value(*args).any?
16
+ end
17
+ end
18
+
19
+ public
20
+
21
+ # An ActiveRecord assertion that expects to pass with a given value or set of values for a given
22
+ # attribute.
23
+ #
24
+ # rails_context User do
25
+ # asserts_topic.allows_values_for :email, "a@b.cd"
26
+ # asserts_topic.allows_values_for :email, "a@b.cd", "e@f.gh"
27
+ # end
28
+ class AllowsValuesForMacro < ValidationAssertionMacro
29
+ register :allows_values_for
30
+
31
+ def evaluate(actual, attribute, *values)
32
+ bad_values = []
33
+ values.each do |value|
34
+ bad_values << value if errors_from_writing_value?(actual, attribute, value)
35
+ end
36
+ bad_values.empty? ? pass : fail(expected_message(attribute).to_allow_values(bad_values))
37
+ end
38
+ end
39
+
40
+ # An ActiveRecord assertion that expects to fail with a given value or set of values for a given
41
+ # attribute.
42
+ #
43
+ # rails_context User do
44
+ # asserts_topic.does_not_allow_values_for :email, "a"
45
+ # asserts_topic.does_not_allow_values_for :email, "a@b", "e f@g.h"
46
+ # end
47
+ class DoesNotAllowValuesForMacro < ValidationAssertionMacro
48
+ register :does_not_allow_values_for
49
+ def evaluate(actual, attribute, *values)
50
+ good_values = []
51
+ values.each do |value|
52
+ good_values << value unless errors_from_writing_value?(actual, attribute, value)
53
+ end
54
+ good_values.empty? ? pass : fail(expected_message(attribute).not_to_allow_values(good_values))
55
+ end
56
+ end
57
+
58
+ # An ActiveRecord assertion that expects to fail with invalid value for an attribute. Optionally, the
59
+ # error message can be provided as the exact string or as regular expression.
60
+ #
61
+ # rails_context User do
62
+ # asserts_topic.is_invalid_when :email, "fake", "is invalid"
63
+ # asserts_topic.is_invalid_when :email, "another fake", /invalid/
64
+ # end
65
+ class IsInvalidWhenMacro < ValidationAssertionMacro
66
+ register :is_invalid_when
67
+
68
+ def evaluate(actual, attribute, value, expected_error=nil)
69
+ actual_errors = errors_from_writing_value(actual, attribute, value)
70
+ if actual_errors.empty?
71
+ fail expected_message(attribute).to_be_invalid_when_value_is(value)
72
+ elsif expected_error && !has_error_message?(expected_error, actual_errors)
73
+ fail expected_message(attribute).to_be_invalid_with_error_message(expected_error)
74
+ else
75
+ pass new_message.attribute(attribute).is_invalid
76
+ end
77
+ end
78
+ private
79
+ def has_error_message?(expected, errors)
80
+ return true unless expected
81
+ expected.kind_of?(Regexp) ? errors.any? {|e| e =~ expected } : errors.any? {|e| e == expected }
82
+ end
83
+ end
84
+
85
+ # An ActiveRecord assertion that expects to fail when a given attribute is validated after a nil value
86
+ # is provided to it.
87
+ #
88
+ # rails_context User do
89
+ # asserts_topic.validates_presence_of(:name)
90
+ # end
91
+ class ValidatesPresenceOfMacro < ValidationAssertionMacro
92
+ register :validates_presence_of
93
+
94
+ def evaluate(actual, attribute)
95
+ if errors_from_writing_value?(actual, attribute, nil)
96
+ pass new_message.validates_presence_of(attribute)
97
+ else
98
+ fail expected_message.to_validate_presence_of(attribute)
99
+ end
100
+ end
101
+ end
102
+
103
+ # An ActiveRecord assertion that expects to fail with an attribute is not valid for record because the
104
+ # value of the attribute is not unique. Requires the topic of the context to be a created record; one
105
+ # that returns false for a call to +new_record?+.
106
+ #
107
+ # rails_context User do
108
+ # setup { User.create(:email => "a@b.cde", ... ) }
109
+ # asserts_topic.validates_uniqueness_of :email
110
+ # end
111
+ class ValidatesUniquenessOfMacro < ValidationAssertionMacro
112
+ register :validates_uniqueness_of
113
+
114
+ def evaluate(actual, attribute)
115
+ actual_record = actual
116
+ if actual_record.new_record?
117
+ fail new_message.must_use_a_persisted_record_when_testing_uniqueness_of(attribute)
118
+ else
119
+ copied_model = actual_record.class.new
120
+ actual_record.attributes.each do |dup_attribute, dup_value|
121
+ copied_model.__send__(:write_attribute, dup_attribute, dup_value)
122
+ end
123
+ copied_value = actual_record.__send__(:read_attribute, attribute)
124
+ if errors_from_writing_value?(copied_model, attribute, copied_value)
125
+ pass new_message(attribute).is_unique
126
+ else
127
+ fail expected_message.to_fail_because(attribute).is_not_unique
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ # An ActiveRecord assertion to test that the length of the value of an attribute must be within a
134
+ # specified range. Assertion fails if: there are errors when min/max characters are used, there are no
135
+ # errors when min-1/max+1 characters are used.
136
+ #
137
+ # rails_context User do
138
+ # asserts_topic.validates_length_of :name, (2..36)
139
+ # end
140
+ #
141
+ # TODO: allow for options on what to validate
142
+ class ValidatesLengthOfMacro < ValidationAssertionMacro
143
+ register :validates_length_of
144
+
145
+ def evaluate(actual, attribute, range)
146
+ min, max = range.first, range.last
147
+ [min, max].each do |length|
148
+ if errors_from_writing_value?(actual, attribute, "r" * length)
149
+ return fail(new_message(attribute).should_be_able_to_be(length).characters)
150
+ end
151
+ end
152
+
153
+ if (min-1) > 0 && !errors_from_writing_value?(actual, attribute, "r" * (min-1))
154
+ fail new_message(attribute).expected_to_be_more_than(min - 1).characters
155
+ elsif !errors_from_writing_value?(actual, attribute, "r" * (max+1))
156
+ fail new_message(attribute).expected_to_be_less_than(max + 1).characters
157
+ else
158
+ pass new_message.validates_length_of(attribute).is_within(range)
159
+ end
160
+ end
161
+ end
162
+
163
+ # An ActiveRecord assertion to test that an attribute is invalid along with the expected error message
164
+ # (or error message partial). The assumption is that a value has already been set.
165
+ #
166
+ # rails_context User do
167
+ # hookup { topic.bio = "I'm a goofy clown" }
168
+ # asserts_topic.attribute_is_invalid :bio, "cannot contain adjectives"
169
+ # end
170
+ class AttributeIsInvalidMacro < ValidationAssertionMacro
171
+ register :attribute_is_invalid
172
+
173
+ def evaluate(actual, attribute, error_message)
174
+ actual.valid?
175
+ errors = actual.errors[attribute]
176
+ if errors.empty?
177
+ fail new_message(attribute).expected_to_be_invalid
178
+ elsif errors.include?(error_message)
179
+ pass new_message(attribute).is_invalid
180
+ else
181
+ fail new_message(attribute).is_invalid.but(error_message).is_not_a_valid_error_message
182
+ end
183
+ end
184
+ end
185
+
186
+ end # ActiveRecord
187
+ end # RiotRails
@@ -1 +1,3 @@
1
+ require 'riot/rails'
1
2
  require 'riot/active_record/assertion_macros'
3
+ require 'riot/active_record/context_helper'
data/lib/riot/rails.rb CHANGED
@@ -1,3 +1,2 @@
1
1
  require 'riot'
2
- require 'riot/active_record'
3
- require 'riot/action_controller'
2
+ require 'riot/rails_context'
@@ -0,0 +1,84 @@
1
+ module RiotRails
2
+ def self.helpers; @helpers ||= []; end
3
+ def self.register_context_helper(&handler_block)
4
+ helpers << Class.new(&handler_block).new
5
+ end
6
+
7
+ class RailsContext < Riot::Context
8
+ def initialize(description, parent=nil, &definition)
9
+ @options = {:transactional => false}
10
+ transaction { |&default_block| raise Exception, "No transaction handler" }
11
+ super(description, parent, &definition)
12
+ apply_helpers
13
+ end
14
+
15
+ # We're going to allow any helper to help out. Not just one.
16
+ def apply_helpers
17
+ RiotRails.helpers.each { |helper| helper.prepare_context(self) }
18
+ end
19
+
20
+ # Set options for the specific rails_context. For instance, you can tell the context to be transactional
21
+ #
22
+ # rails_context "Foo" do
23
+ # set :transactional, true
24
+ # end
25
+ def set(property, value) options[property] = value; end
26
+
27
+ # Technically, this is a secret ... but whatever. It's ruby. Basically, just make this setup more
28
+ # important than any of the others.
29
+ #
30
+ # Yes I can. See ... I just did!
31
+ def premium_setup(&definition)
32
+ @setups.unshift(::Riot::Setup.new(&definition)).first;
33
+ end
34
+
35
+ # Returns true if current context or a parent context has the transactional option enabled. To enable,
36
+ # anywhere within a rails_context or a child context thereof, do:
37
+ #
38
+ # rails_context Foo do
39
+ # context "sub context" do
40
+ # set :transactional, true
41
+ # end
42
+ # end
43
+ #
44
+ # Transactional support is disabled by default. When enabled, all transactions are rolled back when the
45
+ # context is done executing.
46
+ def transactional?
47
+ options[:transactional] || (parent.respond_to?(:transactional?) && parent.transactional?)
48
+ end
49
+
50
+ # TODO: cleanup how we handle transactions and context helpers
51
+ def transaction(&block) @transaction_block = block; end
52
+
53
+ def local_run(*args)
54
+ transactional? ? @transaction_block.call { super(*args) } : super
55
+ end
56
+ private
57
+ attr_reader :options
58
+ end
59
+
60
+ module Root
61
+ # Things an Object needs at the root level
62
+ #
63
+ # rails_context SomeKindOfClass do
64
+ # end
65
+ def rails_context(description, &definition)
66
+ context(description, RailsContext, &definition)
67
+ end
68
+ end # Root
69
+
70
+ module Context
71
+ # Things a running context needs
72
+ #
73
+ # context "Something" do
74
+ # rails_context SomeKindOfClass do
75
+ # end
76
+ # end
77
+ def rails_context(description, &definition)
78
+ new_context(description, RailsContext, &definition)
79
+ end
80
+ end # Context
81
+ end # RiotRails
82
+
83
+ Object.instance_eval { include RiotRails::Root }
84
+ Riot::Context.instance_eval { include RiotRails::Context }