erd 0.6.4 → 0.8.1
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/README.md +72 -0
- data/app/controllers/erd/erd_controller.rb +62 -48
- data/app/views/erd/erd/_column.html.erb +8 -4
- data/app/views/erd/erd/_model.html.erb +23 -17
- data/app/views/erd/erd/edit.html.erb +87 -0
- data/app/views/erd/erd/erd.html.erb +1 -1
- data/app/views/erd/erd/index.html.erb +12 -81
- data/app/views/layouts/erd/application.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/erd.gemspec +3 -3
- data/lib/erd/version.rb +1 -1
- data/public/erd/erd.css +69 -20
- data/public/erd/erd.js +12 -1
- metadata +9 -28
- data/README.rdoc +0 -66
- data/test/fake_app/config/database.yml +0 -3
- data/test/fake_app/db/migrate/20120428022519_create_authors.rb +0 -11
- data/test/fake_app/db/migrate/20120428022535_create_books.rb +0 -12
- data/test/fake_app/db/schema.rb +0 -0
- data/test/fake_app/fake_app.rb +0 -32
- data/test/fake_app/log/.gitignore +0 -1
- data/test/fake_app/tmp/.gitkeep +0 -0
- data/test/features/erd_test.rb +0 -18
- data/test/lib/migrator_test.rb +0 -59
- data/test/test_helper.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e9b918036131dd6376ed19adf3f6533b6caf0987a87f78b04944881ec579695
|
4
|
+
data.tar.gz: 591899522e0d2247e5bb999c88c182baeee7a90912f6f8c946bb044e83142a13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36353f77def4b6e65e4e565c87be9a9838dbc11c5e86475aada0435d8062a010c3a683c430bf9b1620289ebddfca5ea871ffbb4f7a4fa384402451489e5590ef
|
7
|
+
data.tar.gz: 175f0d4f165dd2fa3148865e4cfc898dbae572da4c200b1b68bb6cbf9450f4b01247ccfe5e189e6e34a8dbba7216d3291285f2464b24a01e67c0fa2b4a20c31b
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# Erd
|
2
|
+
|
3
|
+
A Rails engine for drawing your app's ER diagram and operating migrations
|
4
|
+
|
5
|
+
|
6
|
+
## Requirements
|
7
|
+
|
8
|
+
* Rails 7.0, 6.1, 6.0, 5.2, 5.1, 5.0, 4.2, 4.1, 4.0, 3.2, or 3.1
|
9
|
+
|
10
|
+
* Graphviz
|
11
|
+
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Bundle 'erd' gem to your existing Rails app's Gemfile:
|
16
|
+
```ruby
|
17
|
+
gem 'erd', group: :development
|
18
|
+
|
19
|
+
```
|
20
|
+
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
Browse at your http://localhost:3000/erd
|
25
|
+
|
26
|
+
|
27
|
+
## Features
|
28
|
+
|
29
|
+
### Show Mode
|
30
|
+
|
31
|
+
* Erd draws an ER diagram based on your app's database and models.
|
32
|
+
|
33
|
+
* You can drag and arrange the positions of each model.
|
34
|
+
|
35
|
+
* Then you can save the positions to a local file `db/erd_positions.json`, so you can share the diagram between the team members.
|
36
|
+
|
37
|
+
### Edit Mode
|
38
|
+
|
39
|
+
* You can operate DB schema manipulations such as `add column`, `rename column`, `alter column`, `create model (as well as table)`, and `drop table`.
|
40
|
+
|
41
|
+
* Then, Erd generates migration files on the server.
|
42
|
+
|
43
|
+
* And you can run each migration on your browser super quickly.
|
44
|
+
|
45
|
+
|
46
|
+
## TODO
|
47
|
+
|
48
|
+
* Fix buggy JS
|
49
|
+
|
50
|
+
* drop column (need to think of the UI)
|
51
|
+
|
52
|
+
* stop depending on Graphviz
|
53
|
+
|
54
|
+
* tests
|
55
|
+
|
56
|
+
* cleaner code (the code is horrible. Please don't read the code, though of course your patches welcome)
|
57
|
+
|
58
|
+
|
59
|
+
## Contributing to Erd
|
60
|
+
|
61
|
+
* Send me your pull requests!
|
62
|
+
|
63
|
+
|
64
|
+
## Team
|
65
|
+
|
66
|
+
* [Akira Matsuda][https://github.com/amatsuda]
|
67
|
+
* [Teppei Machida][http://github.com/machida] (design)
|
68
|
+
|
69
|
+
|
70
|
+
## Copyright
|
71
|
+
|
72
|
+
Copyright (c) 2012 Akira Matsuda. See MIT-LICENSE for further details.
|
@@ -6,62 +6,65 @@ require 'erd/application_controller'
|
|
6
6
|
|
7
7
|
module Erd
|
8
8
|
class ErdController < ::Erd::ApplicationController
|
9
|
+
POSITIONS_JSON_FILE = Rails.root.join('db/erd_positions.json').freeze
|
10
|
+
OLD_POSITIONS_JSON_FILE = Rails.root.join('db/erd_positions.json').freeze # for compatibility
|
11
|
+
|
9
12
|
def index
|
10
|
-
|
11
|
-
|
12
|
-
ActiveSupport::JSON.decode json.read
|
13
|
-
else
|
14
|
-
{}
|
15
|
-
end
|
16
|
-
@erd = render_plain plain, positions
|
13
|
+
@erd = render_plain generate_plain, saved_positions
|
14
|
+
end
|
17
15
|
|
16
|
+
def edit
|
17
|
+
@erd = render_plain generate_plain, saved_positions, true
|
18
18
|
@migrations = Erd::Migrator.status
|
19
19
|
end
|
20
20
|
|
21
21
|
def update
|
22
|
+
if params[:position_changes].present?
|
23
|
+
position_changes = ActiveSupport::JSON.decode(params[:position_changes])
|
24
|
+
positions = saved_positions
|
25
|
+
positions.merge! position_changes.transform_keys(&:tableize)
|
26
|
+
POSITIONS_JSON_FILE.open('w') {|f| f.write positions.to_json }
|
27
|
+
end
|
28
|
+
|
22
29
|
changes = params[:changes].present? ? ActiveSupport::JSON.decode(params[:changes]) : []
|
23
30
|
executed_migrations, failed_migrations = [], []
|
24
31
|
changes.each do |row|
|
25
32
|
begin
|
26
33
|
action, model, column, from, to = row['action'], row['model'], row['column'], row['from'], row['to']
|
27
|
-
|
34
|
+
|
35
|
+
case action
|
36
|
+
when 'create_model'
|
37
|
+
columns = column.split(' ').compact
|
38
|
+
generated_migration_file = Erd::GenaratorRunner.execute_generate_model model, columns
|
39
|
+
when 'remove_model'
|
40
|
+
model = model.tableize
|
41
|
+
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "drop_#{model}"
|
42
|
+
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n drop_table :#{model}\n end"
|
43
|
+
when 'rename_model'
|
44
|
+
_model, from, to = from.tableize, to.tableize, model.tableize
|
45
|
+
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "rename_#{from}_to_#{to}"
|
46
|
+
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n rename_table :#{from}, :#{to}\n end"
|
47
|
+
when 'add_column'
|
28
48
|
model = model.tableize
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
49
|
+
name_and_type = column.scan(/(.*)\((.*?)\)/).first
|
50
|
+
name, type = name_and_type[0], name_and_type[1]
|
51
|
+
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "add_#{name}_to_#{model}", ["#{name}:#{type}"]
|
52
|
+
when 'rename_column'
|
53
|
+
model = model.tableize
|
54
|
+
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "rename_#{model}_#{from}_to_#{to}"
|
55
|
+
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n rename_column :#{model}, :#{from}, :#{to}\n end"
|
56
|
+
when 'alter_column'
|
57
|
+
model = model.tableize
|
58
|
+
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "change_#{model}_#{column}_type_to_#{to}"
|
59
|
+
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n change_column :#{model}, :#{column}, :#{to}\n end"
|
60
|
+
when 'move'
|
61
|
+
# do nothing
|
33
62
|
else
|
34
|
-
|
35
|
-
when 'create_model'
|
36
|
-
columns = column.split(' ').compact
|
37
|
-
generated_migration_file = Erd::GenaratorRunner.execute_generate_model model, columns
|
38
|
-
when 'remove_model'
|
39
|
-
model = model.tableize
|
40
|
-
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "drop_#{model}"
|
41
|
-
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n drop_table :#{model}\n end"
|
42
|
-
when 'rename_model'
|
43
|
-
_model, from, to = from.tableize, to.tableize, model.tableize
|
44
|
-
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "rename_#{from}_to_#{to}"
|
45
|
-
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n rename_table :#{from}, :#{to}\n end"
|
46
|
-
when 'add_column'
|
47
|
-
model = model.tableize
|
48
|
-
name_and_type = column.scan(/(.*)\((.*?)\)/).first
|
49
|
-
name, type = name_and_type[0], name_and_type[1]
|
50
|
-
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "add_#{name}_to_#{model}", ["#{name}:#{type}"]
|
51
|
-
when 'rename_column'
|
52
|
-
model = model.tableize
|
53
|
-
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "rename_#{model}_#{from}_to_#{to}"
|
54
|
-
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n rename_column :#{model}, :#{from}, :#{to}\n end"
|
55
|
-
when 'alter_column'
|
56
|
-
model = model.tableize
|
57
|
-
generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "change_#{model}_#{column}_type_to_#{to}"
|
58
|
-
gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n change_column :#{model}, :#{column}, :#{to}\n end"
|
59
|
-
else
|
60
|
-
raise "unexpected action: #{action}"
|
61
|
-
end
|
62
|
-
Erd::Migrator.run_migrations :up => generated_migration_file
|
63
|
-
executed_migrations << generated_migration_file
|
63
|
+
raise "unexpected action: #{action}"
|
64
64
|
end
|
65
|
+
|
66
|
+
Erd::Migrator.run_migrations :up => generated_migration_file
|
67
|
+
executed_migrations << generated_migration_file
|
65
68
|
rescue ::Erd::MigrationError => e
|
66
69
|
failed_migrations << e.message
|
67
70
|
end
|
@@ -77,6 +80,16 @@ module Erd
|
|
77
80
|
|
78
81
|
private
|
79
82
|
|
83
|
+
def saved_positions
|
84
|
+
if POSITIONS_JSON_FILE.exist?
|
85
|
+
ActiveSupport::JSON.decode(POSITIONS_JSON_FILE.read)
|
86
|
+
elsif OLD_POSITIONS_JSON_FILE.exist?
|
87
|
+
ActiveSupport::JSON.decode(OLD_POSITIONS_JSON_FILE.read)
|
88
|
+
else
|
89
|
+
{}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
80
93
|
def generate_plain
|
81
94
|
if Rails.respond_to?(:autoloaders) && Rails.autoloaders.try(:zeitwerk_enabled?)
|
82
95
|
Zeitwerk::Loader.eager_load_all
|
@@ -115,20 +128,21 @@ module Erd
|
|
115
128
|
g.output('plain' => String)
|
116
129
|
end
|
117
130
|
|
118
|
-
def render_plain(plain, positions)
|
131
|
+
def render_plain(plain, positions, edit_mode = false)
|
119
132
|
_scale, svg_width, svg_height = plain.scan(/\Agraph ([\d\.]+) ([\d\.]+) ([\d\.]+)$/).first
|
120
133
|
# node name x y width height label style shape color fillcolor
|
121
|
-
max_model_x, max_model_y = 0, 0
|
122
134
|
models = plain.scan(/^node ([^ ]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([^ ]+) [^ ]+ [^ ]+ [^ ]+ [^ ]+\n/m).map {|model_name, x, y, width, height, label|
|
123
135
|
columns = label.gsub("\\\n", '').split('|')[1].split('\l').map {|name_and_type| name_and_type.scan(/(.*?)\((.*?)\)/).first }.map {|n, t| {:name => n, :type => t} }
|
124
136
|
custom_x, custom_y = positions[model_name.tableize].try(:split, ',')
|
125
|
-
|
126
|
-
max_model_x, max_model_y = [h[:x].to_i + h[:width].to_i, max_model_x, 1024].max, [h[:y].to_i + h[:height].to_i, max_model_y, 768].max
|
127
|
-
h
|
137
|
+
{:model => model_name, :x => (custom_x || (BigDecimal(x) * 72).round), :y => (custom_y || (BigDecimal(y) * 72).round), :width => (BigDecimal(width) * 72).round, :height => (BigDecimal(height) * 72).round, :columns => columns}
|
128
138
|
}.compact
|
139
|
+
max_model_x = models.map {|m| m[:x].to_f + m[:width].to_f }.max
|
140
|
+
erd_width = [[(BigDecimal(svg_width) * 72).round, max_model_x].compact.min + 150, 1024].max
|
141
|
+
max_model_y = models.map {|m| m[:y].to_f + m[:height].to_f }.max
|
142
|
+
erd_height = [[(BigDecimal(svg_height) * 72).round, max_model_y].compact.min + 150, 768].max
|
129
143
|
# edge tail head n x1 y1 .. xn yn [label xl yl] style color
|
130
144
|
edges = plain.scan(/^edge ([^ ]+)+ ([^ ]+)/).map {|from, to| {:from => from, :to => to}}
|
131
|
-
render_to_string 'erd/erd/erd', :layout => nil, :locals => {:width =>
|
145
|
+
render_to_string 'erd/erd/erd', :layout => nil, :locals => {:width => erd_width, :height => erd_height, :models => models, :edges => edges, :edit_mode => edit_mode}
|
132
146
|
end
|
133
147
|
|
134
148
|
def gsub_file(path, flag, *args, &block)
|
@@ -2,12 +2,16 @@
|
|
2
2
|
<table>
|
3
3
|
<tr>
|
4
4
|
<td>
|
5
|
-
<span class="column_name_text"><%= column[:name] %></span>
|
6
|
-
|
5
|
+
<span class="column_name_text<%= "#{' edit' if edit_mode}" %>"><%= column[:name] %></span>
|
6
|
+
<% if edit_mode %>
|
7
|
+
<form class="rename_column_form"><span><input name="model" type="hidden" value="<%= model[:model] %>" /><input name="column" type="hidden" value="<%= column[:name] %>" /><input name="to" type="text" /><input type="submit" value="Change" /></span><a href="#!" class="cancel">Cancel</a></form>
|
8
|
+
<% end %>
|
7
9
|
</td>
|
8
10
|
<td>
|
9
|
-
<span class="column_type_text <%= column[:type] %>"><%= column[:type] %></span>
|
10
|
-
|
11
|
+
<span class="column_type_text <%= column[:type] %><%= "#{' edit' if edit_mode}" %>"><%= column[:type] %></span>
|
12
|
+
<% if edit_mode %>
|
13
|
+
<form class="alter_column_form"><span><input name="model" type="hidden" value="<%= model[:model] %>" /><input name="column" type="hidden" value="<%= column[:name] %>" /><input name="type" type="hidden" value="<%= column[:type] %>" /><input name="to" type="text" /><input type="submit" value="Change" /></span><a href="#!" class="cancel">Cancel</a></form>
|
14
|
+
<% end %>
|
11
15
|
</td>
|
12
16
|
</tr>
|
13
17
|
</table>
|
@@ -1,28 +1,34 @@
|
|
1
1
|
<div id="erd-<%= model[:model] %>" class="model" style="top: <%= model[:y] %>px; left: <%= model[:x] %>px;" data-model_name="<%= model[:model] %>">
|
2
|
-
|
3
|
-
|
2
|
+
<% if edit_mode %>
|
3
|
+
<a href="#!" class="close"><%= image_tag '/erd/close.png' %></a>
|
4
|
+
<% end %>
|
5
|
+
<div class="model_name<%= "#{' edit' if edit_mode}" %>">
|
4
6
|
<div class="model_name_text"><%= model[:model] %></div>
|
5
|
-
|
6
|
-
<
|
7
|
-
<
|
8
|
-
<
|
9
|
-
<
|
10
|
-
|
11
|
-
|
12
|
-
<
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
7
|
+
<% if edit_mode %>
|
8
|
+
<form class="rename_model_form">
|
9
|
+
<table>
|
10
|
+
<tr>
|
11
|
+
<td>
|
12
|
+
<input name="model" type="hidden" value="<%= model[:model] %>"/>
|
13
|
+
</td>
|
14
|
+
<td>
|
15
|
+
<input name="to" type="text" /><input type="submit" value="Change"/>
|
16
|
+
</td>
|
17
|
+
</tr>
|
18
|
+
</table>
|
19
|
+
<a href="#!" class="cancel">Cancel</a>
|
20
|
+
</form>
|
21
|
+
<% end %>
|
18
22
|
</div>
|
19
23
|
<div class="columns">
|
20
24
|
<ul>
|
21
|
-
<%= render :partial => 'erd/erd/column', :collection => model[:columns], :locals => {:model => model} -%>
|
25
|
+
<%= render :partial => 'erd/erd/column', :collection => model[:columns], :locals => {:model => model, :edit_mode => edit_mode} -%>
|
22
26
|
</ul>
|
23
27
|
</div>
|
24
28
|
<div class="add_column_box">
|
25
|
-
|
29
|
+
<% if edit_mode %>
|
30
|
+
<a href="#!" class="add_column">add column</a>
|
31
|
+
<% end %>
|
26
32
|
<form class="add_column_form">
|
27
33
|
<input name="model" type="hidden" value="<%= model[:model] %>" />
|
28
34
|
<table>
|
@@ -0,0 +1,87 @@
|
|
1
|
+
<div id="erd_box">
|
2
|
+
<div id="erd_container">
|
3
|
+
<%=raw @erd %>
|
4
|
+
</div>
|
5
|
+
<div id="open_migration"><%= image_tag '/erd/angle-left.png' %></div>
|
6
|
+
<div id="migration">
|
7
|
+
<div id="close_migration"><%= image_tag '/erd/angle-right.png' %></div>
|
8
|
+
<%- if flash[:executed_migrations].present? -%>
|
9
|
+
<div id="executed">
|
10
|
+
<h2>Successfully executed following <%= flash[:executed_migrations].values.flatten.count %> migrations!</h2>
|
11
|
+
<%- [:up, :down].each do |direction| -%>
|
12
|
+
<%- if flash[:executed_migrations][direction].present? -%>
|
13
|
+
<h3><%= direction %></h3>
|
14
|
+
<ul>
|
15
|
+
<%- flash[:executed_migrations][direction].each do |m| -%>
|
16
|
+
<li><%= File.basename m.to_s %></li>
|
17
|
+
<%- end -%>
|
18
|
+
</ul>
|
19
|
+
<%- end -%>
|
20
|
+
<%- end -%>
|
21
|
+
</div>
|
22
|
+
<%- end -%>
|
23
|
+
<%- if flash[:failed_migrations].present? -%>
|
24
|
+
<div id="failed">
|
25
|
+
<h2>failed migrations</h2>
|
26
|
+
<ul>
|
27
|
+
<%- flash[:failed_migrations].each do |m| -%>
|
28
|
+
<li><%= m %></li>
|
29
|
+
<%- end -%>
|
30
|
+
</ul>
|
31
|
+
</div>
|
32
|
+
<%- end -%>
|
33
|
+
<table id="changes">
|
34
|
+
<caption>schema changes</caption>
|
35
|
+
<thead><tr><th>action</th><th>model</th><th>column</th><th>from</th><th>to</th></tr></thead>
|
36
|
+
<tbody></tbody>
|
37
|
+
</table>
|
38
|
+
<%= form_tag '/erd', :method => :put, :id => 'changes_form' do %>
|
39
|
+
<%= hidden_field_tag 'changes' %>
|
40
|
+
<%= hidden_field_tag 'position_changes' %>
|
41
|
+
<%= submit_tag 'save changes' %>
|
42
|
+
<% end %>
|
43
|
+
<%= form_tag '/erd/migrate', :method => :put, :id => 'migrate_form' do %>
|
44
|
+
<ul id="open_buttons">
|
45
|
+
<li><button id="open_up" type="button">up</button></li>
|
46
|
+
<li><button id="open_down" type="button">down</button></li>
|
47
|
+
<li><button id="close_all" type="button">close</button></li>
|
48
|
+
</ul>
|
49
|
+
<table id="migration_status">
|
50
|
+
<caption>migration status</caption>
|
51
|
+
<thead>
|
52
|
+
<tr><th class="status">status</th><th class="version">version</th><th class="name">name</th></tr>
|
53
|
+
<tr><td colspan="3" class="migrations">
|
54
|
+
<button>UNFOLD ALL</button>
|
55
|
+
<%= @migrations.count %> migrations in total (up: <%= @migrations.count {|m| m[:status] == 'up'} %>, down: <%= @migrations.count {|m| m[:status] == 'down'} %>)
|
56
|
+
</td></tr>
|
57
|
+
</thead>
|
58
|
+
<tbody>
|
59
|
+
<%- @migrations.each do |m| -%>
|
60
|
+
<tr class="<%= m[:status] %>">
|
61
|
+
<td><label><%= check_box_tag (m[:status] == 'up' ? 'down[]' : 'up[]'), m[:filename], false, :id => "check_#{m[:filename]}" %><%= m[:status] %></label></td>
|
62
|
+
<td><%= m[:version] %></td>
|
63
|
+
<td class="migration_file_name"><%= m[:name] %></td>
|
64
|
+
</tr>
|
65
|
+
<%- end -%>
|
66
|
+
</tbody>
|
67
|
+
</table>
|
68
|
+
<%= submit_tag 'run migrations' %>
|
69
|
+
<% end %>
|
70
|
+
</div>
|
71
|
+
<a href="#" id='open_create_model_dialog'%>Create Model</a>
|
72
|
+
<div id="create_model_form">
|
73
|
+
<form>
|
74
|
+
Model Name: <input id="new_model_name" name="new_model_name" type="text" /><br>
|
75
|
+
<table id="create_model_table">
|
76
|
+
<tbody>
|
77
|
+
<tr>
|
78
|
+
<td><input name="new_model_column_name_1" type="text" /></td>
|
79
|
+
<td class="separator">:</td>
|
80
|
+
<td><input name="new_model_column_type_1" type="text" value="string" /></td>
|
81
|
+
</tr>
|
82
|
+
</tbody>
|
83
|
+
</table>
|
84
|
+
<div><a href="#" id="new_model_add_column" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">Add More Column</span></a></div>
|
85
|
+
</form>
|
86
|
+
</div>
|
87
|
+
</div>
|
@@ -1,4 +1,4 @@
|
|
1
1
|
<div id="erd" data-svg_width="<%= width %>" data-svg_height="<%= height %>">
|
2
|
-
<%= render :partial => 'erd/erd/model', :collection => models -%>
|
2
|
+
<%= render :partial => 'erd/erd/model', :collection => models, :locals => {:edit_mode => edit_mode} -%>
|
3
3
|
<script>window.raw_edges = <%= edges.to_json.html_safe %>;</script>
|
4
4
|
</div>
|
@@ -2,85 +2,16 @@
|
|
2
2
|
<div id="erd_container">
|
3
3
|
<%=raw @erd %>
|
4
4
|
</div>
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
<%- end -%>
|
18
|
-
</ul>
|
19
|
-
<%- end -%>
|
20
|
-
<%- end -%>
|
21
|
-
</div>
|
22
|
-
<%- end -%>
|
23
|
-
<%- if flash[:failed_migrations].present? -%>
|
24
|
-
<div id="failed">
|
25
|
-
<h2>failed migrations</h2>
|
26
|
-
<ul>
|
27
|
-
<%- flash[:failed_migrations].each do |m| -%>
|
28
|
-
<li><%= m %></li>
|
29
|
-
<%- end -%>
|
30
|
-
</ul>
|
31
|
-
</div>
|
32
|
-
<%- end -%>
|
33
|
-
<table id="changes">
|
34
|
-
<caption>schema changes</caption>
|
35
|
-
<thead><tr><th>action</th><th>model</th><th>column</th><th>from</th><th>to</th></tr></thead>
|
36
|
-
<tbody></tbody>
|
37
|
-
</table>
|
38
|
-
<%= form_tag '/erd', :method => :put, :id => 'changes_form' do %>
|
39
|
-
<%= hidden_field_tag 'changes' %>
|
40
|
-
<%= submit_tag 'save changes' %>
|
41
|
-
<% end %>
|
42
|
-
<%= form_tag '/erd/migrate', :method => :put, :id => 'migrate_form' do %>
|
43
|
-
<ul id="open_buttons">
|
44
|
-
<li><button id="open_up" type="button">up</button></li>
|
45
|
-
<li><button id="open_down" type="button">down</button></li>
|
46
|
-
<li><button id="close_all" type="button">close</button></li>
|
47
|
-
</ul>
|
48
|
-
<table id="migration_status">
|
49
|
-
<caption>migration status</caption>
|
50
|
-
<thead>
|
51
|
-
<tr><th class="status">status</th><th class="version">version</th><th class="name">name</th></tr>
|
52
|
-
<tr><td colspan="3" class="migrations">
|
53
|
-
<button>UNFOLD ALL</button>
|
54
|
-
<%= @migrations.count %> migrations in total (up: <%= @migrations.count {|m| m[:status] == 'up'} %>, down: <%= @migrations.count {|m| m[:status] == 'down'} %>)
|
55
|
-
</td></tr>
|
56
|
-
</thead>
|
57
|
-
<tbody>
|
58
|
-
<%- @migrations.each do |m| -%>
|
59
|
-
<tr class="<%= m[:status] %>">
|
60
|
-
<td><label><%= check_box_tag (m[:status] == 'up' ? 'down[]' : 'up[]'), m[:filename] %><%= m[:status] %></label></td>
|
61
|
-
<td><%= m[:version] %></td>
|
62
|
-
<td class="migration_file_name"><%= m[:name] %></td>
|
63
|
-
</tr>
|
64
|
-
<%- end -%>
|
65
|
-
</tbody>
|
66
|
-
</table>
|
67
|
-
<%= submit_tag 'run migrations' %>
|
68
|
-
<% end %>
|
69
|
-
</div>
|
70
|
-
<a href="#" id='open_create_model_dialog'%>Create Model</a>
|
71
|
-
<div id="create_model_form">
|
72
|
-
<form>
|
73
|
-
Model Name: <input id="new_model_name" name="new_model_name" type="text" /><br>
|
74
|
-
<table id="create_model_table">
|
75
|
-
<tbody>
|
76
|
-
<tr>
|
77
|
-
<td><input name="new_model_column_name_1" type="text" /></td>
|
78
|
-
<td class="separator">:</td>
|
79
|
-
<td><input name="new_model_column_type_1" type="text" value="string" /></td>
|
80
|
-
</tr>
|
81
|
-
</tbody>
|
82
|
-
</table>
|
83
|
-
<div><a href="#" id="new_model_add_column" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"><span class="ui-button-text">Add More Column</span></a></div>
|
84
|
-
</form>
|
85
|
-
</div>
|
5
|
+
<%= form_tag '/erd', :method => :put, :id => 'changes_form' do %>
|
6
|
+
<%= hidden_field_tag 'position_changes' %>
|
7
|
+
<% end %>
|
8
|
+
<input id="drawer_switch" type="checkbox">
|
9
|
+
<label id="drawer_hide_label" class="drawer_switch_label" for="drawer_switch">> Hide Buttons</label>
|
10
|
+
<label id="drawer_show_label" class="drawer_switch_label" for="drawer_switch"><</label>
|
11
|
+
<nav id="buttons">
|
12
|
+
<ul>
|
13
|
+
<li><a href="#!" id="save_position_changes" class="menu_button">Save Position Changes</a></li>
|
14
|
+
<li><%= link_to 'Edit the Database', :edit, :id => 'link_to_edit', :class => 'menu_button' %></li>
|
15
|
+
</ul>
|
16
|
+
</nav>
|
86
17
|
</div>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<meta charset='utf-8'>
|
5
|
-
<title>ERD - <%= Rails.application.class.
|
5
|
+
<title>ERD - <%= Rails.application.class.name[/[^:]+/] %></title>
|
6
6
|
<%= stylesheet_link_tag '/erd/erd', :media => 'all' %>
|
7
7
|
<%= stylesheet_link_tag 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css', :media => 'all' %>
|
8
8
|
<%= javascript_include_tag 'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js' %>
|
data/config/routes.rb
CHANGED
data/erd.gemspec
CHANGED
@@ -11,9 +11,9 @@ Gem::Specification.new do |gem|
|
|
11
11
|
gem.homepage = 'https://github.com/amatsuda/erd'
|
12
12
|
gem.license = 'MIT'
|
13
13
|
|
14
|
-
gem.files
|
15
|
-
|
16
|
-
|
14
|
+
gem.files = Dir.chdir(File.expand_path(__dir__)) do
|
15
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
16
|
+
end
|
17
17
|
gem.name = 'erd'
|
18
18
|
gem.require_paths = ['lib']
|
19
19
|
gem.version = Erd::VERSION
|
data/lib/erd/version.rb
CHANGED
data/public/erd/erd.css
CHANGED
@@ -219,8 +219,9 @@ li {
|
|
219
219
|
box-shadow: rgba(0, 0, 0, 0.7) 0 2px 2px; }
|
220
220
|
#erd .model .model_name {
|
221
221
|
border-bottom: 1px #444444 solid;
|
222
|
-
cursor: pointer;
|
223
222
|
margin: 1px 5px 0 5px auto; }
|
223
|
+
#erd .model .model_name.edit {
|
224
|
+
cursor: pointer; }
|
224
225
|
#erd .model .model_name .model_name_text {
|
225
226
|
text-align: center;
|
226
227
|
display: block;
|
@@ -229,10 +230,10 @@ li {
|
|
229
230
|
background: -ms-linear-gradient(top, #eeeeee 0%, #d0d0d0 88%);
|
230
231
|
background: -o-linear-gradient(top, #eeeeee 0%, #d0d0d0 88%);
|
231
232
|
background: linear-gradient(top, #eeeeee 0%, #d0d0d0 88%);
|
232
|
-
font-size:
|
233
|
+
font-size: 15px;
|
233
234
|
font-weight: bold;
|
234
235
|
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
235
|
-
line-height:
|
236
|
+
line-height: 1.6;
|
236
237
|
padding: 0 0.8em; }
|
237
238
|
#erd .model .model_name .model_name_text.unsaved {
|
238
239
|
background: #fffebe;
|
@@ -314,7 +315,7 @@ li {
|
|
314
315
|
font-size: small;
|
315
316
|
display: block;
|
316
317
|
padding: 0 5px;
|
317
|
-
line-height:
|
318
|
+
line-height: 1.6;
|
318
319
|
background: #444444;
|
319
320
|
color: white;
|
320
321
|
text-decoration: none; }
|
@@ -328,9 +329,9 @@ li {
|
|
328
329
|
|
329
330
|
.columns .column {
|
330
331
|
border-bottom: dotted 1px #cccccc;
|
331
|
-
padding: 0 0.
|
332
|
+
padding: 0 0.5em;
|
332
333
|
clear: both;
|
333
|
-
line-height:
|
334
|
+
line-height: 20px;
|
334
335
|
font-size: 14px; }
|
335
336
|
.columns .column table {
|
336
337
|
width: 100%; }
|
@@ -338,12 +339,12 @@ li {
|
|
338
339
|
background: #f9f9f9; }
|
339
340
|
.columns .column:last-child {
|
340
341
|
border-bottom: none; }
|
341
|
-
.columns .column .column_name_text,
|
342
|
-
.columns .column .column_type_text {
|
342
|
+
.columns .column .column_name_text.edit,
|
343
|
+
.columns .column .column_type_text.edit {
|
343
344
|
cursor: pointer; }
|
344
345
|
.columns .column .column_name_text {
|
345
346
|
margin: 0 1.4em 0 0; }
|
346
|
-
.columns .column .column_name_text:hover {
|
347
|
+
.columns .column .column_name_text.edit:hover {
|
347
348
|
text-decoration: underline; }
|
348
349
|
.columns .column.unsaved {
|
349
350
|
background: #fffebe;
|
@@ -377,11 +378,11 @@ li {
|
|
377
378
|
.columns .column .rename_column_form {
|
378
379
|
padding: 8px; }
|
379
380
|
.columns .column .column_type_text {
|
380
|
-
font-size:
|
381
|
+
font-size: 13px;
|
381
382
|
float: right;
|
382
383
|
display: block;
|
383
384
|
line-height: 16px;
|
384
|
-
margin:
|
385
|
+
margin: 1px 0 0;
|
385
386
|
-webkit-border-radius: 4px;
|
386
387
|
-moz-border-radius: 4px;
|
387
388
|
-ms-border-radius: 4px;
|
@@ -395,7 +396,7 @@ li {
|
|
395
396
|
background: #f9eee9;
|
396
397
|
color: #444444;
|
397
398
|
border-style: solid; }
|
398
|
-
.columns .column .column_type_text:hover {
|
399
|
+
.columns .column .column_type_text.edit:hover {
|
399
400
|
border-color: #e36c33;
|
400
401
|
background: #edd0c2; }
|
401
402
|
.columns .column .column_type_text.date, .columns .column .column_type_text.time, .columns .column .column_type_text.timestamp, .columns .column .column_type_text.datetime {
|
@@ -403,7 +404,7 @@ li {
|
|
403
404
|
background: #f3e2e4;
|
404
405
|
color: #444444;
|
405
406
|
border-style: solid; }
|
406
|
-
.columns .column .column_type_text.date:hover, .columns .column .column_type_text.time:hover, .columns .column .column_type_text.timestamp:hover, .columns .column .column_type_text.datetime:hover {
|
407
|
+
.columns .column .column_type_text.edit.date:hover, .columns .column .column_type_text.edit.time:hover, .columns .column .column_type_text.edit.timestamp:hover, .columns .column .column_type_text.edit.datetime:hover {
|
407
408
|
border-color: #d0394d;
|
408
409
|
background: #e4bec3; }
|
409
410
|
.columns .column .column_type_text.boolean {
|
@@ -411,7 +412,7 @@ li {
|
|
411
412
|
background: #c7dce6;
|
412
413
|
color: #444444;
|
413
414
|
border-style: solid; }
|
414
|
-
.columns .column .column_type_text.boolean:hover {
|
415
|
+
.columns .column .column_type_text.edit.boolean:hover {
|
415
416
|
border-color: #2e87b3;
|
416
417
|
background: #a3c6d7; }
|
417
418
|
.columns .column .column_type_text.text {
|
@@ -419,7 +420,7 @@ li {
|
|
419
420
|
background: #efe7d3;
|
420
421
|
color: #444444;
|
421
422
|
border-style: solid; }
|
422
|
-
.columns .column .column_type_text.text:hover {
|
423
|
+
.columns .column .column_type_text.edit.text:hover {
|
423
424
|
border-color: #cb9d2b;
|
424
425
|
background: #e1d2ae; }
|
425
426
|
.columns .column .column_type_text.decimal {
|
@@ -427,7 +428,7 @@ li {
|
|
427
428
|
background: #dfdf50;
|
428
429
|
color: #444444;
|
429
430
|
border-style: solid; }
|
430
|
-
.columns .column .column_type_text.decimal:hover {
|
431
|
+
.columns .column .column_type_text.edit.decimal:hover {
|
431
432
|
border-color: #5e5e05;
|
432
433
|
background: #d5d527; }
|
433
434
|
.columns .column .column_type_text.float {
|
@@ -435,7 +436,7 @@ li {
|
|
435
436
|
background: silver;
|
436
437
|
color: #444444;
|
437
438
|
border-style: solid; }
|
438
|
-
.columns .column .column_type_text.float:hover {
|
439
|
+
.columns .column .column_type_text.edit.float:hover {
|
439
440
|
border-color: #684b53;
|
440
441
|
background: #a6a6a6; }
|
441
442
|
.columns .column .column_type_text.integer {
|
@@ -443,7 +444,7 @@ li {
|
|
443
444
|
background: #dfdfe0;
|
444
445
|
color: #444444;
|
445
446
|
border-style: solid; }
|
446
|
-
.columns .column .column_type_text.integer:hover {
|
447
|
+
.columns .column .column_type_text.edit.integer:hover {
|
447
448
|
border-color: #606493;
|
448
449
|
background: #c5c5c7; }
|
449
450
|
.columns .column .column_type_text.primary_key {
|
@@ -451,7 +452,7 @@ li {
|
|
451
452
|
background: #f9efe7;
|
452
453
|
color: #444444;
|
453
454
|
border-style: solid; }
|
454
|
-
.columns .column .column_type_text.primary_key:hover {
|
455
|
+
.columns .column .column_type_text.edit.primary_key:hover {
|
455
456
|
border-color: #e47e30;
|
456
457
|
background: #eed3bf; }
|
457
458
|
.columns .column .column_type_text.string {
|
@@ -459,7 +460,7 @@ li {
|
|
459
460
|
background: #d3e09e;
|
460
461
|
color: #444444;
|
461
462
|
border-style: solid; }
|
462
|
-
.columns .column .column_type_text.string:hover {
|
463
|
+
.columns .column .column_type_text.edit.string:hover {
|
463
464
|
border-color: #7f9919;
|
464
465
|
background: #c1d477; }
|
465
466
|
.columns .column .column_type_text.unsaved {
|
@@ -510,6 +511,54 @@ li {
|
|
510
511
|
#model_name_changes, #column_name_changes {
|
511
512
|
display: none; }
|
512
513
|
|
514
|
+
#drawer_switch {
|
515
|
+
display: none; }
|
516
|
+
#drawer_switch:checked ~ #buttons {
|
517
|
+
right: -400px; }
|
518
|
+
#drawer_switch:checked ~ #drawer_hide_label {
|
519
|
+
visibility: hidden; }
|
520
|
+
#drawer_switch:checked ~ #drawer_show_label {
|
521
|
+
visibility: visible; }
|
522
|
+
|
523
|
+
#buttons {
|
524
|
+
position: fixed;
|
525
|
+
top: 70px;
|
526
|
+
right: 15px;
|
527
|
+
-webkit-transition: all 0.5s;
|
528
|
+
transition: all 0.5s; }
|
529
|
+
|
530
|
+
.drawer_switch_label {
|
531
|
+
position: fixed;
|
532
|
+
right: 15px;
|
533
|
+
top: 10px;
|
534
|
+
background: rgba(0, 0, 0, 0.6);
|
535
|
+
padding: 12px 20px;
|
536
|
+
color: white;
|
537
|
+
cursor: pointer;
|
538
|
+
text-decoration: none;
|
539
|
+
font-size: 13px; }
|
540
|
+
.drawer_switch_label:hover {
|
541
|
+
background: rgba(0, 0, 0, 0.8); }
|
542
|
+
#drawer_show_label {
|
543
|
+
visibility: hidden; }
|
544
|
+
|
545
|
+
.menu_button {
|
546
|
+
position: relative;
|
547
|
+
right: 0;
|
548
|
+
background: rgba(0, 0, 0, 0.6);
|
549
|
+
padding: 12px 20px;
|
550
|
+
color: white;
|
551
|
+
text-decoration: none;
|
552
|
+
font-size: 13px; }
|
553
|
+
.menu_button:hover {
|
554
|
+
background: rgba(0, 0, 0, 0.8); }
|
555
|
+
#save_position_changes {
|
556
|
+
visibility: hidden;
|
557
|
+
top: 70px;
|
558
|
+
background: rgba(191, 0, 0, 0.6); }
|
559
|
+
#save_position_changes:hover {
|
560
|
+
background: rgba(191, 0, 0, 0.8); }
|
561
|
+
|
513
562
|
#open_create_model_dialog {
|
514
563
|
position: fixed;
|
515
564
|
right: 15px;
|
data/public/erd/erd.js
CHANGED
@@ -19,10 +19,12 @@ class ERD {
|
|
19
19
|
this.handle_new_model_add_column_click = this.handle_new_model_add_column_click.bind(this);
|
20
20
|
this.handle_open_migration_click = this.handle_open_migration_click.bind(this);
|
21
21
|
this.handle_close_migration_click = this.handle_close_migration_click.bind(this);
|
22
|
+
this.handle_save_position_changes_click = this.handle_save_position_changes_click.bind(this);
|
22
23
|
this.name = name;
|
23
24
|
this.elem = elem;
|
24
25
|
this.edges = edges;
|
25
26
|
this.paper = Raphael(this.name, this.elem.data('svg_width'), this.elem.data('svg_height'));
|
27
|
+
this.position_changes = {};
|
26
28
|
this.setup_handlers();
|
27
29
|
const models = this.elem.find('.model');
|
28
30
|
this.models = {};
|
@@ -122,11 +124,13 @@ class ERD {
|
|
122
124
|
const from = target.data('original_position');
|
123
125
|
const to = [target.css('left').replace(/px$/, ''), target.css('top').replace(/px$/, '')].join();
|
124
126
|
this.upsert_change('move', model_name, '', '', to);
|
127
|
+
this.position_changes[model_name] = to;
|
125
128
|
this.connect_arrows(this.edges.filter(e=> (e.from === model_name) || (e.to === model_name)));
|
129
|
+
document.getElementById("save_position_changes").style.visibility = "visible";
|
126
130
|
}
|
127
131
|
|
128
132
|
setup_click_handlers() {
|
129
|
-
$('div.model_name_text, span.column_name_text, span.column_type_text').on('click', this.handle_text_elem_click);
|
133
|
+
$('div.model_name_text.edit, span.column_name_text.edit, span.column_type_text.edit').on('click', this.handle_text_elem_click);
|
130
134
|
$('div.model a.add_column').on('click', this.handle_add_column_click);
|
131
135
|
$('div.model a.cancel').on('click', this.handle_cancel_click);
|
132
136
|
$('div.model a.close').on('click', this.handle_remove_model_click);
|
@@ -134,6 +138,7 @@ class ERD {
|
|
134
138
|
$('div.model a.cancel').on('click', this.handle_cancel_click);
|
135
139
|
$('div#open_migration').on('click', this.handle_open_migration_click);
|
136
140
|
$('div#close_migration').on('click', this.handle_close_migration_click);
|
141
|
+
$('#save_position_changes').on('click', this.handle_save_position_changes_click);
|
137
142
|
}
|
138
143
|
|
139
144
|
setup_submit_handlers() {
|
@@ -165,6 +170,7 @@ class ERD {
|
|
165
170
|
return change;
|
166
171
|
}).toArray();
|
167
172
|
$('#changes_form').find('input[name=changes]').val(JSON.stringify(changes));
|
173
|
+
$('#changes_form').find('input[name=position_changes]').val(JSON.stringify(this.position_changes));
|
168
174
|
}
|
169
175
|
|
170
176
|
handle_add_column(ev) {
|
@@ -359,6 +365,11 @@ class ERD {
|
|
359
365
|
.parent().hide()
|
360
366
|
.prev('div').show();
|
361
367
|
}
|
368
|
+
|
369
|
+
handle_save_position_changes_click(ev) {
|
370
|
+
$('#changes_form').find('input[name=position_changes]').val(JSON.stringify(this.position_changes));
|
371
|
+
$('#changes_form').submit();
|
372
|
+
}
|
362
373
|
}
|
363
374
|
|
364
375
|
$(function() {
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Akira Matsuda
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-graphviz
|
@@ -161,12 +161,13 @@ files:
|
|
161
161
|
- ".travis.yml"
|
162
162
|
- Gemfile
|
163
163
|
- MIT-LICENSE
|
164
|
-
- README.
|
164
|
+
- README.md
|
165
165
|
- Rakefile
|
166
166
|
- app/controllers/erd/application_controller.rb
|
167
167
|
- app/controllers/erd/erd_controller.rb
|
168
168
|
- app/views/erd/erd/_column.html.erb
|
169
169
|
- app/views/erd/erd/_model.html.erb
|
170
|
+
- app/views/erd/erd/edit.html.erb
|
170
171
|
- app/views/erd/erd/erd.html.erb
|
171
172
|
- app/views/erd/erd/index.html.erb
|
172
173
|
- app/views/layouts/erd/application.html.erb
|
@@ -196,21 +197,11 @@ files:
|
|
196
197
|
- public/erd/erd.css
|
197
198
|
- public/erd/erd.js
|
198
199
|
- src/assets/stylesheets/erd/erd.css.scss
|
199
|
-
- test/fake_app/config/database.yml
|
200
|
-
- test/fake_app/db/migrate/20120428022519_create_authors.rb
|
201
|
-
- test/fake_app/db/migrate/20120428022535_create_books.rb
|
202
|
-
- test/fake_app/db/schema.rb
|
203
|
-
- test/fake_app/fake_app.rb
|
204
|
-
- test/fake_app/log/.gitignore
|
205
|
-
- test/fake_app/tmp/.gitkeep
|
206
|
-
- test/features/erd_test.rb
|
207
|
-
- test/lib/migrator_test.rb
|
208
|
-
- test/test_helper.rb
|
209
200
|
homepage: https://github.com/amatsuda/erd
|
210
201
|
licenses:
|
211
202
|
- MIT
|
212
203
|
metadata: {}
|
213
|
-
post_install_message:
|
204
|
+
post_install_message:
|
214
205
|
rdoc_options: []
|
215
206
|
require_paths:
|
216
207
|
- lib
|
@@ -225,18 +216,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
225
216
|
- !ruby/object:Gem::Version
|
226
217
|
version: '0'
|
227
218
|
requirements: []
|
228
|
-
rubygems_version: 3.0.
|
229
|
-
signing_key:
|
219
|
+
rubygems_version: 3.4.0.dev
|
220
|
+
signing_key:
|
230
221
|
specification_version: 4
|
231
222
|
summary: erd engine on Rails
|
232
|
-
test_files:
|
233
|
-
- test/fake_app/config/database.yml
|
234
|
-
- test/fake_app/db/migrate/20120428022519_create_authors.rb
|
235
|
-
- test/fake_app/db/migrate/20120428022535_create_books.rb
|
236
|
-
- test/fake_app/db/schema.rb
|
237
|
-
- test/fake_app/fake_app.rb
|
238
|
-
- test/fake_app/log/.gitignore
|
239
|
-
- test/fake_app/tmp/.gitkeep
|
240
|
-
- test/features/erd_test.rb
|
241
|
-
- test/lib/migrator_test.rb
|
242
|
-
- test/test_helper.rb
|
223
|
+
test_files: []
|
data/README.rdoc
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
= Erd
|
2
|
-
|
3
|
-
A Rails engine for drawing your app's ER diagram and operating migrations
|
4
|
-
|
5
|
-
|
6
|
-
== Requirements
|
7
|
-
|
8
|
-
* Rails 5.2, 5.1, 5.0, 4.2, 4.1, 4.0, 3.2, or 3.1
|
9
|
-
|
10
|
-
* Graphviz
|
11
|
-
|
12
|
-
|
13
|
-
== Installation
|
14
|
-
|
15
|
-
Add 'erd' gem to your existing app's Gemfile:
|
16
|
-
gem 'erd'
|
17
|
-
|
18
|
-
Bundle it:
|
19
|
-
% bundle
|
20
|
-
|
21
|
-
|
22
|
-
== Usage
|
23
|
-
|
24
|
-
Browse at your http://localhost:3000/erd
|
25
|
-
|
26
|
-
|
27
|
-
== Features
|
28
|
-
|
29
|
-
* Erd draws an ER diagram based on your app's database and models
|
30
|
-
|
31
|
-
* You can drag and arrange the positions of each model
|
32
|
-
|
33
|
-
* You can operate DB schema manipulations such as `add column`, `rename column`, `alter column`, `create model (as well as table)`, and `drop table`
|
34
|
-
|
35
|
-
* Then, Erd generates migration files on the server
|
36
|
-
|
37
|
-
* And you can run each migration on your browser
|
38
|
-
|
39
|
-
|
40
|
-
== TODO
|
41
|
-
|
42
|
-
* Fix buggy JS
|
43
|
-
|
44
|
-
* drop column (need to think of the UI)
|
45
|
-
|
46
|
-
* stop depending on Graphviz
|
47
|
-
|
48
|
-
* tests
|
49
|
-
|
50
|
-
* cleaner code (the code is horrible. Please don't read the code, though of course your patches welcome)
|
51
|
-
|
52
|
-
|
53
|
-
== Contributing to Erd
|
54
|
-
|
55
|
-
* Send me your pull requests!
|
56
|
-
|
57
|
-
|
58
|
-
== Team
|
59
|
-
|
60
|
-
* {Akira Matsuda}[https://github.com/amatsuda]
|
61
|
-
* {Teppei Machida}[http://github.com/machida] (design)
|
62
|
-
|
63
|
-
|
64
|
-
== Copyright
|
65
|
-
|
66
|
-
Copyright (c) 2012 Akira Matsuda. See MIT-LICENSE for further details.
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class CreateBooks < ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration
|
4
|
-
def change
|
5
|
-
create_table :books do |t|
|
6
|
-
t.references :author
|
7
|
-
t.string :title
|
8
|
-
|
9
|
-
t.timestamps
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
data/test/fake_app/db/schema.rb
DELETED
File without changes
|
data/test/fake_app/fake_app.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record'
|
4
|
-
require 'action_controller/railtie'
|
5
|
-
|
6
|
-
# config
|
7
|
-
module ErdApp
|
8
|
-
class Application < Rails::Application
|
9
|
-
# Rais.root
|
10
|
-
config.root = File.dirname(__FILE__)
|
11
|
-
|
12
|
-
config.secret_token = 'fall to your knees and repent if you please'
|
13
|
-
config.session_store :cookie_store, :key => '_myapp_session'
|
14
|
-
config.active_support.deprecation = :log
|
15
|
-
config.eager_load = false
|
16
|
-
|
17
|
-
config.app_generators.orm :active_record, :migration => true, :timestamps => true
|
18
|
-
end
|
19
|
-
end
|
20
|
-
ErdApp::Application.initialize!
|
21
|
-
ErdApp::Application.routes.draw {}
|
22
|
-
|
23
|
-
# models
|
24
|
-
class Author < ActiveRecord::Base
|
25
|
-
has_many :books
|
26
|
-
end
|
27
|
-
class Book < ActiveRecord::Base
|
28
|
-
belongs_to :author
|
29
|
-
end
|
30
|
-
|
31
|
-
# helpers
|
32
|
-
module ApplicationHelper; end
|
@@ -1 +0,0 @@
|
|
1
|
-
*.log
|
data/test/fake_app/tmp/.gitkeep
DELETED
File without changes
|
data/test/features/erd_test.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
|
5
|
-
class ErdIndexTest < ActionDispatch::IntegrationTest
|
6
|
-
test 'with author and book model' do
|
7
|
-
visit '/erd'
|
8
|
-
|
9
|
-
if Capybara::VERSION > '3'
|
10
|
-
assert has_content? 'Author', :minimum => 1
|
11
|
-
else
|
12
|
-
assert has_content? 'Author'
|
13
|
-
end
|
14
|
-
assert has_content? 'name'
|
15
|
-
assert has_content? 'Book'
|
16
|
-
assert has_content? 'title'
|
17
|
-
end
|
18
|
-
end
|
data/test/lib/migrator_test.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'test_helper'
|
4
|
-
|
5
|
-
class MigratorTest < ActiveSupport::TestCase
|
6
|
-
teardown do
|
7
|
-
Dir.glob(Rails.root.join('db/migrate/*.rb')).each do |f|
|
8
|
-
FileUtils.rm f unless File.basename(f).in? %w(20120428022519_create_authors.rb 20120428022535_create_books.rb)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
sub_test_case '.status' do
|
13
|
-
test 'when all migrations are up' do
|
14
|
-
assert_equal [{:status => 'up', :version => '20120428022519', :name => 'create_authors', :filename => '20120428022519_create_authors.rb'}, {:status => 'up', :version => '20120428022535', :name => 'create_books', :filename => '20120428022535_create_books.rb'}], Erd::Migrator.status
|
15
|
-
end
|
16
|
-
|
17
|
-
test 'when one is undone' do
|
18
|
-
FileUtils.touch Rails.root.join('db/migrate/20999999999999_create_foobars.rb')
|
19
|
-
|
20
|
-
assert_equal [{:status => 'up', :version => '20120428022519', :name => 'create_authors', :filename => '20120428022519_create_authors.rb'}, {:status => 'up', :version => '20120428022535', :name => 'create_books', :filename => '20120428022535_create_books.rb'}, {:status => 'down', :version => '20999999999999', :name => 'create_foobars', :filename => '20999999999999_create_foobars.rb'}], Erd::Migrator.status
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
sub_test_case '.run_migrations' do
|
25
|
-
setup do
|
26
|
-
File.open(Rails.root.join('db/migrate/20999999999999_create_foobars.rb'), 'w') do |f|
|
27
|
-
f.puts 'class CreateFoobars < ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Migration[5.0] : ActiveRecord::Migration; end'
|
28
|
-
end
|
29
|
-
|
30
|
-
if defined? ActiveRecord::MigrationContext # >= 5.2
|
31
|
-
mock.instance_of(ActiveRecord::MigrationContext).run(:up, 20999999999999)
|
32
|
-
else
|
33
|
-
mock(ActiveRecord::Migrator).run(:up, ['db/migrate'], 20999999999999)
|
34
|
-
end
|
35
|
-
mock(ActiveRecord::SchemaDumper).dump(ActiveRecord::Base.connection, anything)
|
36
|
-
end
|
37
|
-
test 'runs migration by version number' do
|
38
|
-
Erd::Migrator.run_migrations(:up => ['20999999999999'])
|
39
|
-
end
|
40
|
-
test 'runs migration by migration filename' do
|
41
|
-
Erd::Migrator.run_migrations(:up => [Rails.root.join('db/migrate/20999999999999_create_foobars.rb')])
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class GenaratorRunnerTest < ActiveSupport::TestCase
|
47
|
-
setup do
|
48
|
-
stub.proxy(Time).now {|t| stub(t).utc { Time.parse '2012/5/12 13:26' } }
|
49
|
-
end
|
50
|
-
teardown do
|
51
|
-
Dir.glob(Rails.root.join('db/migrate/*.rb')).each do |f|
|
52
|
-
FileUtils.rm f unless File.basename(f).in? %w(20120428022519_create_authors.rb 20120428022535_create_books.rb)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
test '.execute_generate_migration' do
|
57
|
-
assert_includes Erd::GenaratorRunner.execute_generate_migration('create_foobars'), 'db/migrate/20120512132600_create_foobars.rb'
|
58
|
-
end
|
59
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
-
|
6
|
-
ENV['RAILS_ENV'] = 'development'
|
7
|
-
|
8
|
-
# load Rails first
|
9
|
-
require 'rails'
|
10
|
-
require 'action_controller/railtie'
|
11
|
-
require 'active_record/railtie'
|
12
|
-
require 'erd'
|
13
|
-
require 'fake_app/fake_app'
|
14
|
-
require 'test/unit/rails/test_help'
|
15
|
-
Bundler.require
|
16
|
-
require 'capybara'
|
17
|
-
require 'selenium/webdriver'
|
18
|
-
|
19
|
-
begin
|
20
|
-
require "action_dispatch/system_test_case"
|
21
|
-
rescue LoadError
|
22
|
-
Capybara.register_driver :chrome do |app|
|
23
|
-
options = Selenium::WebDriver::Chrome::Options.new(:args => %w[no-sandbox headless disable-gpu])
|
24
|
-
Capybara::Selenium::Driver.new(app, :browser => :chrome, :options => options)
|
25
|
-
end
|
26
|
-
Capybara.javascript_driver = :chrome
|
27
|
-
else
|
28
|
-
ActionDispatch::SystemTestCase.driven_by(:selenium, :using => :headless_chrome)
|
29
|
-
end
|
30
|
-
|
31
|
-
ActiveRecord::Migration.verbose = false
|
32
|
-
if defined? ActiveRecord::MigrationContext # >= 5.2
|
33
|
-
ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
|
34
|
-
ActiveRecord::Base.connection.migration_context.migrate
|
35
|
-
else
|
36
|
-
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths.map {|p| Rails.root.join p}, nil)
|
37
|
-
end
|