mr 0.35.2

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 (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +22 -0
  5. data/README.md +29 -0
  6. data/bench/all.rb +4 -0
  7. data/bench/factory.rb +68 -0
  8. data/bench/fake_record.rb +174 -0
  9. data/bench/model.rb +201 -0
  10. data/bench/read_model.rb +191 -0
  11. data/bench/results/factory.txt +21 -0
  12. data/bench/results/fake_record.txt +37 -0
  13. data/bench/results/model.txt +44 -0
  14. data/bench/results/read_model.txt +46 -0
  15. data/bench/setup.rb +132 -0
  16. data/lib/mr.rb +11 -0
  17. data/lib/mr/after_commit.rb +49 -0
  18. data/lib/mr/after_commit/fake_record.rb +39 -0
  19. data/lib/mr/after_commit/record.rb +48 -0
  20. data/lib/mr/after_commit/record_procs_methods.rb +82 -0
  21. data/lib/mr/factory.rb +82 -0
  22. data/lib/mr/factory/config.rb +240 -0
  23. data/lib/mr/factory/model_factory.rb +103 -0
  24. data/lib/mr/factory/model_stack.rb +28 -0
  25. data/lib/mr/factory/read_model_factory.rb +104 -0
  26. data/lib/mr/factory/record_factory.rb +130 -0
  27. data/lib/mr/factory/record_stack.rb +219 -0
  28. data/lib/mr/fake_query.rb +53 -0
  29. data/lib/mr/fake_record.rb +58 -0
  30. data/lib/mr/fake_record/associations.rb +257 -0
  31. data/lib/mr/fake_record/attributes.rb +168 -0
  32. data/lib/mr/fake_record/persistence.rb +116 -0
  33. data/lib/mr/json_field.rb +180 -0
  34. data/lib/mr/json_field/fake_record.rb +31 -0
  35. data/lib/mr/json_field/record.rb +38 -0
  36. data/lib/mr/model.rb +67 -0
  37. data/lib/mr/model/associations.rb +161 -0
  38. data/lib/mr/model/configuration.rb +67 -0
  39. data/lib/mr/model/fields.rb +177 -0
  40. data/lib/mr/model/persistence.rb +79 -0
  41. data/lib/mr/query.rb +126 -0
  42. data/lib/mr/read_model.rb +83 -0
  43. data/lib/mr/read_model/data.rb +38 -0
  44. data/lib/mr/read_model/fields.rb +218 -0
  45. data/lib/mr/read_model/query_expression.rb +188 -0
  46. data/lib/mr/read_model/querying.rb +214 -0
  47. data/lib/mr/read_model/set_querying.rb +82 -0
  48. data/lib/mr/read_model/subquery.rb +98 -0
  49. data/lib/mr/record.rb +35 -0
  50. data/lib/mr/test_helpers.rb +229 -0
  51. data/lib/mr/type_converter.rb +85 -0
  52. data/lib/mr/version.rb +3 -0
  53. data/log/.gitkeep +0 -0
  54. data/mr.gemspec +29 -0
  55. data/test/helper.rb +21 -0
  56. data/test/support/db.rb +10 -0
  57. data/test/support/factory.rb +13 -0
  58. data/test/support/factory/area.rb +6 -0
  59. data/test/support/factory/comment.rb +14 -0
  60. data/test/support/factory/image.rb +6 -0
  61. data/test/support/factory/user.rb +6 -0
  62. data/test/support/models/area.rb +58 -0
  63. data/test/support/models/comment.rb +60 -0
  64. data/test/support/models/image.rb +53 -0
  65. data/test/support/models/user.rb +96 -0
  66. data/test/support/read_model/querying.rb +150 -0
  67. data/test/support/read_models/comment_with_user_data.rb +27 -0
  68. data/test/support/read_models/set_data.rb +49 -0
  69. data/test/support/read_models/subquery_data.rb +41 -0
  70. data/test/support/read_models/user_with_area_data.rb +15 -0
  71. data/test/support/schema.rb +39 -0
  72. data/test/support/setup_test_db.rb +10 -0
  73. data/test/system/factory/model_factory_tests.rb +87 -0
  74. data/test/system/factory/model_stack_tests.rb +30 -0
  75. data/test/system/factory/record_factory_tests.rb +84 -0
  76. data/test/system/factory/record_stack_tests.rb +51 -0
  77. data/test/system/factory_tests.rb +32 -0
  78. data/test/system/read_model_tests.rb +199 -0
  79. data/test/system/with_model_tests.rb +275 -0
  80. data/test/unit/after_commit/fake_record_tests.rb +110 -0
  81. data/test/unit/after_commit/record_procs_methods_tests.rb +177 -0
  82. data/test/unit/after_commit/record_tests.rb +134 -0
  83. data/test/unit/after_commit_tests.rb +113 -0
  84. data/test/unit/factory/config_tests.rb +651 -0
  85. data/test/unit/factory/model_factory_tests.rb +473 -0
  86. data/test/unit/factory/model_stack_tests.rb +97 -0
  87. data/test/unit/factory/read_model_factory_tests.rb +195 -0
  88. data/test/unit/factory/record_factory_tests.rb +446 -0
  89. data/test/unit/factory/record_stack_tests.rb +549 -0
  90. data/test/unit/factory_tests.rb +213 -0
  91. data/test/unit/fake_query_tests.rb +137 -0
  92. data/test/unit/fake_record/associations_tests.rb +585 -0
  93. data/test/unit/fake_record/attributes_tests.rb +265 -0
  94. data/test/unit/fake_record/persistence_tests.rb +239 -0
  95. data/test/unit/fake_record_tests.rb +106 -0
  96. data/test/unit/json_field/fake_record_tests.rb +75 -0
  97. data/test/unit/json_field/record_tests.rb +80 -0
  98. data/test/unit/json_field_tests.rb +302 -0
  99. data/test/unit/model/associations_tests.rb +346 -0
  100. data/test/unit/model/configuration_tests.rb +92 -0
  101. data/test/unit/model/fields_tests.rb +278 -0
  102. data/test/unit/model/persistence_tests.rb +114 -0
  103. data/test/unit/model_tests.rb +137 -0
  104. data/test/unit/query_tests.rb +300 -0
  105. data/test/unit/read_model/data_tests.rb +56 -0
  106. data/test/unit/read_model/fields_tests.rb +416 -0
  107. data/test/unit/read_model/query_expression_tests.rb +381 -0
  108. data/test/unit/read_model/querying_tests.rb +613 -0
  109. data/test/unit/read_model/set_querying_tests.rb +149 -0
  110. data/test/unit/read_model/subquery_tests.rb +242 -0
  111. data/test/unit/read_model_tests.rb +187 -0
  112. data/test/unit/record_tests.rb +45 -0
  113. data/test/unit/test_helpers_tests.rb +431 -0
  114. data/test/unit/type_converter_tests.rb +207 -0
  115. metadata +285 -0
@@ -0,0 +1,214 @@
1
+ require 'much-plugin'
2
+ require 'mr/read_model/query_expression'
3
+ require 'mr/record'
4
+ require 'mr/query'
5
+
6
+ module MR::ReadModel
7
+
8
+ module Querying
9
+ include MuchPlugin
10
+
11
+ plugin_included do
12
+ extend ClassMethods
13
+ end
14
+
15
+ module ClassMethods
16
+
17
+ def relation
18
+ @relation ||= Relation.new
19
+ end
20
+
21
+ def record_class
22
+ self.relation.from_record_class
23
+ end
24
+
25
+ def find(id, params = nil)
26
+ self.new(self.relation.build_for_find(id, params || {}).first!)
27
+ end
28
+
29
+ def query(params = nil)
30
+ MR::Query.new(self, self.relation.build_for_all(params || {}))
31
+ end
32
+
33
+ def count(*args)
34
+ self.query(*args).count
35
+ end
36
+
37
+ def build_sql(params = nil)
38
+ self.relation.build_sql(params)
39
+ end
40
+
41
+ def find_attr(column = nil)
42
+ self.relation.find_attr = column if !column.nil?
43
+ self.relation.find_attr
44
+ end
45
+
46
+ def select(*args, &block)
47
+ add_query_expression(:select, *args, &block)
48
+ rescue InvalidQueryExpressionError, ArgumentError => exception
49
+ raise ArgumentError, exception.message, caller
50
+ end
51
+
52
+ def from(record_class)
53
+ relation.from(record_class)
54
+ rescue ArgumentError => exception
55
+ raise ArgumentError, exception.message, caller
56
+ end
57
+
58
+ def from_subquery(&block)
59
+ relation.from_subquery(&block)
60
+ rescue ArgumentError => exception
61
+ raise ArgumentError, exception.message, caller
62
+ end
63
+
64
+ def joins(*args, &block)
65
+ add_query_expression(:joins, *args, &block)
66
+ rescue InvalidQueryExpressionError, ArgumentError => exception
67
+ raise ArgumentError, exception.message, caller
68
+ end
69
+
70
+ def inner_join_subquery(&block)
71
+ add_subquery_expression(:joins, :inner, &block)
72
+ rescue InvalidQueryExpressionError, ArgumentError => exception
73
+ raise ArgumentError, exception.message, caller
74
+ end
75
+
76
+ def left_outer_join_subquery(&block)
77
+ add_subquery_expression(:joins, :left, &block)
78
+ rescue InvalidQueryExpressionError, ArgumentError => exception
79
+ raise ArgumentError, exception.message, caller
80
+ end
81
+ alias :left_join_subquery :left_outer_join_subquery
82
+
83
+ def right_outer_join_subquery(&block)
84
+ add_subquery_expression(:joins, :right, &block)
85
+ rescue InvalidQueryExpressionError, ArgumentError => exception
86
+ raise ArgumentError, exception.message, caller
87
+ end
88
+ alias :right_join_subquery :right_outer_join_subquery
89
+
90
+ def full_outer_join_subquery(&block)
91
+ add_subquery_expression(:joins, :full, &block)
92
+ rescue InvalidQueryExpressionError, ArgumentError => exception
93
+ raise ArgumentError, exception.message, caller
94
+ end
95
+ alias :full_join_subquery :full_outer_join_subquery
96
+
97
+ def where(*args, &block)
98
+ add_merge_query_expression(:where, *args, &block)
99
+ rescue InvalidQueryExpressionError, ArgumentError => exception
100
+ raise ArgumentError, exception.message, caller
101
+ end
102
+
103
+ def order(*args, &block)
104
+ add_merge_query_expression(:order, *args, &block)
105
+ rescue InvalidQueryExpressionError, ArgumentError => exception
106
+ raise ArgumentError, exception.message, caller
107
+ end
108
+
109
+ def group(*args, &block)
110
+ add_query_expression(:group, *args, &block)
111
+ rescue InvalidQueryExpressionError, ArgumentError => exception
112
+ raise ArgumentError, exception.message, caller
113
+ end
114
+
115
+ def having(*args, &block)
116
+ add_query_expression(:having, *args, &block)
117
+ rescue InvalidQueryExpressionError, ArgumentError => exception
118
+ raise ArgumentError, exception.message, caller
119
+ end
120
+
121
+ def limit(*args, &block)
122
+ add_query_expression(:limit, *args, &block)
123
+ rescue InvalidQueryExpressionError, ArgumentError => exception
124
+ raise ArgumentError, exception.message, caller
125
+ end
126
+
127
+ def offset(*args, &block)
128
+ add_query_expression(:offset, *args, &block)
129
+ rescue InvalidQueryExpressionError, ArgumentError => exception
130
+ raise ArgumentError, exception.message, caller
131
+ end
132
+
133
+ def merge(*args, &block)
134
+ add_merge_query_expression(:merge, *args, &block)
135
+ rescue InvalidQueryExpressionError, ArgumentError => exception
136
+ raise ArgumentError, exception.message, caller
137
+ end
138
+
139
+ private
140
+
141
+ def add_query_expression(type, *args, &block)
142
+ QueryExpression.new(type, *args, &block).tap do |expression|
143
+ relation.query_expressions << expression
144
+ end
145
+ end
146
+
147
+ def add_merge_query_expression(type, *args, &block)
148
+ MergeQueryExpression.new(type, *args, &block).tap do |expression|
149
+ relation.query_expressions << expression
150
+ end
151
+ end
152
+
153
+ def add_subquery_expression(type, *args, &block)
154
+ SubqueryExpression.new(type, *args, &block).tap do |expression|
155
+ relation.query_expressions << expression
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+
163
+ class Relation
164
+ attr_reader :from_expression, :query_expressions, :set_expressions
165
+ attr_writer :find_attr
166
+
167
+ def initialize
168
+ @from_expression = NullFromExpression.new
169
+ @query_expressions = []
170
+ @set_expressions = []
171
+ end
172
+
173
+ def from(record_class)
174
+ @from_expression = FromExpression.new(record_class)
175
+ end
176
+
177
+ def from_subquery(&block)
178
+ @from_expression = FromSubqueryExpression.new(&block)
179
+ end
180
+
181
+ def from_record_class
182
+ self.from_expression.record_class
183
+ end
184
+
185
+ def find_attr
186
+ @find_attr ||= self.from_expression.default_find_attr
187
+ end
188
+
189
+ FIND_EXCLUDED_TYPES = [:where, :limit, :offset].freeze
190
+ def build_for_find(id, params = nil)
191
+ query_expressions = self.query_expressions.reject do |e|
192
+ FIND_EXCLUDED_TYPES.include?(e.type)
193
+ end
194
+ ar_relation = build_ar_relation_for_all(query_expressions, params)
195
+ ar_relation.where(self.find_attr => id).limit(1)
196
+ end
197
+
198
+ def build_for_all(params = nil)
199
+ build_ar_relation_for_all(self.query_expressions, params)
200
+ end
201
+
202
+ def build_sql(params = nil)
203
+ self.build_for_all(params).to_sql.strip
204
+ end
205
+
206
+ private
207
+
208
+ def build_ar_relation_for_all(query_expressions, params)
209
+ ar_relation = self.from_expression.ar_relation(params)
210
+ query_expressions.inject(ar_relation){ |r, e| e.apply_to(r, params) }
211
+ end
212
+ end
213
+
214
+ end
@@ -0,0 +1,82 @@
1
+ require 'much-plugin'
2
+ require 'mr/read_model/querying'
3
+
4
+ module MR::ReadModel
5
+
6
+ module SetQuerying
7
+ include MuchPlugin
8
+
9
+ plugin_included do
10
+ include MR::ReadModel::Querying
11
+ extend ClassMethods
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def relation
17
+ @relation ||= Relation.new
18
+ end
19
+
20
+ def union(&block)
21
+ add_set_expression(:union, &block)
22
+ rescue ArgumentError => exception
23
+ raise ArgumentError, exception.message, caller
24
+ end
25
+
26
+ def union_all(&block)
27
+ add_set_expression(:union_all, &block)
28
+ rescue ArgumentError => exception
29
+ raise ArgumentError, exception.message, caller
30
+ end
31
+
32
+ def intersect(&block)
33
+ add_set_expression(:intersect, &block)
34
+ rescue ArgumentError => exception
35
+ raise ArgumentError, exception.message, caller
36
+ end
37
+
38
+ def intersect_all(&block)
39
+ add_set_expression(:intersect_all, &block)
40
+ rescue ArgumentError => exception
41
+ raise ArgumentError, exception.message, caller
42
+ end
43
+
44
+ def except(&block)
45
+ add_set_expression(:except, &block)
46
+ rescue ArgumentError => exception
47
+ raise ArgumentError, exception.message, caller
48
+ end
49
+
50
+ def except_all(&block)
51
+ add_set_expression(:except_all, &block)
52
+ rescue ArgumentError => exception
53
+ raise ArgumentError, exception.message, caller
54
+ end
55
+
56
+ private
57
+
58
+ def add_set_expression(type, &block)
59
+ SetExpression.new(type, &block).tap do |expression|
60
+ relation.set_expressions << expression
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ class Relation < MR::ReadModel::Relation
67
+ attr_reader :set_expressions
68
+
69
+ def initialize
70
+ super
71
+ @set_expressions = []
72
+ end
73
+
74
+ def build_sql(params = nil)
75
+ sql = super(params)
76
+ self.set_expressions.inject(sql){ |s, e| e.combine_sql(s, params) }
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,98 @@
1
+ module MR; end
2
+ module MR::ReadModel
3
+
4
+ module Subquery
5
+ attr_reader :read_model_class
6
+
7
+ def initialize(&block)
8
+ self.instance_eval(&block)
9
+ if !self.read_model_class
10
+ raise ArgumentError, "invalid subquery - " \
11
+ "use `read_model` to define the subquery"
12
+ end
13
+ end
14
+
15
+ def read_model(&block)
16
+ @read_model_class = Class.new do
17
+ # TODO - fix circular require
18
+ require 'mr/read_model'
19
+ require 'mr/read_model/set_querying'
20
+ include MR::ReadModel
21
+ include MR::ReadModel::SetQuerying
22
+ end
23
+ @read_model_class.class_eval(&block)
24
+ end
25
+
26
+ def build_sql(params = nil)
27
+ "(#{self.read_model_class.build_sql(params)})"
28
+ end
29
+
30
+ end
31
+
32
+ module AliasSubquery
33
+ include Subquery
34
+
35
+ def alias_sql
36
+ @alias_sql ||= ""
37
+ end
38
+
39
+ def as(alias_name)
40
+ if alias_name.to_s.strip.empty?
41
+ raise ArgumentError, "alias can't be blank"
42
+ end
43
+ @alias_sql = "AS #{alias_name}"
44
+ end
45
+
46
+ def build_sql(params = nil)
47
+ if self.alias_sql.to_s.strip.empty?
48
+ raise InvalidSubqueryError, "subquery must have an alias"
49
+ end
50
+ "#{super} #{self.alias_sql}".strip
51
+ end
52
+
53
+ end
54
+
55
+ class FromSubquery
56
+ include AliasSubquery
57
+
58
+ def record_class
59
+ self.read_model_class.record_class
60
+ end
61
+
62
+ end
63
+
64
+ class JoinSubquery
65
+ include AliasSubquery
66
+
67
+ DEFAULT_JOIN_SQL = 'JOIN'.freeze
68
+ JOIN_SQL = Hash.new(DEFAULT_JOIN_SQL).tap do |h|
69
+ h[:inner] = 'INNER JOIN'.freeze
70
+ h[:left] = 'LEFT OUTER JOIN'.freeze
71
+ h[:right] = 'RIGHT OUTER JOIN'.freeze
72
+ h[:full] = 'FULL OUTER JOIN'.freeze
73
+ end.freeze
74
+
75
+ attr_reader :join_sql
76
+
77
+ def initialize(type, &block)
78
+ @join_sql = JOIN_SQL[type]
79
+ super(&block)
80
+ end
81
+
82
+ def conditions_sql
83
+ @conditions_sql ||= ""
84
+ end
85
+
86
+ def on(conditions)
87
+ @conditions_sql = "ON #{conditions}"
88
+ end
89
+
90
+ def build_sql(params = nil)
91
+ "#{self.join_sql} #{super} #{self.conditions_sql}".strip
92
+ end
93
+
94
+ end
95
+
96
+ InvalidSubqueryError = Class.new(RuntimeError)
97
+
98
+ end
@@ -0,0 +1,35 @@
1
+ require 'much-plugin'
2
+
3
+ module MR
4
+
5
+ module Record
6
+ include MuchPlugin
7
+
8
+ plugin_included do
9
+ extend ClassMethods
10
+ include InstanceMethods
11
+ end
12
+
13
+ module InstanceMethods
14
+
15
+ attr_writer :model
16
+
17
+ def model
18
+ @model ||= self.class.model_class.new(self)
19
+ end
20
+
21
+ def model_class
22
+ self.class.model_class
23
+ end
24
+
25
+ end
26
+
27
+ module ClassMethods
28
+
29
+ attr_accessor :model_class
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,229 @@
1
+ require 'mr/fake_record'
2
+ require 'mr/model'
3
+
4
+ module MR
5
+
6
+ module TestHelpers
7
+ module_function
8
+
9
+ def model_reset_save_called(model, &block)
10
+ fake_record = model.record
11
+ if !fake_record.kind_of?(MR::FakeRecord)
12
+ raise ArgumentError, "model must be using a fake record"
13
+ end
14
+ yield model if block
15
+ fake_record.reset_save_called
16
+ end
17
+
18
+ def assert_association_saved(model, association, expected_value)
19
+ with_backtrace(caller) do
20
+ AssociationSavedAssertion.new(model, association, expected_value).run(self)
21
+ end
22
+ end
23
+
24
+ def assert_not_association_saved(model, association, expected_value)
25
+ with_backtrace(caller) do
26
+ AssociationNotSavedAssertion.new(model, association, expected_value).run(self)
27
+ end
28
+ end
29
+
30
+ def assert_model_destroyed(model)
31
+ with_backtrace(caller) do
32
+ ModelDestroyedAssertion.new(model).run(self)
33
+ end
34
+ end
35
+
36
+ def assert_not_model_destroyed(model)
37
+ with_backtrace(caller) do
38
+ ModelNotDestroyedAssertion.new(model).run(self)
39
+ end
40
+ end
41
+
42
+ def assert_field_saved(model, field, expected_value)
43
+ with_backtrace(caller) do
44
+ FieldSavedAssertion.new(model, field, expected_value).run(self)
45
+ end
46
+ end
47
+
48
+ def assert_not_field_saved(model, field, expected_value)
49
+ with_backtrace(caller) do
50
+ FieldNotSavedAssertion.new(model, field, expected_value).run(self)
51
+ end
52
+ end
53
+
54
+ def assert_model_saved(model)
55
+ with_backtrace(caller) do
56
+ ModelSavedAssertion.new(model).run(self)
57
+ end
58
+ end
59
+
60
+ def assert_not_model_saved(model)
61
+ with_backtrace(caller) do
62
+ ModelNotSavedAssertion.new(model).run(self)
63
+ end
64
+ end
65
+
66
+ class AssociationSavedAssertionBase
67
+ def initialize(model, association, expected_value)
68
+ reflection = model.record.association(association).reflection
69
+ if reflection.macro != :belongs_to
70
+ raise ArgumentError, "association must be a belongs to"
71
+ end
72
+ @expected_value = expected_value || NULL_MODEL
73
+ # use `record.class` instead of `record_class` here, the configured
74
+ # `record_class` won't match the actual `record` class because `record` is
75
+ # a fake record
76
+ expected_foreign_type = @expected_value.record.class.to_s
77
+ expected_foreign_key = @expected_value.id
78
+ @assertions = [
79
+ build_assertion(model, reflection.foreign_type, expected_foreign_type),
80
+ build_assertion(model, reflection.foreign_key, expected_foreign_key)
81
+ ].compact
82
+ end
83
+
84
+ def run(context)
85
+ @assertions.each{ |a| a.run(context) }
86
+ end
87
+
88
+ private
89
+
90
+ def build_assertion(model, field, expected_value)
91
+ return unless field
92
+ self.field_assertion_class.new(model, field, expected_value)
93
+ end
94
+
95
+ NullModel = Struct.new(:id, :record)
96
+ NullRecord = Struct.new(:class)
97
+ NullRecordClass = Struct.new(:name)
98
+ NULL_MODEL = NullModel.new(nil, NullRecord.new(NullRecordClass.new))
99
+ end
100
+
101
+ class AssociationSavedAssertion < AssociationSavedAssertionBase
102
+ def field_assertion_class; FieldSavedAssertion; end
103
+ end
104
+
105
+ class AssociationNotSavedAssertion < AssociationSavedAssertionBase
106
+ def field_assertion_class; FieldNotSavedAssertion; end
107
+ end
108
+
109
+ class FieldSavedAssertionBase
110
+ def initialize(model, field, expected_value)
111
+ if !model.record.kind_of?(MR::FakeRecord)
112
+ raise ArgumentError, "model must be using a fake record"
113
+ end
114
+ @expected_value = expected_value
115
+ @field = field.to_s
116
+ @saved = model.record.saved_attributes.key?(@field)
117
+ @saved_as = model.record.saved_attributes[@field]
118
+ end
119
+ end
120
+
121
+ class FieldSavedAssertion < FieldSavedAssertionBase
122
+ def run(context)
123
+ if @saved
124
+ context.assert_equal @expected_value, @saved_as, saved_as_desc
125
+ else
126
+ context.assert_true @saved, saved_desc
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ def saved_desc
133
+ "Expected #{@field.inspect} field was saved."
134
+ end
135
+
136
+ def saved_as_desc
137
+ "Expected #{@field.inspect} field was saved as #{@expected_value.inspect}."
138
+ end
139
+ end
140
+
141
+ class FieldNotSavedAssertion < FieldSavedAssertionBase
142
+ def run(context)
143
+ if @saved
144
+ context.assert_not_equal @expected_value, @saved_as, saved_as_desc
145
+ else
146
+ context.assert_false @saved, saved_desc
147
+ end
148
+ end
149
+
150
+ private
151
+
152
+ def saved_desc
153
+ "Expected #{@field.inspect} field was not saved."
154
+ end
155
+
156
+ def saved_as_desc
157
+ "Expected #{@field.inspect} field was not saved as #{@expected_value.inspect}."
158
+ end
159
+ end
160
+
161
+ class ModelDestroyedAssertionBase
162
+ def initialize(model)
163
+ @model = model
164
+ @destroyed = @model.destroyed?
165
+ end
166
+ end
167
+
168
+ class ModelDestroyedAssertion < ModelDestroyedAssertionBase
169
+ def run(context)
170
+ context.assert_true(@destroyed){ destroyed_desc }
171
+ end
172
+
173
+ private
174
+
175
+ def destroyed_desc
176
+ "Expected #{@model.inspect} was destroyed."
177
+ end
178
+ end
179
+
180
+ class ModelNotDestroyedAssertion < ModelDestroyedAssertionBase
181
+ def run(context)
182
+ context.assert_false(@destroyed){ destroyed_desc }
183
+ end
184
+
185
+ private
186
+
187
+ def destroyed_desc
188
+ "Expected #{@model.inspect} was not destroyed."
189
+ end
190
+ end
191
+
192
+ class ModelSavedAssertionBase
193
+ def initialize(model)
194
+ @model = model
195
+ fake_record = model.record
196
+ if !fake_record.kind_of?(MR::FakeRecord)
197
+ raise ArgumentError, "model must be using a fake record"
198
+ end
199
+ @saved = fake_record.save_called
200
+ end
201
+ end
202
+
203
+ class ModelSavedAssertion < ModelSavedAssertionBase
204
+ def run(context)
205
+ context.assert_true(@saved){ saved_desc }
206
+ end
207
+
208
+ private
209
+
210
+ def saved_desc
211
+ "Expected #{@model.inspect} was saved."
212
+ end
213
+ end
214
+
215
+ class ModelNotSavedAssertion < ModelSavedAssertionBase
216
+ def run(context)
217
+ context.assert_false(@saved){ saved_desc }
218
+ end
219
+
220
+ private
221
+
222
+ def saved_desc
223
+ "Expected #{@model.inspect} was not saved."
224
+ end
225
+ end
226
+
227
+ end
228
+
229
+ end