with_model 2.0.0 → 2.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.
- 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
|