declare_schema 3.1.0.colin.1 → 3.1.0.colin.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.
- checksums.yaml +4 -4
- data/.github/workflows/pipeline.yml +1 -1
- data/CHANGELOG.md +3 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +151 -105
- data/config/brakeman.ignore +5 -5
- data/declare_schema.gemspec +1 -1
- data/lib/declare_schema/model/field_spec.rb +10 -2
- data/lib/declare_schema/model/habtm_model_shim.rb +30 -3
- data/lib/declare_schema/model.rb +85 -9
- data/lib/declare_schema/version.rb +1 -1
- data/lib/declare_schema.rb +12 -7
- data/lib/generators/declare_schema/migration/migrator.rb +11 -30
- data/spec/lib/declare_schema/field_spec_spec.rb +32 -0
- data/spec/lib/declare_schema/migration_generator_spec.rb +134 -0
- data/spec/lib/declare_schema/model/habtm_model_shim_spec.rb +13 -7
- data/spec/lib/generators/declare_schema/migration/migrator_default_pk_type_spec.rb +54 -0
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1b65098860b02787bce136f3b694bcd294cdaaa6980716492bcf66b5ab9c4cc9
|
|
4
|
+
data.tar.gz: 9d8ff3e2e7c2cc06886f59b8052ab0f4b2d4bac88ea46ac4ada831000b6cb0a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e7456ba8c763846255997c5fb08a77cb094a199abea888f13c39a9a5dea8e099fe1e09f2a017931eb3b8f1c1e2e7c46b8752dda2510e7f2a892c3f0d2a29a43
|
|
7
|
+
data.tar.gz: 38909be09762f69b40d245385bccf004520142838a95cd27f2c75ef0f82c292a65f301dda444b05e6ce30d76406f712f59c6a9160f8fea9588c1791d3448a8d3
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,9 @@ Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0
|
|
|
8
8
|
### Added
|
|
9
9
|
- Add HABTM support for arbitrary primary key in the referenced table (rather than just :bigint).
|
|
10
10
|
|
|
11
|
+
### Removed
|
|
12
|
+
- Drop support for Rails 6.x. Minimum supported Rails is now 7.0.
|
|
13
|
+
|
|
11
14
|
## [3.0.0] - 2025-04-08
|
|
12
15
|
### Changed
|
|
13
16
|
- The `timestamps` DSL method to create `created_at` and `updated_at` columns now defaults to `null: false` for `datetime` columns
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,126 +1,153 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
declare_schema (3.1.0.colin.
|
|
5
|
-
rails (>=
|
|
4
|
+
declare_schema (3.1.0.colin.2)
|
|
5
|
+
rails (>= 7.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
actioncable (
|
|
11
|
-
actionpack (=
|
|
12
|
-
activesupport (=
|
|
10
|
+
actioncable (7.2.3.1)
|
|
11
|
+
actionpack (= 7.2.3.1)
|
|
12
|
+
activesupport (= 7.2.3.1)
|
|
13
13
|
nio4r (~> 2.0)
|
|
14
14
|
websocket-driver (>= 0.6.1)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
15
|
+
zeitwerk (~> 2.6)
|
|
16
|
+
actionmailbox (7.2.3.1)
|
|
17
|
+
actionpack (= 7.2.3.1)
|
|
18
|
+
activejob (= 7.2.3.1)
|
|
19
|
+
activerecord (= 7.2.3.1)
|
|
20
|
+
activestorage (= 7.2.3.1)
|
|
21
|
+
activesupport (= 7.2.3.1)
|
|
22
|
+
mail (>= 2.8.0)
|
|
23
|
+
actionmailer (7.2.3.1)
|
|
24
|
+
actionpack (= 7.2.3.1)
|
|
25
|
+
actionview (= 7.2.3.1)
|
|
26
|
+
activejob (= 7.2.3.1)
|
|
27
|
+
activesupport (= 7.2.3.1)
|
|
28
|
+
mail (>= 2.8.0)
|
|
29
|
+
rails-dom-testing (~> 2.2)
|
|
30
|
+
actionpack (7.2.3.1)
|
|
31
|
+
actionview (= 7.2.3.1)
|
|
32
|
+
activesupport (= 7.2.3.1)
|
|
33
|
+
cgi
|
|
34
|
+
nokogiri (>= 1.8.5)
|
|
35
|
+
racc
|
|
36
|
+
rack (>= 2.2.4, < 3.3)
|
|
37
|
+
rack-session (>= 1.0.1)
|
|
33
38
|
rack-test (>= 0.6.3)
|
|
34
|
-
rails-dom-testing (~> 2.
|
|
35
|
-
rails-html-sanitizer (~> 1.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
rails-dom-testing (~> 2.2)
|
|
40
|
+
rails-html-sanitizer (~> 1.6)
|
|
41
|
+
useragent (~> 0.16)
|
|
42
|
+
actiontext (7.2.3.1)
|
|
43
|
+
actionpack (= 7.2.3.1)
|
|
44
|
+
activerecord (= 7.2.3.1)
|
|
45
|
+
activestorage (= 7.2.3.1)
|
|
46
|
+
activesupport (= 7.2.3.1)
|
|
47
|
+
globalid (>= 0.6.0)
|
|
41
48
|
nokogiri (>= 1.8.5)
|
|
42
|
-
actionview (
|
|
43
|
-
activesupport (=
|
|
49
|
+
actionview (7.2.3.1)
|
|
50
|
+
activesupport (= 7.2.3.1)
|
|
44
51
|
builder (~> 3.1)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
rails-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
cgi
|
|
53
|
+
erubi (~> 1.11)
|
|
54
|
+
rails-dom-testing (~> 2.2)
|
|
55
|
+
rails-html-sanitizer (~> 1.6)
|
|
56
|
+
activejob (7.2.3.1)
|
|
57
|
+
activesupport (= 7.2.3.1)
|
|
50
58
|
globalid (>= 0.3.6)
|
|
51
|
-
activemodel (
|
|
52
|
-
activesupport (=
|
|
53
|
-
activerecord (
|
|
54
|
-
activemodel (=
|
|
55
|
-
activesupport (=
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
activemodel (7.2.3.1)
|
|
60
|
+
activesupport (= 7.2.3.1)
|
|
61
|
+
activerecord (7.2.3.1)
|
|
62
|
+
activemodel (= 7.2.3.1)
|
|
63
|
+
activesupport (= 7.2.3.1)
|
|
64
|
+
timeout (>= 0.4.0)
|
|
65
|
+
activestorage (7.2.3.1)
|
|
66
|
+
actionpack (= 7.2.3.1)
|
|
67
|
+
activejob (= 7.2.3.1)
|
|
68
|
+
activerecord (= 7.2.3.1)
|
|
69
|
+
activesupport (= 7.2.3.1)
|
|
61
70
|
marcel (~> 1.0)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
activesupport (7.2.3.1)
|
|
72
|
+
base64
|
|
73
|
+
benchmark (>= 0.3)
|
|
74
|
+
bigdecimal
|
|
75
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
76
|
+
connection_pool (>= 2.2.5)
|
|
77
|
+
drb
|
|
65
78
|
i18n (>= 1.6, < 2)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
logger (>= 1.4.2)
|
|
80
|
+
minitest (>= 5.1, < 6)
|
|
81
|
+
securerandom (>= 0.3)
|
|
82
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
69
83
|
appraisal (2.5.0)
|
|
70
84
|
bundler
|
|
71
85
|
rake
|
|
72
86
|
thor (>= 0.14.0)
|
|
73
87
|
ast (2.4.2)
|
|
74
88
|
base64 (0.2.0)
|
|
89
|
+
benchmark (0.5.0)
|
|
75
90
|
bigdecimal (3.1.9)
|
|
76
91
|
bootsnap (1.18.4)
|
|
77
92
|
msgpack (~> 1.2)
|
|
78
93
|
builder (3.3.0)
|
|
79
94
|
byebug (11.1.3)
|
|
95
|
+
cgi (0.5.1)
|
|
80
96
|
climate_control (0.2.0)
|
|
81
97
|
coderay (1.1.3)
|
|
82
98
|
concurrent-ruby (1.3.4)
|
|
99
|
+
connection_pool (3.0.2)
|
|
83
100
|
crass (1.0.6)
|
|
84
|
-
date (3.
|
|
101
|
+
date (3.5.1)
|
|
85
102
|
diff-lcs (1.5.1)
|
|
86
|
-
|
|
103
|
+
drb (2.2.3)
|
|
104
|
+
erb (6.0.4)
|
|
105
|
+
erubi (1.13.1)
|
|
87
106
|
ffi (1.17.0)
|
|
88
|
-
globalid (1.
|
|
107
|
+
globalid (1.3.0)
|
|
89
108
|
activesupport (>= 6.1)
|
|
90
|
-
i18n (1.14.
|
|
109
|
+
i18n (1.14.8)
|
|
91
110
|
concurrent-ruby (~> 1.0)
|
|
111
|
+
io-console (0.8.2)
|
|
112
|
+
irb (1.18.0)
|
|
113
|
+
pp (>= 0.6.0)
|
|
114
|
+
prism (>= 1.3.0)
|
|
115
|
+
rdoc (>= 4.0.0)
|
|
116
|
+
reline (>= 0.4.2)
|
|
92
117
|
json (2.7.2)
|
|
93
118
|
language_server-protocol (3.17.0.3)
|
|
94
119
|
listen (3.9.0)
|
|
95
120
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
96
121
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
97
|
-
|
|
122
|
+
logger (1.7.0)
|
|
123
|
+
loofah (2.25.1)
|
|
98
124
|
crass (~> 1.0.2)
|
|
99
125
|
nokogiri (>= 1.12.0)
|
|
100
|
-
mail (2.
|
|
126
|
+
mail (2.9.0)
|
|
127
|
+
logger
|
|
101
128
|
mini_mime (>= 0.1.1)
|
|
102
129
|
net-imap
|
|
103
130
|
net-pop
|
|
104
131
|
net-smtp
|
|
105
|
-
marcel (1.0
|
|
132
|
+
marcel (1.1.0)
|
|
106
133
|
method_source (1.1.0)
|
|
107
134
|
mini_mime (1.1.5)
|
|
108
|
-
mini_portile2 (2.8.
|
|
109
|
-
minitest (5.
|
|
135
|
+
mini_portile2 (2.8.9)
|
|
136
|
+
minitest (5.27.0)
|
|
110
137
|
msgpack (1.7.2)
|
|
111
138
|
mutex_m (0.3.0)
|
|
112
139
|
mysql2 (0.5.6)
|
|
113
|
-
net-imap (0.4
|
|
140
|
+
net-imap (0.6.4)
|
|
114
141
|
date
|
|
115
142
|
net-protocol
|
|
116
143
|
net-pop (0.1.2)
|
|
117
144
|
net-protocol
|
|
118
145
|
net-protocol (0.2.2)
|
|
119
146
|
timeout
|
|
120
|
-
net-smtp (0.5.
|
|
147
|
+
net-smtp (0.5.1)
|
|
121
148
|
net-protocol
|
|
122
|
-
nio4r (2.7.
|
|
123
|
-
nokogiri (1.
|
|
149
|
+
nio4r (2.7.5)
|
|
150
|
+
nokogiri (1.19.3)
|
|
124
151
|
mini_portile2 (~> 2.8.2)
|
|
125
152
|
racc (~> 1.4)
|
|
126
153
|
parallel (1.26.2)
|
|
@@ -128,54 +155,75 @@ GEM
|
|
|
128
155
|
ast (~> 2.4.1)
|
|
129
156
|
racc
|
|
130
157
|
pg (1.5.7)
|
|
158
|
+
pp (0.6.3)
|
|
159
|
+
prettyprint
|
|
160
|
+
prettyprint (0.2.0)
|
|
161
|
+
prism (1.9.0)
|
|
131
162
|
pry (0.14.2)
|
|
132
163
|
coderay (~> 1.1)
|
|
133
164
|
method_source (~> 1.0)
|
|
134
165
|
pry-byebug (3.10.1)
|
|
135
166
|
byebug (~> 11.0)
|
|
136
167
|
pry (>= 0.13, < 0.15)
|
|
168
|
+
psych (5.3.1)
|
|
169
|
+
date
|
|
170
|
+
stringio
|
|
137
171
|
racc (1.8.1)
|
|
138
|
-
rack (
|
|
139
|
-
rack-
|
|
172
|
+
rack (3.2.6)
|
|
173
|
+
rack-session (2.1.2)
|
|
174
|
+
base64 (>= 0.1.0)
|
|
175
|
+
rack (>= 3.0.0)
|
|
176
|
+
rack-test (2.2.0)
|
|
140
177
|
rack (>= 1.3)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
178
|
+
rackup (2.3.1)
|
|
179
|
+
rack (>= 3)
|
|
180
|
+
rails (7.2.3.1)
|
|
181
|
+
actioncable (= 7.2.3.1)
|
|
182
|
+
actionmailbox (= 7.2.3.1)
|
|
183
|
+
actionmailer (= 7.2.3.1)
|
|
184
|
+
actionpack (= 7.2.3.1)
|
|
185
|
+
actiontext (= 7.2.3.1)
|
|
186
|
+
actionview (= 7.2.3.1)
|
|
187
|
+
activejob (= 7.2.3.1)
|
|
188
|
+
activemodel (= 7.2.3.1)
|
|
189
|
+
activerecord (= 7.2.3.1)
|
|
190
|
+
activestorage (= 7.2.3.1)
|
|
191
|
+
activesupport (= 7.2.3.1)
|
|
153
192
|
bundler (>= 1.15.0)
|
|
154
|
-
railties (=
|
|
155
|
-
|
|
156
|
-
rails-dom-testing (2.2.0)
|
|
193
|
+
railties (= 7.2.3.1)
|
|
194
|
+
rails-dom-testing (2.3.0)
|
|
157
195
|
activesupport (>= 5.0.0)
|
|
158
196
|
minitest
|
|
159
197
|
nokogiri (>= 1.6)
|
|
160
|
-
rails-html-sanitizer (1.
|
|
161
|
-
loofah (~> 2.
|
|
162
|
-
nokogiri (
|
|
163
|
-
railties (
|
|
164
|
-
actionpack (=
|
|
165
|
-
activesupport (=
|
|
166
|
-
|
|
198
|
+
rails-html-sanitizer (1.7.0)
|
|
199
|
+
loofah (~> 2.25)
|
|
200
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
201
|
+
railties (7.2.3.1)
|
|
202
|
+
actionpack (= 7.2.3.1)
|
|
203
|
+
activesupport (= 7.2.3.1)
|
|
204
|
+
cgi
|
|
205
|
+
irb (~> 1.13)
|
|
206
|
+
rackup (>= 1.0.0)
|
|
167
207
|
rake (>= 12.2)
|
|
168
|
-
thor (~> 1.0)
|
|
208
|
+
thor (~> 1.0, >= 1.2.2)
|
|
209
|
+
tsort (>= 0.2)
|
|
210
|
+
zeitwerk (~> 2.6)
|
|
169
211
|
rainbow (3.1.1)
|
|
170
|
-
rake (13.2
|
|
212
|
+
rake (13.4.2)
|
|
171
213
|
rb-fsevent (0.11.2)
|
|
172
214
|
rb-inotify (0.11.1)
|
|
173
215
|
ffi (~> 1.0)
|
|
216
|
+
rdoc (7.2.0)
|
|
217
|
+
erb
|
|
218
|
+
psych (>= 4.0.0)
|
|
219
|
+
tsort
|
|
174
220
|
regexp_parser (2.9.2)
|
|
221
|
+
reline (0.6.3)
|
|
222
|
+
io-console (~> 0.5)
|
|
175
223
|
responders (3.1.1)
|
|
176
224
|
actionpack (>= 5.2)
|
|
177
225
|
railties (>= 5.2)
|
|
178
|
-
rexml (3.
|
|
226
|
+
rexml (3.4.4)
|
|
179
227
|
rspec (3.13.0)
|
|
180
228
|
rspec-core (~> 3.13.0)
|
|
181
229
|
rspec-expectations (~> 3.13.0)
|
|
@@ -206,25 +254,23 @@ GEM
|
|
|
206
254
|
rubocop-ast (1.32.0)
|
|
207
255
|
parser (>= 3.3.1.0)
|
|
208
256
|
ruby-progressbar (1.13.0)
|
|
209
|
-
|
|
210
|
-
concurrent-ruby (~> 1.0)
|
|
211
|
-
rack (>= 2.2.4, < 4)
|
|
212
|
-
sprockets-rails (3.5.2)
|
|
213
|
-
actionpack (>= 6.1)
|
|
214
|
-
activesupport (>= 6.1)
|
|
215
|
-
sprockets (>= 3.0.0)
|
|
257
|
+
securerandom (0.4.1)
|
|
216
258
|
sqlite3 (1.7.3)
|
|
217
259
|
mini_portile2 (~> 2.8.0)
|
|
218
|
-
|
|
219
|
-
|
|
260
|
+
stringio (3.2.0)
|
|
261
|
+
thor (1.5.0)
|
|
262
|
+
timeout (0.6.1)
|
|
263
|
+
tsort (0.2.0)
|
|
220
264
|
tzinfo (2.0.6)
|
|
221
265
|
concurrent-ruby (~> 1.0)
|
|
222
266
|
unicode-display_width (2.5.0)
|
|
223
|
-
|
|
267
|
+
useragent (0.16.11)
|
|
268
|
+
websocket-driver (0.8.0)
|
|
269
|
+
base64
|
|
224
270
|
websocket-extensions (>= 0.1.0)
|
|
225
271
|
websocket-extensions (0.1.5)
|
|
226
272
|
yard (0.9.36)
|
|
227
|
-
zeitwerk (2.
|
|
273
|
+
zeitwerk (2.7.5)
|
|
228
274
|
|
|
229
275
|
PLATFORMS
|
|
230
276
|
ruby
|
|
@@ -245,7 +291,7 @@ DEPENDENCIES
|
|
|
245
291
|
pg (~> 1.1)
|
|
246
292
|
pry
|
|
247
293
|
pry-byebug
|
|
248
|
-
rails (~>
|
|
294
|
+
rails (~> 7.0)
|
|
249
295
|
responders
|
|
250
296
|
rspec
|
|
251
297
|
rspec-its
|
data/config/brakeman.ignore
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
{
|
|
4
4
|
"warning_type": "Command Injection",
|
|
5
5
|
"warning_code": 14,
|
|
6
|
-
"fingerprint": "
|
|
6
|
+
"fingerprint": "069879c09668f26db81818bd906f169f4f3be6dc661c9b223fcd6f53b2155369",
|
|
7
7
|
"check_name": "Execute",
|
|
8
8
|
"message": "Possible command injection",
|
|
9
9
|
"file": "lib/declare_schema/command.rb",
|
|
10
|
-
"line":
|
|
10
|
+
"line": 55,
|
|
11
11
|
"link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
|
|
12
12
|
"code": "system(\"bundle exec rails #{Regexp.last_match(1)} declare_schema:#{(args * \" \")}\")",
|
|
13
13
|
"render_path": null,
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
{
|
|
27
27
|
"warning_type": "Command Injection",
|
|
28
28
|
"warning_code": 14,
|
|
29
|
-
"fingerprint": "
|
|
29
|
+
"fingerprint": "9914293cc386897d62f7f645fd2e0bfa51c582f3058ac34357f61b04866a33fe",
|
|
30
30
|
"check_name": "Execute",
|
|
31
31
|
"message": "Possible command injection",
|
|
32
32
|
"file": "lib/declare_schema/command.rb",
|
|
33
33
|
"line": 45,
|
|
34
34
|
"link": "https://brakemanscanner.org/docs/warning_types/command_injection/",
|
|
35
|
-
"code": "system(\"rails new #{\"new\"} #{(args * \" \")} -m #{File.join(Dir.tmpdir, \"declare_schema_app_template\")}
|
|
35
|
+
"code": "system(\"rails new #{\"new\"} #{(args * \" \")} -m #{File.join(Dir.tmpdir, \"declare_schema_app_template\")}\")",
|
|
36
36
|
"render_path": null,
|
|
37
37
|
"location": {
|
|
38
38
|
"type": "method",
|
|
@@ -66,6 +66,6 @@
|
|
|
66
66
|
"note": ""
|
|
67
67
|
}
|
|
68
68
|
],
|
|
69
|
-
"updated": "
|
|
69
|
+
"updated": "2026-05-02 06:56:54 -0700",
|
|
70
70
|
"brakeman_version": "5.4.1"
|
|
71
71
|
}
|
data/declare_schema.gemspec
CHANGED
|
@@ -46,12 +46,13 @@ module DeclareSchema
|
|
|
46
46
|
define_method(option) { @options[option] }
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def initialize(model, name, type, position: 0, **options)
|
|
49
|
+
def initialize(model, name, type, position: 0, resolver: nil, **options)
|
|
50
50
|
@model = model
|
|
51
51
|
@name = name.to_sym
|
|
52
52
|
type.is_a?(Symbol) or raise ArgumentError, "type must be a Symbol; got #{type.inspect}"
|
|
53
53
|
@type = TYPE_SYNONYMS[type] || type
|
|
54
54
|
@position = position
|
|
55
|
+
@resolver = resolver
|
|
55
56
|
@options = options.dup
|
|
56
57
|
|
|
57
58
|
@options.has_key?(:null) or @options[:null] = ::DeclareSchema.default_null
|
|
@@ -120,7 +121,14 @@ module DeclareSchema
|
|
|
120
121
|
@sql_options = @options.slice(*SQL_OPTIONS)
|
|
121
122
|
end
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
# Returns the final FieldSpec, invoking the deferred-resolution callback if one
|
|
125
|
+
# was supplied. Specs without a `resolver:` simply return `self`. Result is
|
|
126
|
+
# memoized so repeated calls (e.g. across migration generation passes) are cheap.
|
|
127
|
+
def resolve
|
|
128
|
+
@resolved ||= @resolver ? @resolver.call(self) : self
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def foreign_key_field_spec(model, name, position:, null:)
|
|
124
132
|
self.class.new(model, name, @type, position:, **@options.merge(null.nil? ? {} : { null: }))
|
|
125
133
|
end
|
|
126
134
|
|
|
@@ -39,15 +39,42 @@ module DeclareSchema
|
|
|
39
39
|
join_table
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# The migrator (in change_column_back / add_column_back / etc.) wraps column
|
|
43
|
+
# introspection in `with_previous_model_table_name`, which expects a model that
|
|
44
|
+
# exposes `table_name=`. Real AR classes do; this shim doesn't keep AR-style
|
|
45
|
+
# cached metadata keyed on table_name, so we just rewrite @join_table -- the
|
|
46
|
+
# only state our `table_name` reads.
|
|
47
|
+
def table_name=(new_table_name)
|
|
48
|
+
@join_table = new_table_name
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Mirror the AR class API for `model.columns_hash` so the migrator's
|
|
52
|
+
# change_column_back / add_column_back paths work uniformly across real AR
|
|
53
|
+
# models and HABTM shims. Backed by `connection.columns(table_name)` rather
|
|
54
|
+
# than AR's class-level cache because the shim has no class-level cache.
|
|
55
|
+
def columns_hash
|
|
56
|
+
connection.columns(table_name).index_by(&:name)
|
|
57
|
+
end
|
|
58
|
+
|
|
42
59
|
def field_specs
|
|
43
60
|
foreign_keys.each_with_index.each_with_object({}) do |(foreign_key, i), result|
|
|
44
|
-
|
|
61
|
+
parent_model = parent_models[i]
|
|
62
|
+
result[foreign_key] =
|
|
63
|
+
if parent_model.respond_to?(:_foreign_key_field_spec)
|
|
64
|
+
# declare_schema model: mirror the parent's primary key (type, limit, etc.)
|
|
65
|
+
parent_model._foreign_key_field_spec(self, foreign_key, position: i, null: false)
|
|
66
|
+
else
|
|
67
|
+
# Non-declare_schema parent: fall back to the configured default PK type.
|
|
68
|
+
::DeclareSchema::Model::FieldSpec.new(self, foreign_key, ::DeclareSchema.default_generated_primary_key_type, position: i, null: false)
|
|
69
|
+
end
|
|
45
70
|
end
|
|
46
71
|
end
|
|
47
72
|
|
|
73
|
+
# The HABTM join table's primary key is the composite of its two foreign keys.
|
|
74
|
+
# (Rails 7.1+ supports composite PKs natively; on 7.0 nothing in AR introspects this
|
|
75
|
+
# shim, so returning an Array is safe for our callers.)
|
|
48
76
|
def primary_key
|
|
49
|
-
|
|
50
|
-
false # no single-column primary key in database
|
|
77
|
+
foreign_keys
|
|
51
78
|
end
|
|
52
79
|
|
|
53
80
|
def _declared_primary_key
|
data/lib/declare_schema/model.rb
CHANGED
|
@@ -227,19 +227,80 @@ module DeclareSchema
|
|
|
227
227
|
end
|
|
228
228
|
|
|
229
229
|
# Returns a FieldSpec for the foreign key column of a belongs_to association.
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
230
|
+
# - For a polymorphic association, the FK uses `DeclareSchema.default_generated_primary_key_type`
|
|
231
|
+
# (mirroring `config.generators.primary_key_type`, default :bigint), or :integer with the
|
|
232
|
+
# existing column's limit if the column already exists in the database.
|
|
233
|
+
# - For a non-polymorphic association, the FK should mirror the primary key it points
|
|
234
|
+
# at (same data type, same options like limit:, charset:, etc.). However we cannot
|
|
235
|
+
# load the parent model right now (at `belongs_to` time) without risking dependency
|
|
236
|
+
# cycles between models, so we install a `resolver:` callback. The migration
|
|
237
|
+
# generator calls that resolver at generation time -- after all models are
|
|
238
|
+
# eager-loaded -- and the resolver returns a fully-mirrored FieldSpec that the
|
|
239
|
+
# generator swaps in for this default_spec.
|
|
233
240
|
def _infer_foreign_key_field_spec(foreign_key_column_name, reflection, column_options)
|
|
234
241
|
if reflection.options[:polymorphic]
|
|
235
242
|
if (foreign_key_column = _column(foreign_key_column_name)) && foreign_key_column.type == :integer
|
|
236
243
|
# grandfather foreign key column to match what's in the database
|
|
237
244
|
column_options = column_options.merge(limit: foreign_key_column.limit)
|
|
238
245
|
end
|
|
239
|
-
FieldSpec.new(self, foreign_key_column_name,
|
|
246
|
+
FieldSpec.new(self, foreign_key_column_name, DeclareSchema.default_generated_primary_key_type, position: field_specs.size, **column_options)
|
|
240
247
|
else
|
|
241
|
-
|
|
242
|
-
|
|
248
|
+
# Capture only what we need from `reflection` (no `reflection.klass` here -- that
|
|
249
|
+
# would force the parent model to load, which is exactly the cycle we are avoiding).
|
|
250
|
+
# `reflection.klass` is resolved lazily inside the block below.
|
|
251
|
+
resolver = ->(default_spec) do
|
|
252
|
+
_resolve_belongs_to_foreign_key_field_spec(reflection, default_spec)
|
|
253
|
+
end
|
|
254
|
+
FieldSpec.new(self, foreign_key_column_name, DeclareSchema.default_generated_primary_key_type,
|
|
255
|
+
position: field_specs.size, resolver:, **column_options)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# Called at migration generation time to mirror the parent model's primary key.
|
|
260
|
+
# Always returns a FieldSpec: the default_spec unchanged when the parent class is not
|
|
261
|
+
# a declare_schema model (we can't ask for its PK spec, so the configured default PK
|
|
262
|
+
# type is the best we can offer without inspecting the DB), otherwise a fully mirrored
|
|
263
|
+
# FieldSpec.
|
|
264
|
+
#
|
|
265
|
+
# Reconciliation with the live DB: if the parent's PK column already exists in the
|
|
266
|
+
# database with the same Rails type but a different :limit (e.g. a legacy table where
|
|
267
|
+
# `id` is INT(4) but the model now declares the default :bigint), prefer the live
|
|
268
|
+
# column's :limit so the FK matches what's actually on disk. This preserves the
|
|
269
|
+
# behavior of the old DB-column lookup (formerly `fk_field_options`) without
|
|
270
|
+
# overriding intentional type changes.
|
|
271
|
+
def _resolve_belongs_to_foreign_key_field_spec(reflection, default_spec)
|
|
272
|
+
klass = reflection.klass or
|
|
273
|
+
raise "Couldn't find belongs_to klass for #{reflection.name} on #{name} in #{reflection.inspect}"
|
|
274
|
+
|
|
275
|
+
if klass.respond_to?(:_primary_key_field_spec)
|
|
276
|
+
_mirror_parent_primary_key(klass, default_spec)
|
|
277
|
+
else
|
|
278
|
+
default_spec
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
# Build a FieldSpec for the FK by mirroring the parent's declared primary key,
|
|
283
|
+
# then reconciling against the live DB column when it differs only in :limit
|
|
284
|
+
# (see _resolve_belongs_to_foreign_key_field_spec for the full rationale).
|
|
285
|
+
def _mirror_parent_primary_key(klass, default_spec)
|
|
286
|
+
spec = klass._primary_key_field_spec.foreign_key_field_spec(
|
|
287
|
+
default_spec.model, default_spec.name,
|
|
288
|
+
position: default_spec.position, null: default_spec.null
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Look up the parent's live PK column directly (not via _column, whose
|
|
292
|
+
# @table_exists memoization can pin to a stale value when the parent table
|
|
293
|
+
# is created after the model class is first defined). The rescue covers
|
|
294
|
+
# the table-doesn't-exist-yet case (greenfield migration).
|
|
295
|
+
live_pk_column = klass.columns_hash[klass._declared_primary_key.to_s] rescue nil
|
|
296
|
+
if live_pk_column && live_pk_column.type == spec.type && live_pk_column.limit && live_pk_column.limit != spec.limit
|
|
297
|
+
FieldSpec.new(
|
|
298
|
+
spec.model, spec.name, spec.type,
|
|
299
|
+
position: spec.position,
|
|
300
|
+
**spec.options.merge(limit: live_pk_column.limit)
|
|
301
|
+
)
|
|
302
|
+
else
|
|
303
|
+
spec
|
|
243
304
|
end
|
|
244
305
|
end
|
|
245
306
|
|
|
@@ -269,14 +330,29 @@ module DeclareSchema
|
|
|
269
330
|
end
|
|
270
331
|
|
|
271
332
|
def _primary_key_field_spec_from_table_options(declared_primary_key)
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
333
|
+
# _table_options is nil on STI subclasses that never call `declare_schema` themselves:
|
|
334
|
+
# they inherit `field_specs` etc. via `inheriting_cattr_reader`, but `@_table_options`
|
|
335
|
+
# is a plain class-instance variable on each class, so the subclass's reader returns
|
|
336
|
+
# nil. Treat that the same as an empty options hash and fall through to
|
|
337
|
+
# `default_generated_primary_key_type` below.
|
|
338
|
+
type, options = _parse_pk_table_options(_table_options&.[](declared_primary_key.to_sym))
|
|
339
|
+
type ||= DeclareSchema.default_generated_primary_key_type
|
|
275
340
|
FieldSpec.new(self, declared_primary_key, type, **options)
|
|
276
341
|
end
|
|
277
342
|
|
|
278
343
|
private
|
|
279
344
|
|
|
345
|
+
# `declare_schema id: ...` accepts either a Hash (`id: { type: :integer, limit: 4 }`)
|
|
346
|
+
# or a bare type Symbol (`id: :integer`). Returns [type, options_hash], with type == nil
|
|
347
|
+
# when value is neither (caller falls back to a default).
|
|
348
|
+
def _parse_pk_table_options(value)
|
|
349
|
+
case value
|
|
350
|
+
when Hash then [value[:type], value.except(:type)]
|
|
351
|
+
when Symbol then [value, {}]
|
|
352
|
+
else [nil, {}]
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
280
356
|
# if this is a derived class, returns the base class's _declared_primary_key
|
|
281
357
|
# otherwise, returns 'id'
|
|
282
358
|
def _default_declared_primary_key
|
data/lib/declare_schema.rb
CHANGED
|
@@ -65,7 +65,7 @@ module DeclareSchema
|
|
|
65
65
|
if mysql_version && mysql_version >= SEMVER_8 && charset == 'utf8'
|
|
66
66
|
'utf8mb3'
|
|
67
67
|
else
|
|
68
|
-
charset
|
|
68
|
+
charset
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
|
|
@@ -74,7 +74,7 @@ module DeclareSchema
|
|
|
74
74
|
collation.sub(/\Autf8_/, 'utf8mb3_')
|
|
75
75
|
else
|
|
76
76
|
collation
|
|
77
|
-
end
|
|
77
|
+
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def default_charset=(charset)
|
|
@@ -162,11 +162,16 @@ module DeclareSchema
|
|
|
162
162
|
end
|
|
163
163
|
|
|
164
164
|
def current_adapter(model_class = ActiveRecord::Base)
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
model_class.connection_db_config.adapter
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Default primary key type for generated foreign keys when we have no parent
|
|
169
|
+
# model to mirror (e.g. polymorphic FKs, non-declare_schema parents). Mirrors
|
|
170
|
+
# `config.generators.primary_key_type` so apps that override the Rails default
|
|
171
|
+
# get consistent behavior. Memoized — Rails config is set at boot.
|
|
172
|
+
def default_generated_primary_key_type
|
|
173
|
+
@default_generated_primary_key_type ||=
|
|
174
|
+
Rails.application.config.generators.options.dig(:active_record, :primary_key_type) || :bigint
|
|
170
175
|
end
|
|
171
176
|
end
|
|
172
177
|
end
|
|
@@ -187,11 +187,7 @@ module Generators
|
|
|
187
187
|
models, db_tables = models_and_tables
|
|
188
188
|
models_by_table_name = {}
|
|
189
189
|
models.each do |m|
|
|
190
|
-
m.try(:field_specs)&.
|
|
191
|
-
if (pre_migration = field_spec.options.delete(:pre_migration))
|
|
192
|
-
pre_migration.call(field_spec)
|
|
193
|
-
end
|
|
194
|
-
end
|
|
190
|
+
m.try(:field_specs)&.transform_values!(&:resolve)
|
|
195
191
|
|
|
196
192
|
if !models_by_table_name.has_key?(m.table_name)
|
|
197
193
|
models_by_table_name[m.table_name] = m
|
|
@@ -379,9 +375,14 @@ module Generators
|
|
|
379
375
|
adds = to_add.map do |col_name_to_add|
|
|
380
376
|
type, options =
|
|
381
377
|
if (spec = model.field_specs[col_name_to_add])
|
|
382
|
-
[spec.type, spec.sql_options.
|
|
378
|
+
[spec.type, spec.sql_options.compact]
|
|
383
379
|
else
|
|
384
|
-
|
|
380
|
+
# No FieldSpec for this column: it's the declared PK appended at line 366
|
|
381
|
+
# because it isn't in the DB yet. Use the configured default PK type so
|
|
382
|
+
# apps that override config.generators.primary_key_type get consistent
|
|
383
|
+
# behavior; the actual PRIMARY KEY designation is added separately by
|
|
384
|
+
# PrimaryKeyChange.
|
|
385
|
+
[::DeclareSchema.default_generated_primary_key_type, {}]
|
|
385
386
|
end
|
|
386
387
|
::DeclareSchema::SchemaChange::ColumnAdd.new(new_table_name, col_name_to_add, type, **options)
|
|
387
388
|
end
|
|
@@ -400,13 +401,12 @@ module Generators
|
|
|
400
401
|
spec_attrs = spec.schema_attributes(column)
|
|
401
402
|
column_declaration = ::DeclareSchema::Model::Column.new(model, current_table_name, column)
|
|
402
403
|
col_attrs = column_declaration.schema_attributes
|
|
403
|
-
normalized_schema_attrs = spec_attrs.merge(fk_field_options(model, col_name_to_change))
|
|
404
404
|
|
|
405
|
-
if !::DeclareSchema::Model::Column.equivalent_schema_attributes?(
|
|
406
|
-
type =
|
|
405
|
+
if !::DeclareSchema::Model::Column.equivalent_schema_attributes?(spec_attrs, col_attrs)
|
|
406
|
+
type = spec_attrs.delete(:type) or raise "no :type found in #{spec_attrs.inspect}"
|
|
407
407
|
old_type, old_options = change_column_back(model, current_table_name, orig_col_name)
|
|
408
408
|
changes << ::DeclareSchema::SchemaChange::ColumnChange.new(new_table_name, col_name_to_change,
|
|
409
|
-
new_type: type, new_options:
|
|
409
|
+
new_type: type, new_options: spec_attrs,
|
|
410
410
|
old_type: old_type, old_options: old_options)
|
|
411
411
|
end
|
|
412
412
|
end
|
|
@@ -544,25 +544,6 @@ module Generators
|
|
|
544
544
|
end
|
|
545
545
|
end
|
|
546
546
|
|
|
547
|
-
# TODO: switch this to depend on _infer_foreign_key_field_spec instead
|
|
548
|
-
def fk_field_options(model, field_name)
|
|
549
|
-
# check if the field_name is a foreign key
|
|
550
|
-
if (foreign_key = model.constraint_definitions.find { field_name == _1.foreign_key_column })
|
|
551
|
-
# if so, look up the target table's primary key column to get its limit (note: this is looking in the DB, not the spec)
|
|
552
|
-
parent_columns = connection.columns(foreign_key.parent_table_name) rescue []
|
|
553
|
-
pk_limit =
|
|
554
|
-
if (pk_column = parent_columns.find { _1.name.to_s == "id" }) # right now foreign keys assume id is the target
|
|
555
|
-
pk_column.limit
|
|
556
|
-
else
|
|
557
|
-
8
|
|
558
|
-
end
|
|
559
|
-
|
|
560
|
-
{ limit: pk_limit }
|
|
561
|
-
else
|
|
562
|
-
{}
|
|
563
|
-
end
|
|
564
|
-
end
|
|
565
|
-
|
|
566
547
|
def change_table_options(model, current_table_name)
|
|
567
548
|
old_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.for_model(model, current_table_name)
|
|
568
549
|
new_options_definition = ::DeclareSchema::Model::TableOptionsDefinition.new(model.table_name, **table_options_for_model(model))
|
|
@@ -25,6 +25,38 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
describe '#resolve' do
|
|
29
|
+
let(:default_spec) { described_class.new(model, :advertiser_id, :integer, limit: 8, null: false, position: 1) }
|
|
30
|
+
let(:mirrored_spec) { described_class.new(model, :advertiser_id, :string, limit: 36, null: false, position: 1) }
|
|
31
|
+
|
|
32
|
+
context 'when no resolver was supplied' do
|
|
33
|
+
it 'returns self' do
|
|
34
|
+
expect(default_spec.resolve).to equal(default_spec)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
context 'when a resolver was supplied' do
|
|
39
|
+
it 'invokes the resolver with self and returns its result' do
|
|
40
|
+
captured = nil
|
|
41
|
+
spec_under_test = described_class.new(model, :advertiser_id, :integer, limit: 8, null: false, position: 1,
|
|
42
|
+
resolver: ->(spec) { captured = spec; mirrored_spec })
|
|
43
|
+
|
|
44
|
+
expect(spec_under_test.resolve).to equal(mirrored_spec)
|
|
45
|
+
expect(captured).to equal(spec_under_test)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'memoizes the resolver result' do
|
|
49
|
+
call_count = 0
|
|
50
|
+
spec_under_test = described_class.new(model, :advertiser_id, :integer, limit: 8, null: false, position: 1,
|
|
51
|
+
resolver: ->(_spec) { call_count += 1; mirrored_spec })
|
|
52
|
+
|
|
53
|
+
3.times { spec_under_test.resolve }
|
|
54
|
+
|
|
55
|
+
expect(call_count).to eq(1)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
28
60
|
describe '#schema_attributes' do
|
|
29
61
|
describe 'integer 4' do
|
|
30
62
|
subject { described_class.new(model, :price, :integer, limit: 4, null: false, position: 0) }
|
|
@@ -988,6 +988,140 @@ RSpec.describe 'DeclareSchema Migration Generator' do
|
|
|
988
988
|
nuke_model_class(Creative)
|
|
989
989
|
end
|
|
990
990
|
|
|
991
|
+
context 'foreign keys mirror non-default primary key types' do
|
|
992
|
+
after do
|
|
993
|
+
nuke_model_class(SpecialOrder) if defined?(SpecialOrder)
|
|
994
|
+
nuke_model_class(Order) if defined?(Order)
|
|
995
|
+
nuke_model_class(LineItem) if defined?(LineItem)
|
|
996
|
+
nuke_model_class(Tag) if defined?(Tag)
|
|
997
|
+
nuke_model_class(Product) if defined?(Product)
|
|
998
|
+
nuke_model_class(ProductsTagsClass) if defined?(ProductsTagsClass)
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
it 'belongs_to defers parent class lookup to migration time (no dependency cycle)' do
|
|
1002
|
+
# LineItem.belongs_to :order is allowed to mention :order before Order is loaded.
|
|
1003
|
+
# If we eagerly resolved reflection.klass at belongs_to time this would raise
|
|
1004
|
+
# NameError: uninitialized constant LineItem::Order.
|
|
1005
|
+
expect do
|
|
1006
|
+
class LineItem < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1007
|
+
declare_schema { integer :quantity, null: false }
|
|
1008
|
+
belongs_to :order
|
|
1009
|
+
end
|
|
1010
|
+
end.to_not raise_error
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
it 'belongs_to mirrors the parent primary key type and limit (declared via _table_options)' do
|
|
1014
|
+
# Order has a non-default :string primary key; LineItem.belongs_to :order should
|
|
1015
|
+
# produce a :string foreign key with the same limit -- without forcing Order to
|
|
1016
|
+
# load when LineItem is defined.
|
|
1017
|
+
class LineItem < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1018
|
+
declare_schema do
|
|
1019
|
+
integer :quantity, null: false
|
|
1020
|
+
end
|
|
1021
|
+
belongs_to :order
|
|
1022
|
+
end
|
|
1023
|
+
|
|
1024
|
+
class Order < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1025
|
+
self.primary_key = "id"
|
|
1026
|
+
declare_schema id: { type: :string, limit: 36 } do
|
|
1027
|
+
string :customer_email, limit: 250, null: false
|
|
1028
|
+
end
|
|
1029
|
+
end
|
|
1030
|
+
|
|
1031
|
+
up, _ = Generators::DeclareSchema::Migration::Migrator.run
|
|
1032
|
+
|
|
1033
|
+
expect(up).to match(/t\.string\s+:order_id, limit: 36, null: false/)
|
|
1034
|
+
end
|
|
1035
|
+
|
|
1036
|
+
context 'when belongs_to points at an STI subclass' do
|
|
1037
|
+
# An STI subclass inherits field_specs (via inheriting_cattr_reader) and the
|
|
1038
|
+
# `_primary_key_field_spec` class method (mixed into its parent), but it never
|
|
1039
|
+
# calls `declare_schema` on itself, so its own `@_table_options` is nil. When a
|
|
1040
|
+
# belongs_to points at the STI subclass, the migrator's resolver calls
|
|
1041
|
+
# `klass._primary_key_field_spec`, which previously crashed with
|
|
1042
|
+
# `NoMethodError: undefined method '[]' for nil` because
|
|
1043
|
+
# `_primary_key_field_spec_from_table_options` indexed `_table_options` without
|
|
1044
|
+
# a nil guard.
|
|
1045
|
+
before do
|
|
1046
|
+
class Order < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1047
|
+
declare_schema do
|
|
1048
|
+
string :customer_email, limit: 250, null: false
|
|
1049
|
+
end
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
class SpecialOrder < Order # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1053
|
+
end
|
|
1054
|
+
|
|
1055
|
+
class LineItem < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1056
|
+
declare_schema do
|
|
1057
|
+
integer :quantity, null: false
|
|
1058
|
+
end
|
|
1059
|
+
belongs_to :special_order
|
|
1060
|
+
end
|
|
1061
|
+
end
|
|
1062
|
+
|
|
1063
|
+
let(:up) { Generators::DeclareSchema::Migration::Migrator.run.first }
|
|
1064
|
+
|
|
1065
|
+
it 'mirrors the base class primary key without crashing' do
|
|
1066
|
+
expect(up).to match(/t\.integer\s+:special_order_id, limit: 8, null: false/)
|
|
1067
|
+
end
|
|
1068
|
+
end
|
|
1069
|
+
|
|
1070
|
+
context 'when a HABTM join table has a parent PK type that drifts from the live column' do
|
|
1071
|
+
before do
|
|
1072
|
+
conn = ActiveRecord::Base.connection
|
|
1073
|
+
conn.create_table(:products) { |t| t.string :name, limit: 100, null: false }
|
|
1074
|
+
conn.create_table(:tags) { |t| t.string :label, limit: 50, null: false }
|
|
1075
|
+
conn.create_table(:products_tags, primary_key: [:product_id, :tag_id]) do |t|
|
|
1076
|
+
t.bigint :product_id, null: false
|
|
1077
|
+
t.bigint :tag_id, null: false
|
|
1078
|
+
end
|
|
1079
|
+
conn.schema_cache.clear!
|
|
1080
|
+
|
|
1081
|
+
class Product < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1082
|
+
declare_schema do
|
|
1083
|
+
string :name, limit: 100, null: false
|
|
1084
|
+
end
|
|
1085
|
+
has_and_belongs_to_many :tags
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
class Tag < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1089
|
+
declare_schema id: { type: :string, limit: 36 } do
|
|
1090
|
+
string :label, limit: 50, null: false
|
|
1091
|
+
end
|
|
1092
|
+
has_and_belongs_to_many :products
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
let(:up) { Generators::DeclareSchema::Migration::Migrator.run.first }
|
|
1097
|
+
|
|
1098
|
+
it 'generates change_column on the join table without crashing' do
|
|
1099
|
+
expect(up).to match(/change_column\s+:products_tags,\s+:tag_id,\s+:string/)
|
|
1100
|
+
end
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
it 'HABTM mirrors both parents primary key types (different non-default types)' do
|
|
1104
|
+
class Product < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1105
|
+
declare_schema id: { type: :string, limit: 36 } do
|
|
1106
|
+
string :name, limit: 100, null: false
|
|
1107
|
+
end
|
|
1108
|
+
has_and_belongs_to_many :tags
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
class Tag < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
1112
|
+
declare_schema id: { type: :integer, limit: 4 } do
|
|
1113
|
+
string :label, limit: 50, null: false
|
|
1114
|
+
end
|
|
1115
|
+
has_and_belongs_to_many :products
|
|
1116
|
+
end
|
|
1117
|
+
|
|
1118
|
+
up, _ = Generators::DeclareSchema::Migration::Migrator.run
|
|
1119
|
+
|
|
1120
|
+
expect(up).to match(/t\.string\s+:product_id, limit: 36, null: false/)
|
|
1121
|
+
expect(up).to match(/t\.integer\s+:tag_id, limit: 4, null: false/)
|
|
1122
|
+
end
|
|
1123
|
+
end
|
|
1124
|
+
|
|
991
1125
|
context 'models with the same parent foreign key relation' do
|
|
992
1126
|
include_context 'skip if' do
|
|
993
1127
|
let(:adapter) { 'sqlite3' }
|
|
@@ -41,7 +41,12 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
describe 'instance methods' do
|
|
44
|
-
|
|
44
|
+
let(:parent_models) { [User, Customer] }
|
|
45
|
+
let(:parents) { foreign_keys.zip(parent_models) }
|
|
46
|
+
|
|
47
|
+
subject do
|
|
48
|
+
described_class.new(join_table, parents, connection: connection)
|
|
49
|
+
end
|
|
45
50
|
|
|
46
51
|
describe '#initialize' do
|
|
47
52
|
it 'stores initialization attributes' do
|
|
@@ -88,8 +93,8 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
|
88
93
|
end
|
|
89
94
|
|
|
90
95
|
describe '#primary_key' do
|
|
91
|
-
it 'returns
|
|
92
|
-
expect(subject.primary_key).to eq(
|
|
96
|
+
it 'returns the composite of the two foreign keys (alphabetically sorted)' do
|
|
97
|
+
expect(subject.primary_key).to eq(["customer_id", "user_id"])
|
|
93
98
|
end
|
|
94
99
|
end
|
|
95
100
|
|
|
@@ -116,14 +121,15 @@ RSpec.describe DeclareSchema::Model::HabtmModelShim do
|
|
|
116
121
|
let(:foreign_keys_and_table_names) { [["advertiser_id", "advertisers"], ["campaign_id", "campaigns"]] }
|
|
117
122
|
let(:foreign_keys) { foreign_keys_and_table_names.map(&:first) }
|
|
118
123
|
let(:parent_table_names) { foreign_keys_and_table_names.map(&:last) }
|
|
124
|
+
let(:parent_models) { [Table1, Table2] }
|
|
119
125
|
|
|
120
126
|
before do
|
|
121
|
-
class Table1 < ActiveRecord::Base
|
|
122
|
-
self.table_name = '
|
|
127
|
+
class Table1 < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
128
|
+
self.table_name = 'advertisers'
|
|
123
129
|
end
|
|
124
130
|
|
|
125
|
-
class Table2 < ActiveRecord::Base
|
|
126
|
-
self.table_name = '
|
|
131
|
+
class Table2 < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
132
|
+
self.table_name = 'campaigns'
|
|
127
133
|
end
|
|
128
134
|
end
|
|
129
135
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'generators/declare_schema/support/thor_shell'
|
|
4
|
+
|
|
5
|
+
# Verifies that when the migrator has to add a declared primary key column
|
|
6
|
+
# that has no corresponding FieldSpec (because it was appended to `to_add`
|
|
7
|
+
# at lib/generators/declare_schema/migration/migrator.rb:366), the column
|
|
8
|
+
# type is derived from `DeclareSchema.default_generated_primary_key_type`
|
|
9
|
+
# rather than a hardcoded `:integer`. This matters for apps that override
|
|
10
|
+
# `config.generators.primary_key_type`.
|
|
11
|
+
RSpec.describe 'DeclareSchema.default_generated_primary_key_type integration with Migrator' do
|
|
12
|
+
include_context 'prepare test app'
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
if current_adapter == 'mysql2'
|
|
16
|
+
ActiveRecord::Base.connection.execute("CREATE TABLE foos (id int PRIMARY KEY, name varchar(250))")
|
|
17
|
+
else
|
|
18
|
+
ActiveRecord::Base.connection.execute("CREATE TABLE foos (id integer PRIMARY KEY AUTOINCREMENT NOT NULL, name varchar(250))")
|
|
19
|
+
end
|
|
20
|
+
ActiveRecord::Base.connection.schema_cache.clear!
|
|
21
|
+
|
|
22
|
+
allow_any_instance_of(DeclareSchema::Support::ThorShell)
|
|
23
|
+
.to receive(:ask).with(/one of the rename choices or press enter to keep/) { 'drop id' }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'derives the new PK column type from DeclareSchema.default_generated_primary_key_type rather than a hardcoded :integer' do
|
|
27
|
+
allow(::DeclareSchema).to receive(:default_generated_primary_key_type).and_return(:string)
|
|
28
|
+
|
|
29
|
+
class Foo < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
30
|
+
declare_schema do
|
|
31
|
+
string :name, limit: 250
|
|
32
|
+
end
|
|
33
|
+
self.primary_key = "foo_id"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
up, _down = Generators::DeclareSchema::Migration::Migrator.run
|
|
37
|
+
|
|
38
|
+
expect(up).to include("add_column :foos, :foo_id, :string")
|
|
39
|
+
expect(up).not_to include("add_column :foos, :foo_id, :integer")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'falls through to the configured Rails default (:bigint) when the helper is not stubbed' do
|
|
43
|
+
class Foo < ActiveRecord::Base # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
44
|
+
declare_schema do
|
|
45
|
+
string :name, limit: 250
|
|
46
|
+
end
|
|
47
|
+
self.primary_key = "foo_id"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
up, _down = Generators::DeclareSchema::Migration::Migrator.run
|
|
51
|
+
|
|
52
|
+
expect(up).to include("add_column :foos, :foo_id, #{::DeclareSchema.default_generated_primary_key_type.inspect}")
|
|
53
|
+
end
|
|
54
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: declare_schema
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1.0.colin.
|
|
4
|
+
version: 3.1.0.colin.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Invoca Development adapted from hobo_fields by Tom Locke
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
19
|
+
version: '7.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
26
|
+
version: '7.0'
|
|
27
27
|
description: Declare your Rails/active_record model schemas and have database migrations
|
|
28
28
|
generated for you!
|
|
29
29
|
email: development@invoca.com
|
|
@@ -131,6 +131,7 @@ files:
|
|
|
131
131
|
- spec/lib/declare_schema/schema_change/table_remove_spec.rb
|
|
132
132
|
- spec/lib/declare_schema/schema_change/table_rename_spec.rb
|
|
133
133
|
- spec/lib/declare_schema_spec.rb
|
|
134
|
+
- spec/lib/generators/declare_schema/migration/migrator_default_pk_type_spec.rb
|
|
134
135
|
- spec/lib/generators/declare_schema/migration/migrator_spec.rb
|
|
135
136
|
- spec/spec_helper.rb
|
|
136
137
|
- spec/support/acceptance_spec_helpers.rb
|