erd 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +1 -1
  3. data/app/assets/javascripts/erd/application.js +0 -2
  4. data/app/assets/javascripts/erd/erd.js.js +420 -0
  5. data/app/assets/stylesheets/erd/application.css +0 -1
  6. data/app/controllers/erd/erd_controller.rb +1 -1
  7. data/app/views/layouts/erd/application.html.erb +3 -0
  8. data/erd.gemspec +0 -2
  9. data/gemfiles/rails_edge.gemfile +2 -1
  10. data/lib/erd/version.rb +1 -1
  11. data/test/test_helper.rb +0 -1
  12. metadata +4 -49
  13. data/app/assets/javascripts/erd/erd.js.coffee +0 -345
  14. data/vendor/assets/images/erd/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  15. data/vendor/assets/images/erd/ui-bg_flat_75_ffffff_40x100.png +0 -0
  16. data/vendor/assets/images/erd/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  17. data/vendor/assets/images/erd/ui-bg_glass_65_ffffff_1x400.png +0 -0
  18. data/vendor/assets/images/erd/ui-bg_glass_75_dadada_1x400.png +0 -0
  19. data/vendor/assets/images/erd/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  20. data/vendor/assets/images/erd/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  21. data/vendor/assets/images/erd/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  22. data/vendor/assets/images/erd/ui-icons_222222_256x240.png +0 -0
  23. data/vendor/assets/images/erd/ui-icons_2e83ff_256x240.png +0 -0
  24. data/vendor/assets/images/erd/ui-icons_454545_256x240.png +0 -0
  25. data/vendor/assets/images/erd/ui-icons_888888_256x240.png +0 -0
  26. data/vendor/assets/images/erd/ui-icons_cd0a0a_256x240.png +0 -0
  27. data/vendor/assets/javascripts/jquery-ui.min.js +0 -12
  28. data/vendor/assets/stylesheets/jquery-ui.css +0 -1188
  29. data/vendor/assets/stylesheets/jquery.ui.dialog.css +0 -69
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 89e699a699726399895a401998276b6b36a6b38b120c2e14c973de7c13fbe8f4
4
- data.tar.gz: d4a9db8626fbc276f33dd1ed7f820e854a9cef33c41cd4146bc6b6ffe040de13
3
+ metadata.gz: a9d85fe99c1f99f980306af0ec58827c2259b1669055460c5af1181ba344970a
4
+ data.tar.gz: 39522c53587e660e2445c39a2931f167db10c09429e8a8c020d5e6e371fdf9b3
5
5
  SHA512:
6
- metadata.gz: 61a3938da621aaf9e2f8deb520535744a10b34564649adcdbb6dd2e6e871e8832837f1e8f30030b7e2b67058d11cf093af93e400ef2e9ae513d059582d877cb3
7
- data.tar.gz: 1e33ceaabcbc8d5bb750d1fa4a100667f2275e09faa901eabfb291a53befe408c73e5e0d410999d87cdfc4a20bb376b7ab1b769c2f4dee3dda005eb8ac09567a
6
+ metadata.gz: c2116ff57af688e72b0de8acd82c2b3aa107762cffb576323b79eb002cb895e103dda3286e34e23b3959e88106de7199d120a798ab3ee99495836caf00cf4fca
7
+ data.tar.gz: 1b57a5dbe064649cbb4891bb079814faec9641d5fa1f2f7c37918895e0d36dfadd61e70dfff0cb441b13c3022ca66843a114e5ead333d785bbe8f5e36ab38046
@@ -5,7 +5,7 @@ A Rails engine for drawing your app's ER diagram and operating migrations
5
5
 
6
6
  == Requirements
7
7
 
8
- * Rails 5.0, 4.2, 4.1, 4.0, 3.2, or 3.1
8
+ * Rails 5.2, 5.1, 5.0, 4.2, 4.1, 4.0, 3.2, or 3.1
9
9
 
10
10
  * Graphviz
11
11
 
@@ -10,7 +10,5 @@
10
10
  // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
11
  // GO AFTER THE REQUIRES BELOW.
12
12
  //
13
- //= require jquery
14
- //= require jquery-ui.min
15
13
  //= require raphael-min
16
14
  //= require_tree .
@@ -0,0 +1,420 @@
1
+ /*
2
+ * decaffeinate suggestions:
3
+ * DS207: Consider shorter variations of null checks
4
+ * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
5
+ */
6
+ class ERD {
7
+ constructor(name, elem, edges) {
8
+ this.connect_arrows = this.connect_arrows.bind(this);
9
+ this.handle_drag = this.handle_drag.bind(this);
10
+ this.handle_save = this.handle_save.bind(this);
11
+ this.handle_add_column = this.handle_add_column.bind(this);
12
+ this.handle_change_column_type = this.handle_change_column_type.bind(this);
13
+ this.handle_rename_column = this.handle_rename_column.bind(this);
14
+ this.handle_rename_model = this.handle_rename_model.bind(this);
15
+ this.handle_add_column_click = this.handle_add_column_click.bind(this);
16
+ this.handle_cancel_click = this.handle_cancel_click.bind(this);
17
+ this.handle_text_elem_click = this.handle_text_elem_click.bind(this);
18
+ this.handle_remove_model_click = this.handle_remove_model_click.bind(this);
19
+ this.handle_new_model_add_column_click = this.handle_new_model_add_column_click.bind(this);
20
+ this.handle_open_migration_click = this.handle_open_migration_click.bind(this);
21
+ this.handle_close_migration_click = this.handle_close_migration_click.bind(this);
22
+ this.name = name;
23
+ this.elem = elem;
24
+ this.edges = edges;
25
+ this.paper = Raphael(this.name, this.elem.data('svg_width'), this.elem.data('svg_height'));
26
+ this.setup_handlers();
27
+ const models = this.elem.find('.model');
28
+ this.models = {};
29
+ for (let model of models) {
30
+ this.models[$(model).data('model_name')] = model;
31
+ }
32
+ this.connect_arrows(this.edges);
33
+ }
34
+
35
+ upsert_change(action, model, column, from, to) {
36
+ const rows = ($('#changes > tbody > tr').map((tr) => $(tr).find('td')));
37
+ let existing = null;
38
+ $(rows).each(function(i, row) {
39
+ if ((action === $(row[0]).html()) && (model === $(row[1]).html()) && (column === $(row[2]).html())) { return existing = row; }
40
+ });
41
+ if (existing === null) {
42
+ $('#changes > tbody').append(`\
43
+ <tr>
44
+ <td data-name="action">${action}</td>
45
+ <td data-name="model">${model}</td>
46
+ <td data-name="column">${column}</td>
47
+ <td data-name="from">${from}</td>
48
+ <td data-name="to">${to}</td>
49
+ </tr>\
50
+ `);
51
+ } else {
52
+ $(existing[3]).text(from);
53
+ $(existing[4]).text(to);
54
+ }
55
+ $('#changes').show();
56
+ }
57
+
58
+ positions(div) {
59
+ const [left, width, top, height] = [parseFloat(div.css('left')), parseFloat(div.css('width')), parseFloat(div.css('top')), parseFloat(div.css('height'))];
60
+ return {left, right: left + width, top, bottom: top + height, center: {x: (left + left + width) / 2, y: (top + top + height) / 2}, vertex: {}};
61
+ }
62
+
63
+ connect_arrows(edges) {
64
+ $.each(edges, (i, edge) => {
65
+ this.connect_arrow(edge, $(this.models[edge.from]), $(this.models[edge.to]));
66
+ });
67
+ }
68
+
69
+ connect_arrow(edge, from_elem, to_elem) {
70
+ //TODO handle self referential associations
71
+ let path;
72
+ if (from_elem.attr('id') === to_elem.attr('id')) { return; }
73
+
74
+ if (edge.path != null) { edge.path.remove(); }
75
+
76
+ const from = this.positions(from_elem);
77
+ const to = this.positions(to_elem);
78
+ //FIXME terrible code
79
+ const a = (to.center.y - from.center.y) / (to.center.x - from.center.x);
80
+ const b = from.center.y - (from.center.x * a);
81
+
82
+ const x2y = x => (a * x) + b;
83
+ const y2x = y => (y - b) / a;
84
+
85
+ if (from.center.x > to.center.x) {
86
+ [from.vertex.x, from.vertex.y] = [from.left, x2y(from.left)];
87
+ [to.vertex.x, to.vertex.y] = [to.right, x2y(to.right)];
88
+ } else {
89
+ [from.vertex.x, from.vertex.y] = [from.right, x2y(from.right)];
90
+ [to.vertex.x, to.vertex.y] = [to.left, x2y(to.left)];
91
+ }
92
+ for (let rect of [from, to]) {
93
+ if (rect.vertex.y < rect.top) {
94
+ [rect.vertex.x, rect.vertex.y, rect.vertex.direction] = [y2x(rect.top), rect.top, 'v'];
95
+ } else if (rect.vertex.y > rect.bottom) {
96
+ [rect.vertex.x, rect.vertex.y, rect.vertex.direction] = [y2x(rect.bottom), rect.bottom, 'v'];
97
+ } else {
98
+ from.vertex.direction = 'h';
99
+ }
100
+ }
101
+
102
+ if (from.vertex.direction === 'h') {
103
+ path = `M${Math.floor(from.vertex.x)} ${Math.floor(from.vertex.y)}H${Math.floor((from.vertex.x + to.vertex.x) / 2)} V${Math.floor(to.vertex.y)} H${Math.floor(to.vertex.x)}`;
104
+ } else {
105
+ path = `M${Math.floor(from.vertex.x)} ${Math.floor(from.vertex.y)}V${Math.floor((from.vertex.y + to.vertex.y) / 2)} H${Math.floor(to.vertex.x)} V${Math.floor(to.vertex.y)}`;
106
+ }
107
+
108
+ edge.path = this.paper.path(path).attr({'stroke-width': 2, opacity: 0.5, 'arrow-end': 'classic-wide-long'});
109
+ }
110
+
111
+ setup_handlers() {
112
+ this.setup_click_handlers();
113
+ this.setup_submit_handlers();
114
+ this.setup_migration_event_handlers();
115
+ $('div.model').draggable({drag: this.handle_drag});
116
+ }
117
+
118
+ handle_drag(ev, ui) {
119
+ const target = $(ev.target);
120
+ target.addClass('noclick');
121
+ const model_name = target.data('model_name');
122
+ const from = target.data('original_position');
123
+ const to = [target.css('left').replace(/px$/, ''), target.css('top').replace(/px$/, '')].join();
124
+ this.upsert_change('move', model_name, '', '', to);
125
+ this.connect_arrows(this.edges.filter(e=> (e.from === model_name) || (e.to === model_name)));
126
+ }
127
+
128
+ setup_click_handlers() {
129
+ $('div.model_name_text, span.column_name_text, span.column_type_text').on('click', this.handle_text_elem_click);
130
+ $('div.model a.add_column').on('click', this.handle_add_column_click);
131
+ $('div.model a.cancel').on('click', this.handle_cancel_click);
132
+ $('div.model a.close').on('click', this.handle_remove_model_click);
133
+ $('#new_model_add_column').on('click', this.handle_new_model_add_column_click);
134
+ $('div.model a.cancel').on('click', this.handle_cancel_click);
135
+ $('div#open_migration').on('click', this.handle_open_migration_click);
136
+ $('div#close_migration').on('click', this.handle_close_migration_click);
137
+ }
138
+
139
+ setup_submit_handlers() {
140
+ $('form.rename_model_form').on('submit', this.handle_rename_model);
141
+ $('form.rename_column_form').on('submit', this.handle_rename_column);
142
+ $('form.alter_column_form').on('submit', this.handle_change_column_type);
143
+ $('form.add_column_form').on('submit', this.handle_add_column);
144
+ $('#changes_form').on('submit', this.handle_save);
145
+ }
146
+
147
+ setup_migration_event_handlers() {
148
+ $('#migration_status tr input').on('click', function() {
149
+ $(this).parents('tr').toggleClass('active');
150
+ });
151
+ $('#migration_status thead td button').on('click', function(ev) {
152
+ ev.preventDefault();
153
+ $('#migration_status').toggleClass('show_all_migrations');
154
+ });
155
+ }
156
+
157
+ handle_save(ev) {
158
+ const changes = $('#changes > tbody > tr').map(function() {
159
+ const change = {};
160
+ $(this).find('td').each(function() {
161
+ const name = $(this).data('name');
162
+ const value = $(this).html();
163
+ change[name] = value;
164
+ });
165
+ return change;
166
+ }).toArray();
167
+ $('#changes_form').find('input[name=changes]').val(JSON.stringify(changes));
168
+ }
169
+
170
+ handle_add_column(ev) {
171
+ ev.preventDefault();
172
+ const target = $(ev.target);
173
+ const name = target.find('input[name=name]').val();
174
+ if (name === '') { return; }
175
+
176
+ const model = target.find('input[name=model]').val();
177
+ const type = target.find('input[name=type]').val();
178
+ this.upsert_change('add_column', model, `${name}(${type})`, '', '');
179
+
180
+ const name_span = $("<span/>", {class: 'column_name_text'})
181
+ .append(name);
182
+
183
+ const type_span = $("<span/>", {class: 'column_type_text unsaved'})
184
+ .append(type);
185
+
186
+ const li_node = $("<li/>", {class: 'column unsaved'}).append(name_span).append("&nbsp;").append(type_span);
187
+
188
+ target.hide()
189
+ .parent()
190
+ .siblings('.columns')
191
+ .find('ul').append(li_node).end()
192
+ .end()
193
+ .find('a.add_column').show();
194
+ }
195
+
196
+ handle_change_column_type(ev) {
197
+ ev.preventDefault();
198
+ const target = $(ev.target);
199
+ const to = target.find('input[name=to]').val();
200
+ if (to === '') { return; }
201
+
202
+ const model = target.find('input[name=model]').val();
203
+ const column = target.find('input[name=column]').val();
204
+ const type = target.find('input[name=type]').val();
205
+ if (to !== type) {
206
+ this.upsert_change('alter_column', model, column, type, to);
207
+ }
208
+
209
+ target.hide()
210
+ .siblings('.column_type_text').text(to).show().addClass('unsaved')
211
+ .parents('.column').addClass('unsaved');
212
+ }
213
+
214
+ handle_rename_column(ev) {
215
+ ev.preventDefault();
216
+ const target = $(ev.target);
217
+ const to = target.find('input[name=to]').val();
218
+ if (to === '') { return; }
219
+
220
+ const model = target.find('input[name=model]').val();
221
+ const column = target.find('input[name=column]').val();
222
+ if (to !== column) {
223
+ this.upsert_change('rename_column', model, column, column, to);
224
+ }
225
+
226
+ target.hide()
227
+ .siblings('.column_name_text').text(to).show()
228
+ .parents('.column').addClass('unsaved');
229
+ }
230
+
231
+ handle_rename_model(ev) {
232
+ ev.preventDefault();
233
+ const target = $(ev.target);
234
+ const to = target.find('input[name=to]').val();
235
+ if (to === '') { return; }
236
+
237
+ const model = target.find('input[name=model]').val();
238
+ if (to !== model) {
239
+ this.upsert_change('rename_model', model, '', model, to);
240
+ }
241
+
242
+ target.hide()
243
+ .siblings('.model_name_text').text(to).show().addClass('unsaved');
244
+ }
245
+
246
+ handle_add_column_click(ev) {
247
+ ev.preventDefault();
248
+ const target = $(ev.currentTarget);
249
+
250
+ const m = target.parents('div.model');
251
+ if (m.hasClass('noclick')) {
252
+ m.removeClass('noclick');
253
+ return false;
254
+ }
255
+
256
+ target.hide()
257
+ .next('form').show()
258
+ .find('a.cancel').show().end()
259
+ .find('input[name=type]').val('string').end()
260
+ .find('input[name=name]').val('').focus();
261
+ }
262
+
263
+ handle_cancel_click(ev) {
264
+ ev.preventDefault();
265
+ const target = $(ev.currentTarget);
266
+
267
+ const m = target.parents('div.model');
268
+ if (m.hasClass('noclick')) {
269
+ m.removeClass('noclick');
270
+ return false;
271
+ }
272
+
273
+ target.hide()
274
+ .parent('form').hide()
275
+ .prev('a.add_column, span, div').show();
276
+ }
277
+
278
+
279
+ handle_text_elem_click(ev) {
280
+ const target = $(ev.currentTarget);
281
+ const text = target.text();
282
+
283
+ const m = target.parents('div.model');
284
+ if (m.hasClass('noclick')) {
285
+ m.removeClass('noclick');
286
+ return false;
287
+ }
288
+
289
+ target.hide()
290
+ .next('form').show()
291
+ .find('a.cancel').show().end()
292
+ .find('input[name=to]').val(text).focus();
293
+ }
294
+
295
+ handle_remove_model_click(ev) {
296
+ ev.preventDefault();
297
+
298
+ const target = $(ev.target);
299
+ const parent = target.parent();
300
+
301
+ const m = target.parents('div.model');
302
+ if (m.hasClass('noclick')) {
303
+ m.removeClass('noclick');
304
+ return false;
305
+ }
306
+
307
+ if (!confirm('remove this table?')) { return; }
308
+
309
+ const model_name = m.data('model_name');
310
+ window.erd.upsert_change('remove_model', model_name, '', '', '');
311
+ parent.hide();
312
+
313
+ $.each(this.edges, (i, edge) => {
314
+ if ((edge.from === model_name) || (edge.to === model_name)) { this.edges.splice(i, 1); }
315
+ });
316
+ this.paper.clear();
317
+ this.connect_arrows(this.edges);
318
+ }
319
+
320
+ handle_new_model_add_column_click(ev) {
321
+ ev.preventDefault();
322
+ const target = $(ev.currentTarget);
323
+
324
+ target.parent().siblings('table').append('<tr><td><input type="text" /></td><td class="separator">:</td><td><input type="text" value="string" /></td></tr>').find('tr:last > td > input:first').focus();
325
+ }
326
+
327
+
328
+ handle_open_migration_click(ev) {
329
+ ev.preventDefault();
330
+
331
+ const target = $(ev.currentTarget);
332
+ const text = target.text();
333
+
334
+ const m = target.parents('div.model');
335
+ if (m.hasClass('noclick')) {
336
+ m.removeClass('noclick');
337
+ return false;
338
+ }
339
+
340
+ target.hide()
341
+ .next('div').show()
342
+ .find('#close_migration').show();
343
+ }
344
+
345
+
346
+ handle_close_migration_click(ev) {
347
+ ev.preventDefault();
348
+
349
+ const target = $(ev.currentTarget);
350
+ const text = target.text();
351
+
352
+ const m = target.parents('div.model');
353
+ if (m.hasClass('noclick')) {
354
+ m.removeClass('noclick');
355
+ return false;
356
+ }
357
+
358
+ target.hide()
359
+ .parent().hide()
360
+ .prev('div').show();
361
+ }
362
+ }
363
+
364
+ $(function() {
365
+ window.erd = new ERD('erd', $('#erd'), window.raw_edges);
366
+
367
+ $('#erd').css('height', window.innerHeight);
368
+ $(window).on('resize', () => $('#erd').css('height', window.innerHeight));
369
+
370
+ $("#open_migration").click(function() {
371
+ $('#close_migration, #open_create_model_dialog').css('right', ($('#migration').width() + ($(this).width() / 2)) - 5);
372
+ });
373
+
374
+ $("#close_migration").click(() => $('#open_create_model_dialog').css('right', 15));
375
+
376
+ $('#open_up').click(function() {
377
+ $('#migration_status .up').addClass('open');
378
+ $('#migration_status .down').removeClass('open');
379
+ });
380
+
381
+ $('#open_down').click(function() {
382
+ $('#migration_status .down').addClass('open');
383
+ $('#migration_status .up').removeClass('open');
384
+ });
385
+
386
+ $('#close_all').click(() => $('#migration_status tr').removeClass('open'));
387
+
388
+ $('#create_model_form').dialog({
389
+ autoOpen: false,
390
+ height: 450,
391
+ width: 450,
392
+ modal: true,
393
+ buttons: {
394
+ 'Create Model'() {
395
+ const model = $('#new_model_name').val();
396
+ let columns = '';
397
+ $('#create_model_table > tbody > tr').each(function(i, row) {
398
+ const [name, type] = $(row).find('input').map((v) => $(v).val());
399
+ if (name) { columns += `${name}${type ? `:${type}` : ''} `; }
400
+ });
401
+ window.erd.upsert_change('create_model', model, columns, '', '');
402
+ $(this).find('table > tbody > tr').each(function(i, row) {
403
+ if (i >= 1) { row.remove(); }
404
+ });
405
+ $(this).find('input').val('');
406
+ $(this).find('input[name=new_model_column_type_1]').val('string');
407
+
408
+ $(this).dialog('close');
409
+ },
410
+ Cancel() {
411
+ $(this).dialog('close');
412
+ }
413
+ }
414
+ });
415
+
416
+ $('#open_create_model_dialog').click(function(ev) {
417
+ ev.preventDefault();
418
+ $('#create_model_form').dialog('open');
419
+ });
420
+ });
@@ -9,6 +9,5 @@
9
9
  * compiled file, but it's generally better to create a new file per style scope.
10
10
  *
11
11
  *= require_self
12
- *= require jquery-ui
13
12
  *= require_tree .
14
13
  */