localtower 0.5.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -17
- data/app/controllers/localtower/pages_controller.rb +27 -12
- data/app/views/layouts/localtower/application.html.erb +16 -33
- data/app/views/localtower/pages/_alert_no_models.html.erb +3 -0
- data/app/views/localtower/pages/migrations.html.erb +52 -83
- data/app/views/localtower/pages/models.html.erb +53 -70
- data/app/views/localtower/pages/new_migration.html.erb +103 -0
- data/app/views/localtower/pages/new_model.html.erb +87 -0
- data/config/routes.rb +4 -4
- data/lib/localtower/generators/migration.rb +51 -120
- data/lib/localtower/generators/model.rb +52 -28
- data/lib/localtower/generators/service_objects/insert_array.rb +23 -0
- data/lib/localtower/generators/service_objects/insert_defaults.rb +15 -43
- data/lib/localtower/generators/service_objects/insert_indexes.rb +80 -0
- data/lib/localtower/generators/service_objects/insert_nullable.rb +23 -0
- data/lib/localtower/status.rb +1 -1
- data/lib/localtower/tools.rb +13 -16
- data/lib/localtower/version.rb +1 -1
- data/public/css/app.css +0 -49
- data/public/js/app.js +215 -70
- data/public/screenshots/v1.0.0/migrations.png +0 -0
- data/public/screenshots/v1.0.0/models.png +0 -0
- data/public/screenshots/v1.0.0/new_migration.png +0 -0
- data/public/screenshots/v1.0.0/new_model.png +0 -0
- data/spec/dummy/Gemfile +0 -2
- data/spec/dummy/Gemfile.lock +1 -10
- data/spec/dummy/app/models/post.rb +3 -0
- data/spec/dummy/app/models/user.rb +1 -0
- data/spec/dummy/config/environments/development.rb +1 -0
- data/spec/dummy/config/puma.rb +1 -1
- data/spec/dummy/db/migrate/20230119221452_create_users.rb +14 -0
- data/spec/dummy/db/migrate/20230119221751_change_users_at1674166670.rb +7 -0
- data/spec/dummy/db/migrate/20230119222054_create_posts.rb +11 -0
- data/spec/dummy/db/migrate/20230119222106_change_posts_at1674166865.rb +5 -0
- data/spec/dummy/db/schema.rb +20 -8
- data/spec/dummy/log/development.log +15281 -3345
- data/spec/dummy/log/localtower.log +1897 -132
- data/spec/dummy/log/test.log +0 -0
- data/spec/dummy/test/index.html +38 -0
- data/spec/dummy/tmp/pids/server.pid +1 -0
- data/spec/factories/migration.rb +25 -41
- data/spec/factories/model.rb +39 -25
- data/spec/lib/localtower/generators/migration_spec.rb +36 -63
- data/spec/lib/localtower/generators/model_spec.rb +43 -34
- data/spec/lib/localtower/generators/service_objects/insert_array_spec.rb +47 -0
- data/spec/lib/localtower/generators/service_objects/insert_defaults_spec.rb +30 -35
- data/spec/lib/localtower/generators/service_objects/insert_indexes_spec.rb +90 -0
- data/spec/lib/localtower/generators/service_objects/insert_nullable_spec.rb +61 -0
- data/spec/lib/localtower/tools_spec.rb +1 -11
- data/spec/spec_helper.rb +8 -3
- metadata +34 -18
- data/app/views/localtower/pages/_migrations.html.erb +0 -57
- data/app/views/localtower/pages/schema.html.erb +0 -67
- data/spec/dummy/db/migrate/20221115190039_create_users.rb +0 -13
- data/spec/dummy/db/migrate/20221115193020_change_users.rb +0 -8
- data/spec/dummy/db/migrate/20221115193532_change_users_at1668540931.rb +0 -5
- data/spec/dummy/db/migrate/20221115193605_change_users_at1668540964.rb +0 -5
- data/spec/dummy/db/migrate/20221115193637_change_users_at1668540996.rb +0 -5
- data/spec/dummy/db/migrate/20221115193642_change_users_at1668541001.rb +0 -5
- data/spec/lib/localtower/generators/relation_spec.rb +0 -65
data/public/js/app.js
CHANGED
@@ -1,88 +1,187 @@
|
|
1
1
|
window.MainApp = {};
|
2
2
|
|
3
|
+
Array.prototype.unique = function () {
|
4
|
+
return this.filter(function (value, index, self) {
|
5
|
+
return self.indexOf(value) === index;
|
6
|
+
});
|
7
|
+
};
|
8
|
+
|
3
9
|
MainApp = {
|
4
|
-
init: function() {
|
5
|
-
},
|
10
|
+
init: function () {},
|
6
11
|
|
7
|
-
ready: function() {
|
8
|
-
$(
|
9
|
-
itemSelector:
|
12
|
+
ready: function () {
|
13
|
+
$(".grid").masonry({
|
14
|
+
itemSelector: ".grid-item",
|
10
15
|
percentPosition: true,
|
11
|
-
columnWidth:
|
16
|
+
columnWidth: ".grid-sizer",
|
17
|
+
});
|
18
|
+
|
19
|
+
MainApp.whenActionOnElement("click", "duplicateLineNewModel", function (e) {
|
20
|
+
e.preventDefault();
|
21
|
+
|
22
|
+
if (MainApp.modelNameAndAttributesAreFilled()) {
|
23
|
+
MainApp.duplicateLine();
|
24
|
+
} else {
|
25
|
+
MainApp.notFilled();
|
26
|
+
}
|
27
|
+
|
28
|
+
return false;
|
12
29
|
});
|
13
30
|
|
14
|
-
MainApp.whenActionOnElement(
|
31
|
+
MainApp.whenActionOnElement(
|
32
|
+
"click",
|
33
|
+
"duplicateLineNewMigration",
|
34
|
+
function (e) {
|
35
|
+
e.preventDefault();
|
36
|
+
MainApp.duplicateLineMigration();
|
37
|
+
return false;
|
38
|
+
}
|
39
|
+
);
|
40
|
+
|
41
|
+
MainApp.whenActionOnElement("click", "removeLineModel", function (e) {
|
15
42
|
e.preventDefault();
|
16
|
-
MainApp.
|
43
|
+
MainApp.removeLineModel(e.currentTarget);
|
17
44
|
return false;
|
18
45
|
});
|
19
46
|
|
20
|
-
MainApp.whenActionOnElement("click", "
|
47
|
+
MainApp.whenActionOnElement("click", "removeLineMigration", function (e) {
|
21
48
|
e.preventDefault();
|
22
|
-
MainApp.
|
49
|
+
MainApp.removeLineMigration(e.currentTarget);
|
23
50
|
return false;
|
24
51
|
});
|
25
52
|
|
26
|
-
|
53
|
+
// New Model
|
54
|
+
MainApp.whenActionOnElement("click", "submitNewModel", function (e) {
|
55
|
+
if (!MainApp.modelNameAndAttributesAreFilled()) {
|
56
|
+
MainApp.notFilled();
|
57
|
+
e.preventDefault();
|
58
|
+
return false;
|
59
|
+
}
|
60
|
+
|
27
61
|
var current = $(e.currentTarget);
|
28
62
|
$(".full-message").show();
|
29
63
|
|
30
64
|
if (current.val() === "false") {
|
31
65
|
$(".full-message-migrate").show();
|
32
66
|
}
|
67
|
+
});
|
33
68
|
|
69
|
+
// New Migration
|
70
|
+
MainApp.whenActionOnElement("click", "submitNewMigration", function (e) {
|
71
|
+
var current = $(e.currentTarget);
|
72
|
+
$(".full-message").show();
|
73
|
+
|
74
|
+
if (current.val() === "false") {
|
75
|
+
$(".full-message-migrate").show();
|
76
|
+
}
|
34
77
|
});
|
35
78
|
|
36
|
-
MainApp.whenActionOnElement("change", "action", function(e) {
|
79
|
+
MainApp.whenActionOnElement("change", "action", function (e) {
|
37
80
|
MainApp.adaptLines();
|
38
81
|
});
|
39
82
|
|
40
83
|
MainApp.adaptLines();
|
41
84
|
MainApp.sanitizeInputs();
|
42
|
-
|
43
85
|
hljs.highlightAll();
|
44
86
|
},
|
45
87
|
|
46
88
|
// INSTANCE --------------------------------------------------
|
47
|
-
sanitizeInputs: function() {
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
89
|
+
sanitizeInputs: function () {
|
90
|
+
function camelize(str) {
|
91
|
+
return str
|
92
|
+
.replace(/(?:^\w|[A-Z]|\b\w)/g, function (word, index) {
|
93
|
+
return word.toUpperCase();
|
94
|
+
})
|
95
|
+
.replace(/\s+/g, "");
|
96
|
+
}
|
97
|
+
|
98
|
+
function snakeCase(str) {
|
99
|
+
return str
|
100
|
+
.replace(/\ /g, "_")
|
101
|
+
.replace(/[^a-zA-Z0-9\_]/g, "")
|
102
|
+
.replace(/\_\_/g, "_")
|
103
|
+
.toLowerCase();
|
104
|
+
}
|
105
|
+
|
106
|
+
// Model name
|
107
|
+
MainApp.whenActionOnElement("keyup", "modelName", function (e) {
|
108
|
+
var currentInputValue = $(e.currentTarget).val();
|
109
|
+
var cleanInputValue = currentInputValue.replace(/[^a-zA-Z0-9]/g, "");
|
110
|
+
cleanInputValue = camelize(cleanInputValue);
|
111
|
+
$(e.currentTarget).val(cleanInputValue);
|
112
|
+
});
|
113
|
+
|
114
|
+
// Model attributes
|
115
|
+
MainApp.whenActionOnElement("keyup", "attributeName", function (e) {
|
116
|
+
var currentInputValue = $(e.currentTarget).val();
|
117
|
+
$(e.currentTarget).val(snakeCase(currentInputValue));
|
118
|
+
});
|
119
|
+
|
120
|
+
// Migration
|
121
|
+
MainApp.whenActionOnElement("keyup", "column_text", function (e) {
|
122
|
+
var currentInputValue = $(e.currentTarget).val();
|
123
|
+
$(e.currentTarget).val(snakeCase(currentInputValue));
|
124
|
+
});
|
53
125
|
},
|
54
126
|
|
127
|
+
modelNameAndAttributesAreFilled: function () {
|
128
|
+
// attributes name:
|
129
|
+
var valuesForAttributes = [];
|
130
|
+
MainApp.bySelector("attributeName").each(function (el) {
|
131
|
+
valuesForAttributes.push($(this).val());
|
132
|
+
});
|
133
|
+
// model name:
|
134
|
+
valuesForAttributes.push(MainApp.bySelector("modelName").first().val());
|
135
|
+
|
136
|
+
return (
|
137
|
+
valuesForAttributes.filter(function (n) {
|
138
|
+
return n === "";
|
139
|
+
}).length === 0
|
140
|
+
);
|
141
|
+
},
|
55
142
|
|
143
|
+
notFilled: function () {
|
144
|
+
alert("Please fill all the fields");
|
145
|
+
},
|
56
146
|
|
57
147
|
// This is a little bit dirty but it works well for the moment:
|
58
148
|
// We dynamically show/hide fields
|
59
|
-
adaptLines: function() {
|
60
|
-
$.each(MainApp.bySelector("
|
61
|
-
var $
|
149
|
+
adaptLines: function () {
|
150
|
+
$.each(MainApp.bySelector("table").find("table"), function (table) {
|
151
|
+
var $table = $(this);
|
62
152
|
|
63
|
-
var action_input = $
|
153
|
+
var action_input = $table.find("[data-selector='action']");
|
64
154
|
var action = action_input.val();
|
65
155
|
|
66
|
-
var belongs_to_input = $
|
67
|
-
var belongs_to_label =
|
156
|
+
var belongs_to_input = $table.find("[data-selector='belongs_to']");
|
157
|
+
var belongs_to_label = $table.find("[data-selector='belongs_to_label']");
|
68
158
|
|
69
|
-
var column_text_input = $
|
70
|
-
var
|
159
|
+
var column_text_input = $table.find("[data-selector='column_text']");
|
160
|
+
var column_input = $table.find("[data-selector='column_list']");
|
161
|
+
var column_label = $table.find("[data-selector='column_label']");
|
71
162
|
|
72
|
-
var
|
73
|
-
|
163
|
+
var new_column_name_input = $table.find(
|
164
|
+
"[data-selector='new_column_name']"
|
165
|
+
);
|
166
|
+
var new_column_name_label = $table.find(
|
167
|
+
"[data-selector='new_column_name_label']"
|
168
|
+
);
|
74
169
|
|
75
|
-
var
|
76
|
-
var
|
170
|
+
var column_type_input = $table.find("[data-selector='column_type']");
|
171
|
+
var column_type_label = $table.find(
|
172
|
+
"[data-selector='column_type_label']"
|
173
|
+
);
|
77
174
|
|
78
|
-
var
|
79
|
-
var
|
175
|
+
var index_options_inputs = $table.find("[data-selector='index_options']");
|
176
|
+
var index_options_label = $table.find(
|
177
|
+
"[data-selector='index_options_label']"
|
178
|
+
);
|
80
179
|
|
81
|
-
var
|
82
|
-
var
|
180
|
+
var default_input = $table.find("[data-selector='default_input']");
|
181
|
+
var default_label = $table.find("[data-selector='default_label']");
|
83
182
|
|
84
|
-
var nullable_input = $
|
85
|
-
var nullable_label =
|
183
|
+
var nullable_input = $table.find("[data-selector='nullable_input']");
|
184
|
+
var nullable_label = $table.find("[data-selector='nullable_label']");
|
86
185
|
|
87
186
|
$.each(
|
88
187
|
[
|
@@ -90,8 +189,6 @@ MainApp = {
|
|
90
189
|
belongs_to_label,
|
91
190
|
|
92
191
|
column_text_input,
|
93
|
-
column_text_label,
|
94
|
-
|
95
192
|
column_input,
|
96
193
|
column_label,
|
97
194
|
|
@@ -101,31 +198,32 @@ MainApp = {
|
|
101
198
|
column_type_input,
|
102
199
|
column_type_label,
|
103
200
|
|
104
|
-
|
105
|
-
|
201
|
+
index_options_inputs,
|
202
|
+
index_options_label,
|
203
|
+
|
204
|
+
default_input,
|
205
|
+
default_label,
|
106
206
|
|
107
207
|
nullable_input,
|
108
208
|
nullable_label,
|
109
209
|
],
|
110
|
-
function(i, el) {
|
210
|
+
function (i, el) {
|
111
211
|
el.hide();
|
112
|
-
}
|
212
|
+
}
|
213
|
+
);
|
113
214
|
|
114
215
|
var mapping = {
|
115
216
|
add_column: [
|
116
217
|
column_text_input,
|
117
|
-
|
218
|
+
column_label,
|
118
219
|
column_type_input,
|
119
220
|
column_type_label,
|
120
|
-
|
121
|
-
|
221
|
+
default_input,
|
222
|
+
default_label,
|
122
223
|
nullable_input,
|
123
224
|
nullable_label,
|
124
225
|
],
|
125
|
-
remove_column: [
|
126
|
-
column_input,
|
127
|
-
column_label,
|
128
|
-
],
|
226
|
+
remove_column: [column_input, column_label],
|
129
227
|
rename_column: [
|
130
228
|
column_input,
|
131
229
|
column_label,
|
@@ -138,55 +236,102 @@ MainApp = {
|
|
138
236
|
column_type_input,
|
139
237
|
column_type_label,
|
140
238
|
],
|
141
|
-
belongs_to: [
|
142
|
-
belongs_to_input,
|
143
|
-
belongs_to_label,
|
144
|
-
],
|
239
|
+
belongs_to: [belongs_to_input, belongs_to_label],
|
145
240
|
add_index_to_column: [
|
146
241
|
column_input,
|
147
242
|
column_label,
|
243
|
+
index_options_inputs,
|
244
|
+
index_options_label,
|
148
245
|
],
|
149
|
-
remove_index_to_column: [
|
150
|
-
column_input,
|
151
|
-
column_label,
|
152
|
-
],
|
246
|
+
remove_index_to_column: [column_input, column_label],
|
153
247
|
drop_table: [],
|
154
|
-
}
|
248
|
+
};
|
155
249
|
|
156
250
|
inputs_to_show = mapping[action];
|
157
251
|
|
158
252
|
if (inputs_to_show) {
|
159
|
-
$.each(inputs_to_show, function(i, el) {
|
253
|
+
$.each(inputs_to_show, function (i, el) {
|
160
254
|
el.show();
|
161
255
|
});
|
162
256
|
}
|
163
257
|
});
|
164
258
|
},
|
165
259
|
|
166
|
-
duplicateLine: function() {
|
260
|
+
duplicateLine: function () {
|
167
261
|
var tr = MainApp.bySelector("tbody").find("tr").last().clone();
|
168
262
|
MainApp.bySelector("tbody").append(tr);
|
169
|
-
MainApp.bySelector("tbody")
|
263
|
+
MainApp.bySelector("tbody")
|
264
|
+
.find("tr")
|
265
|
+
.last()
|
266
|
+
.find('[name="model[attributes][][attribute_name]"]')
|
267
|
+
.val("")
|
268
|
+
.focus();
|
170
269
|
},
|
171
270
|
|
172
|
-
|
271
|
+
duplicateLineMigration: function () {
|
272
|
+
var table = MainApp.bySelector("table").find("table").last().clone();
|
273
|
+
MainApp.bySelector("table").append(table);
|
274
|
+
MainApp.adaptLines();
|
275
|
+
|
276
|
+
// Populate select/option for column list:
|
277
|
+
var allAttributes = [];
|
278
|
+
MainApp.bySelector("table")
|
279
|
+
.find('[data-selector="column_text"]')
|
280
|
+
.each(function (i, el) {
|
281
|
+
allAttributes.push(el.value);
|
282
|
+
});
|
283
|
+
allAttributes = allAttributes.unique();
|
284
|
+
|
285
|
+
var columnSelector = MainApp.bySelector("table")
|
286
|
+
.find("table")
|
287
|
+
.last()
|
288
|
+
.find('[data-selector="column_list"]')
|
289
|
+
.last();
|
290
|
+
|
291
|
+
var currentValues = [];
|
292
|
+
columnSelector.find("option").each(function (i, el) {
|
293
|
+
currentValues.push(el.value);
|
294
|
+
});
|
295
|
+
|
296
|
+
currentValues = currentValues.concat(allAttributes).unique().sort();
|
297
|
+
|
298
|
+
columnSelector.empty(); // remove old options
|
299
|
+
$.each(currentValues, function (i, value) {
|
300
|
+
columnSelector.append(
|
301
|
+
$("<option></option>").attr("value", value).text(value)
|
302
|
+
);
|
303
|
+
});
|
304
|
+
|
305
|
+
MainApp.bySelector("table")
|
306
|
+
.find("table")
|
307
|
+
.last()
|
308
|
+
.find('[name="migrations[][column]"]')
|
309
|
+
.val("")
|
310
|
+
.focus();
|
311
|
+
},
|
312
|
+
|
313
|
+
removeLineModel: function (target) {
|
173
314
|
if ($(target).parents("tbody").find("tr").length > 1) {
|
174
315
|
$(target).parents("tr").remove();
|
175
316
|
}
|
176
317
|
},
|
177
318
|
|
319
|
+
removeLineMigration: function (target) {
|
320
|
+
if ($(target).parents(".container-table").find("table").length > 1) {
|
321
|
+
$(target).parents("table").remove();
|
322
|
+
}
|
323
|
+
},
|
324
|
+
|
178
325
|
// PRIVATE
|
179
|
-
bySelector: function(selector) {
|
326
|
+
bySelector: function (selector) {
|
180
327
|
return $("[data-selector='" + selector + "']");
|
181
328
|
},
|
182
329
|
|
183
|
-
whenActionOnElement: function(action, selector, callback) {
|
330
|
+
whenActionOnElement: function (action, selector, callback) {
|
184
331
|
$(document).on(action, "[data-selector='" + selector + "']", callback);
|
185
|
-
}
|
186
|
-
}
|
187
|
-
|
188
|
-
MainApp.init();
|
332
|
+
},
|
333
|
+
};
|
189
334
|
|
190
|
-
$(document).ready(function() {
|
335
|
+
$(document).ready(function () {
|
191
336
|
MainApp.ready();
|
192
337
|
});
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/spec/dummy/Gemfile
CHANGED
data/spec/dummy/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: /Users/damln/Work/localtower
|
3
3
|
specs:
|
4
|
-
localtower (0.
|
4
|
+
localtower (0.5.0)
|
5
5
|
active_link_to (>= 1.0.4)
|
6
6
|
rails (>= 5.2.0)
|
7
7
|
thor (>= 0.18.1)
|
@@ -87,10 +87,6 @@ GEM
|
|
87
87
|
activesupport (>= 5.0)
|
88
88
|
i18n (1.12.0)
|
89
89
|
concurrent-ruby (~> 1.0)
|
90
|
-
jquery-rails (4.5.0)
|
91
|
-
rails-dom-testing (>= 1, < 3)
|
92
|
-
railties (>= 4.2.0)
|
93
|
-
thor (>= 0.14, < 2.0)
|
94
90
|
loofah (2.19.0)
|
95
91
|
crass (~> 1.0.2)
|
96
92
|
nokogiri (>= 1.5.9)
|
@@ -150,9 +146,6 @@ GEM
|
|
150
146
|
rake (13.0.6)
|
151
147
|
thor (1.2.1)
|
152
148
|
timeout (0.3.0)
|
153
|
-
turbolinks (5.2.1)
|
154
|
-
turbolinks-source (~> 5.2)
|
155
|
-
turbolinks-source (5.2.0)
|
156
149
|
tzinfo (2.0.5)
|
157
150
|
concurrent-ruby (~> 1.0)
|
158
151
|
websocket-driver (0.7.5)
|
@@ -164,12 +157,10 @@ PLATFORMS
|
|
164
157
|
ruby
|
165
158
|
|
166
159
|
DEPENDENCIES
|
167
|
-
jquery-rails
|
168
160
|
localtower!
|
169
161
|
pg
|
170
162
|
puma
|
171
163
|
rails (~> 7.0.1)
|
172
|
-
turbolinks (~> 5)
|
173
164
|
tzinfo-data
|
174
165
|
|
175
166
|
BUNDLED WITH
|
@@ -37,6 +37,7 @@ Rails.application.configure do
|
|
37
37
|
# Raise an error on page load if there are pending migrations.
|
38
38
|
# config.active_record.migration_error = :page_load
|
39
39
|
config.active_record.migration_error = false if defined?(Localtower)
|
40
|
+
config.reload_classes_only_on_change = false
|
40
41
|
|
41
42
|
# Debug mode disables concatenation and preprocessing of assets.
|
42
43
|
# This option may cause significant delays in view rendering with a large
|
data/spec/dummy/config/puma.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# the maximum value specified for Puma. Default is set to 5 threads for minimum
|
5
5
|
# and maximum, this matches the default thread size of Active Record.
|
6
6
|
#
|
7
|
-
threads_count = ENV.fetch("RAILS_MAX_THREADS") {
|
7
|
+
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 1 }.to_i
|
8
8
|
threads threads_count, threads_count
|
9
9
|
|
10
10
|
# Specifies the `port` that Puma will listen on to receive requests, default is 3000.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateUsers < ActiveRecord::Migration[7.0]
|
2
|
+
def change
|
3
|
+
create_table :users do |t|
|
4
|
+
t.string :email, null: false
|
5
|
+
t.integer :login_count, default: 0, null: false
|
6
|
+
t.jsonb :metadata, default: {}, null: false
|
7
|
+
t.string :name
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
add_index :users, :email, unique: true
|
12
|
+
add_index :users, :name
|
13
|
+
end
|
14
|
+
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -10,20 +10,32 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema[7.0].define(version:
|
13
|
+
ActiveRecord::Schema[7.0].define(version: 2023_01_19_222106) do
|
14
14
|
# These are extensions that must be enabled in order to support this database
|
15
15
|
enable_extension "plpgsql"
|
16
16
|
|
17
|
+
create_table "posts", force: :cascade do |t|
|
18
|
+
t.string "title", null: false
|
19
|
+
t.string "content", null: false
|
20
|
+
t.datetime "created_at", null: false
|
21
|
+
t.datetime "updated_at", null: false
|
22
|
+
t.bigint "user_id"
|
23
|
+
t.index ["title"], name: "index_posts_on_title"
|
24
|
+
t.index ["user_id"], name: "index_posts_on_user_id"
|
25
|
+
end
|
26
|
+
|
17
27
|
create_table "users", force: :cascade do |t|
|
18
|
-
t.
|
19
|
-
t.
|
20
|
-
t.
|
28
|
+
t.string "email", null: false
|
29
|
+
t.integer "signin_count", default: 0, null: false
|
30
|
+
t.jsonb "metadata", default: {}, null: false
|
31
|
+
t.string "name"
|
21
32
|
t.datetime "created_at", null: false
|
22
33
|
t.datetime "updated_at", null: false
|
23
|
-
t.string "
|
24
|
-
t.
|
25
|
-
t.index ["
|
26
|
-
t.index ["
|
34
|
+
t.string "city"
|
35
|
+
t.index ["city"], name: "index_users_on_city"
|
36
|
+
t.index ["email"], name: "index_users_on_email", unique: true
|
37
|
+
t.index ["name"], name: "index_users_on_name"
|
27
38
|
end
|
28
39
|
|
40
|
+
add_foreign_key "posts", "users"
|
29
41
|
end
|