granite-form 0.1.0

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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +13 -0
  3. data/.github/workflows/ci.yml +35 -0
  4. data/.github/workflows/main.yml +29 -0
  5. data/.gitignore +21 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +64 -0
  8. data/.rubocop_todo.yml +48 -0
  9. data/Appraisals +8 -0
  10. data/CHANGELOG.md +73 -0
  11. data/Gemfile +8 -0
  12. data/Guardfile +77 -0
  13. data/LICENSE +22 -0
  14. data/README.md +429 -0
  15. data/Rakefile +6 -0
  16. data/gemfiles/rails.4.2.gemfile +15 -0
  17. data/gemfiles/rails.5.0.gemfile +15 -0
  18. data/gemfiles/rails.5.1.gemfile +15 -0
  19. data/gemfiles/rails.5.2.gemfile +15 -0
  20. data/gemfiles/rails.6.0.gemfile +14 -0
  21. data/gemfiles/rails.6.1.gemfile +14 -0
  22. data/gemfiles/rails.7.0.gemfile +14 -0
  23. data/granite-form.gemspec +31 -0
  24. data/lib/granite/form/active_record/associations.rb +57 -0
  25. data/lib/granite/form/active_record/nested_attributes.rb +20 -0
  26. data/lib/granite/form/base.rb +15 -0
  27. data/lib/granite/form/config.rb +42 -0
  28. data/lib/granite/form/errors.rb +111 -0
  29. data/lib/granite/form/extensions.rb +36 -0
  30. data/lib/granite/form/model/associations/base.rb +97 -0
  31. data/lib/granite/form/model/associations/collection/embedded.rb +14 -0
  32. data/lib/granite/form/model/associations/collection/proxy.rb +35 -0
  33. data/lib/granite/form/model/associations/embeds_any.rb +19 -0
  34. data/lib/granite/form/model/associations/embeds_many.rb +152 -0
  35. data/lib/granite/form/model/associations/embeds_one.rb +112 -0
  36. data/lib/granite/form/model/associations/nested_attributes.rb +215 -0
  37. data/lib/granite/form/model/associations/persistence_adapters/active_record/referenced_proxy.rb +33 -0
  38. data/lib/granite/form/model/associations/persistence_adapters/active_record.rb +68 -0
  39. data/lib/granite/form/model/associations/persistence_adapters/base.rb +55 -0
  40. data/lib/granite/form/model/associations/references_any.rb +43 -0
  41. data/lib/granite/form/model/associations/references_many.rb +113 -0
  42. data/lib/granite/form/model/associations/references_one.rb +88 -0
  43. data/lib/granite/form/model/associations/reflections/base.rb +92 -0
  44. data/lib/granite/form/model/associations/reflections/embeds_any.rb +52 -0
  45. data/lib/granite/form/model/associations/reflections/embeds_many.rb +17 -0
  46. data/lib/granite/form/model/associations/reflections/embeds_one.rb +19 -0
  47. data/lib/granite/form/model/associations/reflections/references_any.rb +65 -0
  48. data/lib/granite/form/model/associations/reflections/references_many.rb +30 -0
  49. data/lib/granite/form/model/associations/reflections/references_one.rb +32 -0
  50. data/lib/granite/form/model/associations/reflections/singular.rb +37 -0
  51. data/lib/granite/form/model/associations/validations.rb +41 -0
  52. data/lib/granite/form/model/associations.rb +120 -0
  53. data/lib/granite/form/model/attributes/attribute.rb +75 -0
  54. data/lib/granite/form/model/attributes/base.rb +134 -0
  55. data/lib/granite/form/model/attributes/collection.rb +19 -0
  56. data/lib/granite/form/model/attributes/dictionary.rb +28 -0
  57. data/lib/granite/form/model/attributes/localized.rb +44 -0
  58. data/lib/granite/form/model/attributes/reference_many.rb +21 -0
  59. data/lib/granite/form/model/attributes/reference_one.rb +52 -0
  60. data/lib/granite/form/model/attributes/reflections/attribute.rb +61 -0
  61. data/lib/granite/form/model/attributes/reflections/base.rb +62 -0
  62. data/lib/granite/form/model/attributes/reflections/collection.rb +12 -0
  63. data/lib/granite/form/model/attributes/reflections/dictionary.rb +15 -0
  64. data/lib/granite/form/model/attributes/reflections/localized.rb +45 -0
  65. data/lib/granite/form/model/attributes/reflections/reference_many.rb +12 -0
  66. data/lib/granite/form/model/attributes/reflections/reference_one.rb +49 -0
  67. data/lib/granite/form/model/attributes/reflections/represents.rb +56 -0
  68. data/lib/granite/form/model/attributes/represents.rb +67 -0
  69. data/lib/granite/form/model/attributes.rb +204 -0
  70. data/lib/granite/form/model/callbacks.rb +72 -0
  71. data/lib/granite/form/model/conventions.rb +40 -0
  72. data/lib/granite/form/model/dirty.rb +84 -0
  73. data/lib/granite/form/model/lifecycle.rb +309 -0
  74. data/lib/granite/form/model/localization.rb +26 -0
  75. data/lib/granite/form/model/persistence.rb +59 -0
  76. data/lib/granite/form/model/primary.rb +59 -0
  77. data/lib/granite/form/model/representation.rb +101 -0
  78. data/lib/granite/form/model/scopes.rb +118 -0
  79. data/lib/granite/form/model/validations/associated.rb +22 -0
  80. data/lib/granite/form/model/validations/nested.rb +56 -0
  81. data/lib/granite/form/model/validations.rb +29 -0
  82. data/lib/granite/form/model.rb +33 -0
  83. data/lib/granite/form/railtie.rb +9 -0
  84. data/lib/granite/form/undefined_class.rb +11 -0
  85. data/lib/granite/form/version.rb +5 -0
  86. data/lib/granite/form.rb +163 -0
  87. data/spec/lib/granite/form/active_record/associations_spec.rb +211 -0
  88. data/spec/lib/granite/form/active_record/nested_attributes_spec.rb +15 -0
  89. data/spec/lib/granite/form/config_spec.rb +66 -0
  90. data/spec/lib/granite/form/model/associations/embeds_many_spec.rb +706 -0
  91. data/spec/lib/granite/form/model/associations/embeds_one_spec.rb +533 -0
  92. data/spec/lib/granite/form/model/associations/nested_attributes_spec.rb +119 -0
  93. data/spec/lib/granite/form/model/associations/persistence_adapters/active_record_spec.rb +58 -0
  94. data/spec/lib/granite/form/model/associations/references_many_spec.rb +572 -0
  95. data/spec/lib/granite/form/model/associations/references_one_spec.rb +445 -0
  96. data/spec/lib/granite/form/model/associations/reflections/embeds_any_spec.rb +42 -0
  97. data/spec/lib/granite/form/model/associations/reflections/embeds_many_spec.rb +145 -0
  98. data/spec/lib/granite/form/model/associations/reflections/embeds_one_spec.rb +117 -0
  99. data/spec/lib/granite/form/model/associations/reflections/references_many_spec.rb +303 -0
  100. data/spec/lib/granite/form/model/associations/reflections/references_one_spec.rb +287 -0
  101. data/spec/lib/granite/form/model/associations/validations_spec.rb +137 -0
  102. data/spec/lib/granite/form/model/associations_spec.rb +198 -0
  103. data/spec/lib/granite/form/model/attributes/attribute_spec.rb +186 -0
  104. data/spec/lib/granite/form/model/attributes/base_spec.rb +97 -0
  105. data/spec/lib/granite/form/model/attributes/collection_spec.rb +72 -0
  106. data/spec/lib/granite/form/model/attributes/dictionary_spec.rb +100 -0
  107. data/spec/lib/granite/form/model/attributes/localized_spec.rb +103 -0
  108. data/spec/lib/granite/form/model/attributes/reflections/attribute_spec.rb +72 -0
  109. data/spec/lib/granite/form/model/attributes/reflections/base_spec.rb +56 -0
  110. data/spec/lib/granite/form/model/attributes/reflections/collection_spec.rb +37 -0
  111. data/spec/lib/granite/form/model/attributes/reflections/dictionary_spec.rb +43 -0
  112. data/spec/lib/granite/form/model/attributes/reflections/localized_spec.rb +37 -0
  113. data/spec/lib/granite/form/model/attributes/reflections/represents_spec.rb +70 -0
  114. data/spec/lib/granite/form/model/attributes/represents_spec.rb +85 -0
  115. data/spec/lib/granite/form/model/attributes_spec.rb +350 -0
  116. data/spec/lib/granite/form/model/callbacks_spec.rb +337 -0
  117. data/spec/lib/granite/form/model/conventions_spec.rb +11 -0
  118. data/spec/lib/granite/form/model/dirty_spec.rb +84 -0
  119. data/spec/lib/granite/form/model/lifecycle_spec.rb +356 -0
  120. data/spec/lib/granite/form/model/persistence_spec.rb +46 -0
  121. data/spec/lib/granite/form/model/primary_spec.rb +84 -0
  122. data/spec/lib/granite/form/model/representation_spec.rb +139 -0
  123. data/spec/lib/granite/form/model/scopes_spec.rb +86 -0
  124. data/spec/lib/granite/form/model/typecasting_spec.rb +193 -0
  125. data/spec/lib/granite/form/model/validations/associated_spec.rb +102 -0
  126. data/spec/lib/granite/form/model/validations/nested_spec.rb +164 -0
  127. data/spec/lib/granite/form/model/validations_spec.rb +31 -0
  128. data/spec/lib/granite/form/model_spec.rb +10 -0
  129. data/spec/lib/granite/form_spec.rb +11 -0
  130. data/spec/shared/nested_attribute_examples.rb +332 -0
  131. data/spec/spec_helper.rb +50 -0
  132. data/spec/support/model_helpers.rb +10 -0
  133. data/spec/support/muffle_helper.rb +7 -0
  134. metadata +403 -0
@@ -0,0 +1,332 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'nested attributes' do
4
+ before do
5
+ stub_model :project do
6
+ include Granite::Form::Model::Primary
7
+ include Granite::Form::Model::Lifecycle
8
+
9
+ primary :slug, String
10
+ attribute :title, String
11
+ end
12
+
13
+ stub_model :profile do
14
+ include Granite::Form::Model::Primary
15
+ include Granite::Form::Model::Lifecycle
16
+
17
+ primary :identifier
18
+ attribute :first_name, String
19
+ end
20
+ end
21
+
22
+ context 'embeds_one' do
23
+ let(:user) { User.new }
24
+
25
+ specify { expect { user.profile_attributes = {} }.to change { user.profile }.to(an_instance_of(Profile)) }
26
+ specify { expect { user.profile_attributes = {first_name: 'User'} }.to change { user.profile.try(:first_name) }.to('User') }
27
+ specify { expect { user.profile_attributes = {identifier: 42, first_name: 'User'} }.to raise_error Granite::Form::ObjectNotFound }
28
+
29
+ context ':reject_if' do
30
+ context do
31
+ before { User.accepts_nested_attributes_for :profile, reject_if: :all_blank }
32
+ specify { expect { user.profile_attributes = {first_name: ''} }.not_to change { user.profile } }
33
+ end
34
+
35
+ context do
36
+ before { User.accepts_nested_attributes_for :profile, reject_if: ->(attributes) { attributes['first_name'].blank? } }
37
+ specify { expect { user.profile_attributes = {first_name: ''} }.not_to change { user.profile } }
38
+ end
39
+ end
40
+
41
+ context 'existing' do
42
+ let(:profile) { Profile.new(first_name: 'User') }
43
+ let(:user) { User.new profile: profile }
44
+
45
+ specify { expect { user.profile_attributes = {identifier: 42, first_name: 'User'} }.to raise_error Granite::Form::ObjectNotFound }
46
+ specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1'} }.to change { user.profile.first_name }.to('User 1') }
47
+ specify { expect { user.profile_attributes = {first_name: 'User 1'} }.to change { user.profile.first_name }.to('User 1') }
48
+ specify { expect { user.profile_attributes = {first_name: 'User 1', _destroy: '1'} }.not_to change { user.profile.first_name } }
49
+ specify do
50
+ expect do
51
+ user.profile_attributes = {first_name: 'User 1', _destroy: '1'}
52
+ user.save { true }
53
+ end.not_to change { user.profile.first_name }
54
+ end
55
+ specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'} }.to change { user.profile.first_name }.to('User 1') }
56
+ specify do
57
+ expect do
58
+ user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'}
59
+ user.save { true }
60
+ end.to change { user.profile.first_name }.to('User 1')
61
+ end
62
+
63
+ context ':allow_destroy' do
64
+ before { User.accepts_nested_attributes_for :profile, allow_destroy: true }
65
+
66
+ specify { expect { user.profile_attributes = {first_name: 'User 1', _destroy: '1'} }.not_to change { user.profile.first_name } }
67
+ specify do
68
+ expect do
69
+ user.profile_attributes = {first_name: 'User 1', _destroy: '1'}
70
+ user.save { true }
71
+ end.not_to change { user.profile.first_name }
72
+ end
73
+ specify { expect { user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'} }.to change { user.profile.first_name }.to('User 1') }
74
+ specify do
75
+ expect do
76
+ user.profile_attributes = {identifier: profile.identifier.to_s, first_name: 'User 1', _destroy: '1'}
77
+ user.save { true }
78
+ end.to change { user.profile }.to(nil)
79
+ end
80
+ end
81
+
82
+ context ':update_only' do
83
+ before { User.accepts_nested_attributes_for :profile, update_only: true }
84
+
85
+ specify do
86
+ expect { user.profile_attributes = {identifier: 42, first_name: 'User 1'} }
87
+ .to change { user.profile.first_name }.to('User 1')
88
+ end
89
+ end
90
+ end
91
+
92
+ context 'not primary' do
93
+ before do
94
+ stub_model :profile do
95
+ include Granite::Form::Model::Lifecycle
96
+
97
+ attribute :identifier, Integer
98
+ attribute :first_name, String
99
+ end
100
+ end
101
+
102
+ specify { expect { user.profile_attributes = {} }.to change { user.profile }.to(an_instance_of(Profile)) }
103
+ specify { expect { user.profile_attributes = {first_name: 'User'} }.to change { user.profile.try(:first_name) }.to('User') }
104
+
105
+ context do
106
+ let(:profile) { Profile.new(first_name: 'User') }
107
+ let(:user) { User.new profile: profile }
108
+
109
+ specify do
110
+ expect { user.profile_attributes = {identifier: 42, first_name: 'User 1'} }
111
+ .to change { user.profile.first_name }.to('User 1')
112
+ end
113
+ end
114
+ end
115
+
116
+ context 'generated method overwrites' do
117
+ before do
118
+ User.class_eval <<-RUBY, __FILE__, __LINE__ + 1
119
+ def profile_attributes=(args)
120
+ args.reverse_merge!(first_name: 'Default Profile Name')
121
+ super
122
+ end
123
+ RUBY
124
+ end
125
+
126
+ it 'allows generated method overwritting' do
127
+ expect { user.profile_attributes = {} }
128
+ .to change { user.profile.try(:first_name) }.to('Default Profile Name')
129
+ end
130
+ end
131
+ end
132
+
133
+ context 'embeds_many' do
134
+ let(:user) { User.new }
135
+
136
+ specify { expect { user.projects_attributes = {} }.not_to change { user.projects } }
137
+ specify do
138
+ expect { user.projects_attributes = [{title: 'Project 1'}, {title: 'Project 2'}] }
139
+ .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 2'])
140
+ end
141
+ specify do
142
+ expect { user.projects_attributes = {1 => {title: 'Project 1'}, 2 => {title: 'Project 2'}} }
143
+ .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 2'])
144
+ end
145
+ specify do
146
+ expect { user.projects_attributes = [{slug: 42, title: 'Project 1'}, {title: 'Project 2'}] }
147
+ .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 2'])
148
+ end
149
+ specify do
150
+ expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
151
+ .to change { user.projects.map(&:title) }.to(['', 'Project 2'])
152
+ end
153
+
154
+ context ':limit' do
155
+ before { User.accepts_nested_attributes_for :projects, limit: 1 }
156
+
157
+ specify do
158
+ expect { user.projects_attributes = [{title: 'Project 1'}] }
159
+ .to change { user.projects.map(&:title) }.to(['Project 1'])
160
+ end
161
+ specify do
162
+ expect { user.projects_attributes = [{title: 'Project 1'}, {title: 'Project 2'}] }
163
+ .to raise_error Granite::Form::TooManyObjects
164
+ end
165
+ end
166
+
167
+ context ':reject_if' do
168
+ context do
169
+ before { User.accepts_nested_attributes_for :projects, reject_if: :all_blank }
170
+ specify do
171
+ expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
172
+ .to change { user.projects.map(&:title) }.to(['Project 2'])
173
+ end
174
+ end
175
+
176
+ context do
177
+ before { User.accepts_nested_attributes_for :projects, reject_if: ->(attributes) { attributes['title'].blank? } }
178
+ specify do
179
+ expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
180
+ .to change { user.projects.map(&:title) }.to(['Project 2'])
181
+ end
182
+ end
183
+
184
+ context do
185
+ before { User.accepts_nested_attributes_for :projects, reject_if: ->(attributes) { attributes['foobar'].blank? } }
186
+ specify do
187
+ expect { user.projects_attributes = [{title: ''}, {title: 'Project 2'}] }
188
+ .not_to change { user.projects }
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'existing' do
194
+ let(:projects) { Array.new(2) { |i| Project.new(title: "Project #{i.next}").tap { |pr| pr.slug = 42 + i } } }
195
+ let(:user) { User.new projects: projects }
196
+
197
+ specify do
198
+ expect do
199
+ user.projects_attributes = [
200
+ {slug: projects.first.slug.to_i, title: 'Project 3'},
201
+ {title: 'Project 4'}
202
+ ]
203
+ end
204
+ .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2', 'Project 4'])
205
+ end
206
+ specify do
207
+ expect do
208
+ user.projects_attributes = [
209
+ {slug: projects.first.slug.to_i, title: 'Project 3'},
210
+ {slug: 33, title: 'Project 4'}
211
+ ]
212
+ end
213
+ .to change { user.projects.map(&:slug) }.to(%w[42 43 33])
214
+ end
215
+ specify do
216
+ expect do
217
+ user.projects_attributes = [
218
+ {slug: projects.first.slug.to_i, title: 'Project 3'},
219
+ {slug: 33, title: 'Project 4', _destroy: 1}
220
+ ]
221
+ end
222
+ .not_to change { user.projects.map(&:slug) }
223
+ end
224
+ specify do
225
+ expect do
226
+ user.projects_attributes = {
227
+ 1 => {slug: projects.first.slug.to_i, title: 'Project 3'},
228
+ 2 => {title: 'Project 4'}
229
+ }
230
+ end
231
+ .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2', 'Project 4'])
232
+ end
233
+ specify do
234
+ expect do
235
+ user.projects_attributes = [
236
+ {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
237
+ {title: 'Project 4', _destroy: '1'}
238
+ ]
239
+ end
240
+ .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2'])
241
+ end
242
+ specify do
243
+ expect do
244
+ user.projects_attributes = [
245
+ {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
246
+ {title: 'Project 4', _destroy: '1'}
247
+ ]
248
+ user.save { true }
249
+ end
250
+ .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2'])
251
+ end
252
+
253
+ context ':allow_destroy' do
254
+ before { User.accepts_nested_attributes_for :projects, allow_destroy: true }
255
+
256
+ specify do
257
+ expect do
258
+ user.projects_attributes = [
259
+ {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
260
+ {title: 'Project 4', _destroy: '1'}
261
+ ]
262
+ end
263
+ .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2'])
264
+ end
265
+ specify do
266
+ expect do
267
+ user.projects_attributes = [
268
+ {slug: projects.first.slug.to_i, title: 'Project 3', _destroy: '1'},
269
+ {title: 'Project 4', _destroy: '1'}
270
+ ]
271
+ user.save { true }
272
+ end
273
+ .to change { user.projects.map(&:title) }.to(['Project 2'])
274
+ end
275
+ end
276
+
277
+ context ':update_only' do
278
+ before { User.accepts_nested_attributes_for :projects, update_only: true }
279
+
280
+ specify do
281
+ expect do
282
+ user.projects_attributes = [
283
+ {slug: projects.first.slug.to_i, title: 'Project 3'},
284
+ {title: 'Project 4'}
285
+ ]
286
+ end
287
+ .to change { user.projects.map(&:title) }.to(['Project 3', 'Project 2'])
288
+ end
289
+
290
+ specify do
291
+ expect do
292
+ user.projects_attributes = [
293
+ {slug: projects.last.slug.to_i, title: 'Project 3'},
294
+ {slug: projects.first.slug.to_i.pred, title: 'Project 0'}
295
+ ]
296
+ end
297
+ .to change { user.projects.map(&:title) }.to(['Project 1', 'Project 3'])
298
+ end
299
+ end
300
+ end
301
+
302
+ context 'primary absence causes exception' do
303
+ before do
304
+ stub_model :project do
305
+ include Granite::Form::Model::Primary
306
+ include Granite::Form::Model::Lifecycle
307
+
308
+ attribute :slug, String
309
+ attribute :title, String
310
+ end
311
+ end
312
+
313
+ specify { expect { user.projects_attributes = {} }.to raise_error Granite::Form::UndefinedPrimaryAttribute }
314
+ end
315
+
316
+ context 'generated method overwrites' do
317
+ before do
318
+ User.class_eval <<-RUBY, __FILE__, __LINE__ + 1
319
+ def projects_attributes=(args)
320
+ args << {title: 'Default Project'}
321
+ super
322
+ end
323
+ RUBY
324
+ end
325
+
326
+ it 'allows generated method overwritting' do
327
+ expect { user.projects_attributes = [] }
328
+ .to change { user.projects.map(&:title) }.to(['Default Project'])
329
+ end
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,50 @@
1
+ require 'bundler'
2
+ require 'pry'
3
+ Bundler.require
4
+
5
+ require 'rspec/its'
6
+ require 'active_record'
7
+ require 'rack/test'
8
+ require 'action_controller/metal/strong_parameters'
9
+ require 'database_cleaner'
10
+
11
+ require 'support/model_helpers'
12
+ require 'support/muffle_helper'
13
+
14
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
15
+ ActiveRecord::Base.logger = Logger.new('/dev/null')
16
+
17
+ ActiveRecord::Schema.define do
18
+ create_table :users do |t|
19
+ t.column :email, :string
20
+ t.column :projects, :text
21
+ t.column :profile, :text
22
+ end
23
+
24
+ create_table :authors do |t|
25
+ t.column :name, :string
26
+ end
27
+ end
28
+
29
+ RSpec.configure do |config|
30
+ config.mock_with :rspec
31
+ config.order = :random
32
+ config.run_all_when_everything_filtered = true
33
+ config.filter_run focus: true
34
+
35
+ config.include ModelHelpers
36
+ config.include MuffleHelpers
37
+
38
+ config.before(:suite) do
39
+ DatabaseCleaner.clean_with :truncation
40
+ DatabaseCleaner.strategy = :transaction
41
+ end
42
+
43
+ config.before do
44
+ DatabaseCleaner.start
45
+ end
46
+
47
+ config.after do
48
+ DatabaseCleaner.clean
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ module ModelHelpers
2
+ def stub_model(name = nil, superclass = nil, &block)
3
+ stub_class(name, superclass) { include Granite::Form::Model }.tap { |klass| klass.class_eval(&block) if block }
4
+ end
5
+
6
+ def stub_class(name = nil, superclass = nil, &block)
7
+ klass = superclass ? Class.new(superclass, &block) : Class.new(&block)
8
+ name.present? ? stub_const(name.to_s.camelize, klass) : klass
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module MuffleHelpers
2
+ def muffle(*exceptions)
3
+ yield
4
+ rescue *exceptions.flatten
5
+ nil
6
+ end
7
+ end