with_model 2.0.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +6 -0
- data/.travis.yml +22 -16
- data/Gemfile +11 -6
- data/LICENSE +1 -1
- data/README.md +11 -15
- data/Rakefile +20 -0
- data/bin/rake +18 -0
- data/lib/with_model.rb +4 -8
- data/lib/with_model/constant_stubber.rb +30 -8
- data/lib/with_model/methods.rb +2 -0
- data/lib/with_model/model.rb +7 -15
- data/lib/with_model/model/dsl.rb +10 -3
- data/lib/with_model/table.rb +3 -1
- data/lib/with_model/version.rb +3 -1
- data/spec/active_record_behaviors_spec.rb +27 -25
- data/spec/readme_spec.rb +23 -25
- data/spec/spec_helper.rb +13 -16
- data/spec/with_model_spec.rb +80 -68
- data/with_model.gemspec +14 -14
- metadata +27 -17
- data/Rakefile.rb +0 -14
data/spec/readme_spec.rb
CHANGED
@@ -1,12 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
before :all do
|
5
|
-
module SomeModule; end
|
6
|
-
end
|
3
|
+
require 'spec_helper'
|
7
4
|
|
8
|
-
|
9
|
-
|
5
|
+
describe 'A blog post' do
|
6
|
+
before do
|
7
|
+
stub_const('MyModule', Module.new)
|
10
8
|
end
|
11
9
|
|
12
10
|
with_model :BlogPost do
|
@@ -18,7 +16,7 @@ describe "A blog post" do
|
|
18
16
|
|
19
17
|
# The model block works just like the class definition.
|
20
18
|
model do
|
21
|
-
include
|
19
|
+
include MyModule
|
22
20
|
has_many :comments
|
23
21
|
validates_presence_of :title
|
24
22
|
|
@@ -45,30 +43,30 @@ describe "A blog post" do
|
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
48
|
-
it
|
46
|
+
it 'can be accessed as a constant' do
|
49
47
|
expect(BlogPost).to be
|
50
48
|
end
|
51
49
|
|
52
|
-
it
|
53
|
-
expect(BlogPost.include?(
|
50
|
+
it 'has the module' do
|
51
|
+
expect(BlogPost.include?(MyModule)).to eq true
|
54
52
|
end
|
55
53
|
|
56
|
-
it
|
54
|
+
it 'has the class method' do
|
57
55
|
expect(BlogPost.some_class_method).to eq 'chunky'
|
58
56
|
end
|
59
57
|
|
60
|
-
it
|
58
|
+
it 'has the instance method' do
|
61
59
|
expect(BlogPost.new.some_instance_method).to eq 'bacon'
|
62
60
|
end
|
63
61
|
|
64
|
-
it
|
62
|
+
it 'can do all the things a regular model can' do
|
65
63
|
record = BlogPost.new
|
66
64
|
expect(record).to_not be_valid
|
67
|
-
record.title =
|
65
|
+
record.title = 'foo'
|
68
66
|
expect(record).to be_valid
|
69
67
|
expect(record.save).to eq true
|
70
68
|
expect(record.reload).to eq record
|
71
|
-
record.comments.create!(:
|
69
|
+
record.comments.create!(text: 'Lorem ipsum')
|
72
70
|
expect(record.comments.count).to eq 1
|
73
71
|
end
|
74
72
|
|
@@ -80,12 +78,12 @@ describe "A blog post" do
|
|
80
78
|
with_model :Ford, superclass: Car do
|
81
79
|
end
|
82
80
|
|
83
|
-
it
|
81
|
+
it 'has a specified superclass' do
|
84
82
|
expect(Ford < Car).to eq true
|
85
83
|
end
|
86
84
|
end
|
87
85
|
|
88
|
-
describe
|
86
|
+
describe 'with_model can be run within RSpec :all hook' do
|
89
87
|
with_model :BlogPost, scope: :all do
|
90
88
|
table do |t|
|
91
89
|
t.string :title
|
@@ -96,26 +94,26 @@ describe "with_model can be run within RSpec :all hook" do
|
|
96
94
|
BlogPost.create # without scope: :all these will fail
|
97
95
|
end
|
98
96
|
|
99
|
-
it
|
97
|
+
it 'has been initialized within before(:all)' do
|
100
98
|
expect(BlogPost.count).to eq 1
|
101
99
|
end
|
102
100
|
end
|
103
101
|
|
104
|
-
describe
|
105
|
-
it
|
102
|
+
describe 'another example group' do
|
103
|
+
it 'does not have the constant anymore' do
|
106
104
|
expect(defined?(BlogPost)).to be_falsy
|
107
105
|
end
|
108
106
|
end
|
109
107
|
|
110
|
-
describe
|
108
|
+
describe 'with table options' do
|
111
109
|
with_model :WithOptions do
|
112
|
-
table :
|
110
|
+
table id: false do |t|
|
113
111
|
t.string 'foo'
|
114
112
|
t.timestamps null: false
|
115
113
|
end
|
116
114
|
end
|
117
115
|
|
118
|
-
it
|
119
|
-
expect(WithOptions.columns.map(&:name)).to_not include(
|
116
|
+
it 'respects the additional options' do
|
117
|
+
expect(WithOptions.columns.map(&:name)).to_not include('id')
|
120
118
|
end
|
121
119
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler'
|
2
4
|
Bundler.setup
|
3
5
|
|
4
|
-
begin
|
5
|
-
require 'coveralls'
|
6
|
-
Coveralls.wear!
|
7
|
-
rescue LoadError
|
8
|
-
end
|
9
|
-
|
10
6
|
require 'with_model'
|
11
7
|
RSpec.configure do |config|
|
12
8
|
config.extend WithModel
|
@@ -22,13 +18,13 @@ RSpec.configure do |config|
|
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
|
-
is_jruby = RUBY_PLATFORM
|
21
|
+
is_jruby = RUBY_PLATFORM == 'java'
|
26
22
|
adapter = is_jruby ? 'jdbcsqlite3' : 'sqlite3'
|
27
23
|
|
28
24
|
# WithModel requires ActiveRecord::Base.connection to be established.
|
29
25
|
# If ActiveRecord already has a connection, as in a Rails app, this is unnecessary.
|
30
26
|
require 'active_record'
|
31
|
-
ActiveRecord::Base.establish_connection(:
|
27
|
+
ActiveRecord::Base.establish_connection(adapter: adapter, database: ':memory:')
|
32
28
|
|
33
29
|
if defined?(I18n) && I18n.respond_to?(:enforce_available_locales=)
|
34
30
|
I18n.enforce_available_locales = true
|
@@ -41,14 +37,15 @@ end
|
|
41
37
|
|
42
38
|
module SpecHelper
|
43
39
|
module RailsTestCompatability
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
40
|
+
require 'minitest'
|
41
|
+
include Minitest::Assertions
|
42
|
+
|
43
|
+
def assertions
|
44
|
+
@assertions ||= 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def assertions=(value)
|
48
|
+
@assertions = value
|
52
49
|
end
|
53
50
|
end
|
54
51
|
end
|
data/spec/with_model_spec.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_model'
|
2
4
|
require 'spec_helper'
|
3
5
|
|
4
|
-
shared_examples_for
|
6
|
+
shared_examples_for 'ActiveModel' do
|
5
7
|
require 'active_model/lint'
|
6
8
|
include SpecHelper::RailsTestCompatability
|
7
9
|
include ActiveModel::Lint::Tests
|
@@ -10,7 +12,7 @@ shared_examples_for "ActiveModel" do
|
|
10
12
|
active_model_lint_tests = active_model_methods.map(&:to_s).grep(/^test/)
|
11
13
|
|
12
14
|
active_model_lint_tests.each do |method_name|
|
13
|
-
friendly_name = method_name.
|
15
|
+
friendly_name = method_name.tr('_', ' ')
|
14
16
|
example friendly_name do
|
15
17
|
public_send method_name.to_sym
|
16
18
|
end
|
@@ -19,7 +21,7 @@ shared_examples_for "ActiveModel" do
|
|
19
21
|
before { @model = subject }
|
20
22
|
end
|
21
23
|
|
22
|
-
describe
|
24
|
+
describe 'a temporary ActiveRecord model created with with_model' do
|
23
25
|
non_shadowing_example_ran = false
|
24
26
|
|
25
27
|
describe "which doesn't shadow an existing class" do
|
@@ -41,8 +43,8 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
41
43
|
non_shadowing_example_ran = true
|
42
44
|
end
|
43
45
|
|
44
|
-
it
|
45
|
-
record = BlogPost.create!(:
|
46
|
+
it 'acts like a normal ActiveRecord model' do
|
47
|
+
record = BlogPost.create!(title: 'New blog post', content: 'Hello, world!')
|
46
48
|
|
47
49
|
record.reload
|
48
50
|
|
@@ -55,103 +57,113 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
55
57
|
expect { record.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
56
58
|
end
|
57
59
|
|
58
|
-
describe
|
60
|
+
describe 'the class' do
|
59
61
|
subject { BlogPost.new }
|
60
|
-
it_should_behave_like
|
62
|
+
it_should_behave_like 'ActiveModel'
|
61
63
|
end
|
62
64
|
|
63
|
-
it
|
64
|
-
expect(BlogPost.new(:
|
65
|
+
it 'has the methods defined in its model block' do
|
66
|
+
expect(BlogPost.new(title: 'New blog post').fancy_title).to eq 'Title: New blog post'
|
65
67
|
end
|
66
68
|
|
67
|
-
it
|
69
|
+
it 'defines a constant' do
|
68
70
|
expect(BlogPost).to be_a(Class)
|
69
71
|
end
|
70
72
|
|
71
|
-
describe
|
72
|
-
it
|
73
|
+
describe '.with_model?' do
|
74
|
+
it 'returns true' do
|
73
75
|
expect(BlogPost.with_model?).to eq true
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
77
|
-
it
|
79
|
+
it 'is its own base_class' do
|
78
80
|
expect(BlogPost.base_class).to eq BlogPost
|
79
81
|
end
|
80
82
|
end
|
81
83
|
|
82
|
-
context
|
83
|
-
it
|
84
|
+
context 'after an example which uses with_model without shadowing an existing constant' do
|
85
|
+
it 'returns the constant to its undefined state' do
|
84
86
|
expect(non_shadowing_example_ran).to eq true
|
85
87
|
expect(defined?(BlogPost)).to be_falsy
|
86
88
|
end
|
87
89
|
end
|
88
90
|
|
89
|
-
describe
|
91
|
+
describe 'constant restoration' do
|
90
92
|
before { stub_const('MyConst', 1) }
|
91
93
|
shadowing_example_ran = false
|
92
94
|
|
93
|
-
context
|
95
|
+
context 'with the with_model block' do
|
94
96
|
with_model :MyConst
|
95
97
|
|
96
98
|
after do
|
97
99
|
shadowing_example_ran = true
|
98
100
|
end
|
99
101
|
|
100
|
-
it
|
102
|
+
it 'shadows that constant' do
|
101
103
|
expect(MyConst).to be_a(Class)
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
105
|
-
context
|
106
|
-
it
|
107
|
+
context 'without the with_model block' do
|
108
|
+
it 'returns the constant to its original value' do
|
107
109
|
expect(shadowing_example_ran).to eq true
|
108
110
|
expect(MyConst).to eq 1
|
109
111
|
end
|
110
112
|
end
|
111
113
|
end
|
112
114
|
|
113
|
-
describe
|
115
|
+
describe 'with a plural name' do
|
114
116
|
with_model :BlogPosts
|
115
117
|
|
116
|
-
it
|
118
|
+
it 'does not singularize the constant name' do
|
117
119
|
expect(BlogPosts).to be
|
118
|
-
expect(
|
120
|
+
expect(-> { BlogPost }).to raise_error(NameError)
|
119
121
|
end
|
120
122
|
end
|
121
123
|
|
122
|
-
describe
|
124
|
+
describe 'with a name containing capital letters' do
|
123
125
|
with_model :BlogPost
|
124
126
|
|
125
|
-
it
|
127
|
+
it 'tableizes the table name' do
|
126
128
|
expect(BlogPost.table_name).to match(/_blog_posts_/)
|
127
129
|
expect(BlogPost.table_name).to eq BlogPost.table_name.downcase
|
128
130
|
end
|
129
131
|
end
|
130
132
|
|
131
|
-
describe
|
133
|
+
describe 'with a name with underscores' do
|
132
134
|
with_model :blog_post
|
133
135
|
|
134
|
-
it
|
136
|
+
it 'constantizes the name' do
|
135
137
|
expect(BlogPost).to be
|
136
138
|
end
|
137
139
|
|
138
|
-
it
|
140
|
+
it 'tableizes the table name' do
|
139
141
|
expect(BlogPost.table_name).to match(/_blog_posts_/)
|
140
142
|
expect(BlogPost.table_name).to eq BlogPost.table_name.downcase
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
144
|
-
describe
|
146
|
+
describe 'with a name which is namespaced' do
|
147
|
+
before { stub_const('Stuff', Module.new) }
|
148
|
+
with_model :'Stuff::BlogPost'
|
149
|
+
|
150
|
+
it 'creates the model in the namespace' do
|
151
|
+
expect(defined?(::BlogPost)).to be_falsey
|
152
|
+
expect(defined?(::Stuff::BlogPost)).to be_truthy
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'using the constant in the model block' do
|
145
157
|
with_model :BlogPost do
|
146
158
|
model do
|
147
159
|
raise 'I am not myself!' unless self == BlogPost
|
148
160
|
end
|
149
161
|
end
|
150
162
|
|
151
|
-
it
|
163
|
+
it 'is available' do end
|
152
164
|
end
|
153
165
|
|
154
|
-
context
|
166
|
+
context 'with a mixin' do
|
155
167
|
let(:mixin) do
|
156
168
|
Module.new { def foo; end }
|
157
169
|
end
|
@@ -163,18 +175,18 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
163
175
|
end
|
164
176
|
end
|
165
177
|
|
166
|
-
it
|
167
|
-
expect(
|
178
|
+
it 'has the mixin' do
|
179
|
+
expect(-> { ::WithAMixin.new.foo }).to_not raise_error
|
168
180
|
expect(::WithAMixin.include?(AMixin)).to eq true
|
169
181
|
end
|
170
182
|
end
|
171
183
|
|
172
|
-
context
|
184
|
+
context 'with a mixin that has a class_eval' do
|
173
185
|
let(:mixin) do
|
174
186
|
Module.new do
|
175
187
|
def self.included(klass)
|
176
188
|
klass.class_eval do
|
177
|
-
after_save { |object| object.my_method }
|
189
|
+
after_save { |object| object.my_method } # rubocop:disable Style/SymbolProc
|
178
190
|
end
|
179
191
|
end
|
180
192
|
end
|
@@ -190,63 +202,63 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
190
202
|
end
|
191
203
|
end
|
192
204
|
|
193
|
-
it
|
205
|
+
it 'only has one after_save callback' do
|
194
206
|
expect(subject).to receive(:my_method).once
|
195
207
|
subject.save
|
196
208
|
end
|
197
209
|
|
198
|
-
it
|
210
|
+
it 'still only has one after_save callback in future tests' do
|
199
211
|
expect(subject).to receive(:my_method).once
|
200
212
|
subject.save
|
201
213
|
end
|
202
214
|
end
|
203
215
|
|
204
|
-
context
|
216
|
+
context 'with table options' do
|
205
217
|
with_model :WithOptions do
|
206
|
-
table :
|
218
|
+
table id: false do |t|
|
207
219
|
t.string 'foo'
|
208
220
|
t.timestamps null: false
|
209
221
|
end
|
210
222
|
end
|
211
223
|
|
212
|
-
it
|
224
|
+
it 'respects the additional options' do
|
213
225
|
expect(WithOptions.columns.map(&:name)).to_not include('id')
|
214
226
|
end
|
215
227
|
end
|
216
228
|
|
217
|
-
context
|
229
|
+
context 'without a block' do
|
218
230
|
with_model :BlogPost
|
219
231
|
|
220
|
-
it
|
232
|
+
it 'acts like a normal ActiveRecord model' do
|
221
233
|
record = BlogPost.create!
|
222
234
|
record.reload
|
223
235
|
record.destroy
|
224
236
|
expect { record.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
225
237
|
end
|
226
238
|
|
227
|
-
describe
|
239
|
+
describe 'the class' do
|
228
240
|
subject { BlogPost.new }
|
229
|
-
it_should_behave_like
|
241
|
+
it_should_behave_like 'ActiveModel'
|
230
242
|
end
|
231
243
|
end
|
232
244
|
|
233
|
-
context
|
245
|
+
context 'with an empty block' do
|
234
246
|
with_model(:BlogPost) {}
|
235
247
|
|
236
|
-
it
|
248
|
+
it 'acts like a normal ActiveRecord model' do
|
237
249
|
record = BlogPost.create!
|
238
250
|
record.reload
|
239
251
|
record.destroy
|
240
252
|
expect { record.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
241
253
|
end
|
242
254
|
|
243
|
-
describe
|
255
|
+
describe 'the class' do
|
244
256
|
subject { BlogPost.new }
|
245
|
-
it_should_behave_like
|
257
|
+
it_should_behave_like 'ActiveModel'
|
246
258
|
end
|
247
259
|
end
|
248
260
|
|
249
|
-
context
|
261
|
+
context 'without a model block' do
|
250
262
|
with_model :BlogPost do
|
251
263
|
table do |t|
|
252
264
|
t.string 'title'
|
@@ -255,8 +267,8 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
255
267
|
end
|
256
268
|
end
|
257
269
|
|
258
|
-
it
|
259
|
-
record = BlogPost.create!(:
|
270
|
+
it 'acts like a normal ActiveRecord model' do
|
271
|
+
record = BlogPost.create!(title: 'New blog post', content: 'Hello, world!')
|
260
272
|
|
261
273
|
record.reload
|
262
274
|
|
@@ -269,16 +281,16 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
269
281
|
expect { record.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
270
282
|
end
|
271
283
|
|
272
|
-
describe
|
284
|
+
describe 'the class' do
|
273
285
|
subject { BlogPost.new }
|
274
|
-
it_should_behave_like
|
286
|
+
it_should_behave_like 'ActiveModel'
|
275
287
|
end
|
276
288
|
end
|
277
289
|
|
278
|
-
context
|
290
|
+
context 'without a table or model block' do
|
279
291
|
with_model :BlogPost
|
280
292
|
|
281
|
-
it
|
293
|
+
it 'acts like a normal ActiveRecord model' do
|
282
294
|
expect(BlogPost.columns.map(&:name)).to eq ['id']
|
283
295
|
record = BlogPost.create!
|
284
296
|
record.reload
|
@@ -286,23 +298,23 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
286
298
|
expect { record.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
287
299
|
end
|
288
300
|
|
289
|
-
describe
|
301
|
+
describe 'the class' do
|
290
302
|
subject { BlogPost.new }
|
291
|
-
it_should_behave_like
|
303
|
+
it_should_behave_like 'ActiveModel'
|
292
304
|
end
|
293
305
|
end
|
294
306
|
|
295
|
-
context
|
307
|
+
context 'with ActiveSupport::DescendantsTracker' do
|
296
308
|
with_model :BlogPost
|
297
309
|
|
298
|
-
it
|
310
|
+
it 'includes the correct model class in descendants on the first test run' do
|
299
311
|
descendant = ActiveRecord::Base.descendants.detect do |c|
|
300
312
|
c.table_name == BlogPost.table_name
|
301
313
|
end
|
302
314
|
expect(descendant).to eq BlogPost
|
303
315
|
end
|
304
316
|
|
305
|
-
it
|
317
|
+
it 'includes the correct model class in descendants on the second test run' do
|
306
318
|
descendant = ActiveRecord::Base.descendants.detect do |c|
|
307
319
|
c.table_name == BlogPost.table_name
|
308
320
|
end
|
@@ -310,7 +322,7 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
310
322
|
end
|
311
323
|
end
|
312
324
|
|
313
|
-
context
|
325
|
+
context 'with_model can be run within RSpec :all hook' do
|
314
326
|
with_model :BlogPost, scope: :all do
|
315
327
|
table do |t|
|
316
328
|
t.string :title
|
@@ -321,7 +333,7 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
321
333
|
BlogPost.create # without scope: :all these will fail
|
322
334
|
end
|
323
335
|
|
324
|
-
it
|
336
|
+
it 'has been initialized within before(:all)' do
|
325
337
|
expect(BlogPost.count).to eq 1
|
326
338
|
end
|
327
339
|
end
|
@@ -332,7 +344,7 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
332
344
|
end
|
333
345
|
|
334
346
|
after(:all) do
|
335
|
-
Object.
|
347
|
+
Object.__send__(:remove_const, 'BlogPostParent')
|
336
348
|
end
|
337
349
|
|
338
350
|
with_model :BlogPost, superclass: BlogPostParent do
|
@@ -341,20 +353,20 @@ describe "a temporary ActiveRecord model created with with_model" do
|
|
341
353
|
end
|
342
354
|
end
|
343
355
|
|
344
|
-
describe
|
356
|
+
describe 'the class' do
|
345
357
|
subject { BlogPost.new }
|
346
|
-
it_should_behave_like
|
358
|
+
it_should_behave_like 'ActiveModel'
|
347
359
|
end
|
348
360
|
|
349
|
-
it
|
361
|
+
it 'is a subclass of the supplied superclass' do
|
350
362
|
expect(BlogPost < BlogPostParent).to eq true
|
351
363
|
end
|
352
364
|
|
353
|
-
it
|
365
|
+
it 'is its own base_class' do
|
354
366
|
expect(BlogPost.base_class).to eq BlogPost
|
355
367
|
end
|
356
368
|
|
357
|
-
it
|
369
|
+
it 'responds to .with_model? with true' do
|
358
370
|
expect(BlogPost.with_model?).to eq true
|
359
371
|
end
|
360
372
|
end
|