erd 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -4
  4. data/README.md +72 -0
  5. data/app/controllers/erd/erd_controller.rb +107 -65
  6. data/app/views/erd/erd/_column.html.erb +8 -4
  7. data/app/views/erd/erd/_model.html.erb +23 -17
  8. data/app/views/erd/erd/edit.html.erb +87 -0
  9. data/app/views/erd/erd/erd.html.erb +1 -1
  10. data/app/views/erd/erd/index.html.erb +12 -81
  11. data/app/views/layouts/erd/application.html.erb +3 -2
  12. data/config/routes.rb +1 -0
  13. data/erd.gemspec +1 -1
  14. data/gemfiles/rails_32.gemfile +1 -1
  15. data/gemfiles/rails_40.gemfile +1 -0
  16. data/gemfiles/rails_41.gemfile +1 -0
  17. data/gemfiles/rails_42.gemfile +1 -0
  18. data/gemfiles/rails_50.gemfile +1 -0
  19. data/gemfiles/rails_51.gemfile +1 -0
  20. data/gemfiles/rails_52.gemfile +1 -0
  21. data/gemfiles/rails_60.gemfile +9 -0
  22. data/gemfiles/rails_edge.gemfile +1 -0
  23. data/lib/erd.rb +7 -1
  24. data/lib/erd/engine.rb +12 -0
  25. data/lib/erd/migrator.rb +3 -1
  26. data/lib/erd/version.rb +1 -1
  27. data/{vendor/assets/images → public}/erd/angle-left.png +0 -0
  28. data/{vendor/assets/images → public}/erd/angle-right.png +0 -0
  29. data/{vendor/assets/images → public}/erd/animated-overlay.gif +0 -0
  30. data/{vendor/assets/images → public}/erd/background.png +0 -0
  31. data/{vendor/assets/images → public}/erd/cancel.png +0 -0
  32. data/{vendor/assets/images → public}/erd/close.png +0 -0
  33. data/public/erd/erd.css +598 -0
  34. data/{app/assets/javascripts/erd/erd.js.js → public/erd/erd.js} +13 -2
  35. data/{app → src}/assets/stylesheets/erd/erd.css.scss +1 -1
  36. data/test/test_helper.rb +2 -0
  37. metadata +21 -22
  38. data/README.rdoc +0 -66
  39. data/app/assets/javascripts/erd/application.js +0 -14
  40. data/app/assets/stylesheets/erd/application.css +0 -13
  41. data/lib/erd/railtie.rb +0 -21
  42. data/vendor/assets/javascripts/raphael-min.js +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7e01b6f95c48fb88666e202d05e7bf98108528302e77d898b2018f2d4e2605b
4
- data.tar.gz: d36ff0445b128f2a95ce9516c14dcba5774a05e9d44c758fa59ccd7f5e2d1adf
3
+ metadata.gz: 23d4342f2a320f75b2e90d11bf2474d5f1878632369e6c5a9e3608f3c9ac9aee
4
+ data.tar.gz: d7b53a5993a0b95508e44be666ec68f620563e4a9ee7bfb7ef708f09beb0ffda
5
5
  SHA512:
6
- metadata.gz: b872b5a9d45cef77e3c651e67577058b811a817ccd5229b92f2525b5a7a80ecfadbecf3a26e96ae4f6ad7582b9872288f6511c0a0dc136fc121b9faed894140f
7
- data.tar.gz: e36dec85dbaadedd20d2b91872a66b8e555f854f6412c3a4ae6f4a18eb935f1f8d7eff8d920153c9a114febe41a28bdb09de241b5f46a8f0b7aad1b5f2093e89
6
+ metadata.gz: 37a889901a16afac747792e929a71a8831ff55b0119cdb5cf96911fb19447a493a717021c7a65c1e7d3274809af67dd64c6a0184f79aebfa09716198186d38f8
7
+ data.tar.gz: c87a23f1c22cbff2363b8f54d993be86669330854fd80bd9570df87b3f144434c9ad55aacceacdf08f95b0fe60ff0cf67d4a145ee1918c22c1cba2b7b8b31b15
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .sass-cache
6
7
  Gemfile.lock
7
8
  gemfiles/*.lock
8
9
  InstalledFiles
@@ -14,7 +14,6 @@ cache:
14
14
 
15
15
  before_install:
16
16
  - sudo apt install -y chromium-chromedriver
17
- - sudo ln -s /usr/lib/chromium-browser/chromedriver /usr/bin/
18
17
  - gem i bundler -v '<2'
19
18
 
20
19
  rvm:
@@ -22,6 +21,7 @@ rvm:
22
21
  - 2.0.0
23
22
 
24
23
  gemfile:
24
+ - gemfiles/rails_60.gemfile
25
25
  - gemfiles/rails_52.gemfile
26
26
  - gemfiles/rails_51.gemfile
27
27
  - gemfiles/rails_50.gemfile
@@ -32,6 +32,8 @@ gemfile:
32
32
 
33
33
  matrix:
34
34
  exclude:
35
+ - rvm: 2.0.0
36
+ gemfile: gemfiles/rails_60.gemfile
35
37
  - rvm: 2.0.0
36
38
  gemfile: gemfiles/rails_52.gemfile
37
39
  - rvm: 2.0.0
@@ -46,6 +48,3 @@ matrix:
46
48
  gemfile: gemfiles/rails_40.gemfile
47
49
  - rvm: 2.6.0
48
50
  gemfile: gemfiles/rails_32.gemfile
49
- include:
50
- - rvm: 1.8.7
51
- gemfile: gemfiles/rails_32.gemfile
@@ -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 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.
@@ -1,70 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'nokogiri'
4
- require 'rails_erd/diagram/graphviz'
4
+ require 'ruby-graphviz'
5
5
  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
- Rails.application.eager_load!
11
- RailsERD.options[:filename], RailsERD.options[:filetype] = Rails.root.join('tmp/erd'), 'plain'
12
- RailsERD::Diagram::Graphviz.create
13
- plain = Rails.root.join('tmp/erd.plain').read
14
- positions = if (json = Rails.root.join('tmp/erd_positions.json')).exist?
15
- ActiveSupport::JSON.decode json.read
16
- else
17
- {}
18
- end
19
- @erd = render_plain plain, positions
13
+ @erd = render_plain generate_plain, saved_positions
14
+ end
20
15
 
16
+ def edit
17
+ @erd = render_plain generate_plain, saved_positions, true
21
18
  @migrations = Erd::Migrator.status
22
19
  end
23
20
 
24
21
  def update
25
- changes = ActiveSupport::JSON.decode(params[:changes])
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
+
29
+ changes = params[:changes].present? ? ActiveSupport::JSON.decode(params[:changes]) : []
26
30
  executed_migrations, failed_migrations = [], []
27
31
  changes.each do |row|
28
32
  begin
29
33
  action, model, column, from, to = row['action'], row['model'], row['column'], row['from'], row['to']
30
- if action == 'move'
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'
31
40
  model = model.tableize
32
- json_file = Rails.root.join('tmp', 'erd_positions.json')
33
- positions = json_file.exist? ? ActiveSupport::JSON.decode(json_file.read) : {}
34
- positions[model] = to
35
- json_file.open('w') {|f| f.write positions.to_json}
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'
48
+ model = model.tableize
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
36
62
  else
37
- case action
38
- when 'create_model'
39
- columns = column.split(' ').compact
40
- generated_migration_file = Erd::GenaratorRunner.execute_generate_model model, columns
41
- when 'remove_model'
42
- model = model.tableize
43
- generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "drop_#{model}"
44
- gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n drop_table :#{model}\n end"
45
- when 'rename_model'
46
- _model, from, to = from.tableize, to.tableize, model.tableize
47
- generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "rename_#{from}_to_#{to}"
48
- gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n rename_table :#{from}, :#{to}\n end"
49
- when 'add_column'
50
- model = model.tableize
51
- name_and_type = column.scan(/(.*)\((.*?)\)/).first
52
- name, type = name_and_type[0], name_and_type[1]
53
- generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "add_#{name}_to_#{model}", ["#{name}:#{type}"]
54
- when 'rename_column'
55
- model = model.tableize
56
- generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "rename_#{model}_#{from}_to_#{to}"
57
- gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n rename_column :#{model}, :#{from}, :#{to}\n end"
58
- when 'alter_column'
59
- model = model.tableize
60
- generated_migration_file = Erd::GenaratorRunner.execute_generate_migration "change_#{model}_#{column}_type_to_#{to}"
61
- gsub_file generated_migration_file, /def (up|change).* end/m, "def change\n change_column :#{model}, :#{column}, :#{to}\n end"
62
- else
63
- raise "unexpected action: #{action}"
64
- end
65
- Erd::Migrator.run_migrations :up => generated_migration_file
66
- executed_migrations << generated_migration_file
63
+ raise "unexpected action: #{action}"
67
64
  end
65
+
66
+ Erd::Migrator.run_migrations :up => generated_migration_file
67
+ executed_migrations << generated_migration_file
68
68
  rescue ::Erd::MigrationError => e
69
69
  failed_migrations << e.message
70
70
  end
@@ -79,28 +79,70 @@ module Erd
79
79
  end
80
80
 
81
81
  private
82
- def render_plain(plain, positions)
83
- _scale, svg_width, svg_height = plain.scan(/\Agraph ([0-9\.]+) ([0-9\.]+) ([0-9\.]+)$/).first
84
- # node name x y width height label style shape color fillcolor
85
- max_model_x, max_model_y = 0, 0
86
- models = plain.scan(/^node ([^ ]+) ([0-9\.]+) ([0-9\.]+) ([0-9\.]+) ([0-9\.]+) <\{?(<((?!^\}?>).)*)^\}?> [^ ]+ [^ ]+ [^ ]+ [^ ]+\n/m).map {|node_name, x, y, width, height, label|
87
- label_doc = Nokogiri::HTML::DocumentFragment.parse(label)
88
- model_name = node_name.dup
89
- model_name[0] = model_name[-1] = '' if (model_name.first == '"') && (model_name.last == '"')
90
- model_name = model_name.sub(/^m_/, '')
91
- next if model_name.in? ['ActiveRecord::SchemaMigration', 'ActiveRecord::InternalMetadata']
92
- columns = []
93
- if (cols_table = label_doc.search('table')[1])
94
- columns = cols_table.search('tr > td').map {|col| col_name, col_type = col.text.split(' '); {:name => col_name, :type => col_type}}
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
+
93
+ def generate_plain
94
+ if Rails.respond_to?(:autoloaders) && Rails.autoloaders.try(:zeitwerk_enabled?)
95
+ Zeitwerk::Loader.eager_load_all
96
+ else
97
+ Rails.application.eager_load!
98
+ end
99
+ ar_descendants = ActiveRecord::Base.descendants.reject {|m| m.name.in?(%w(ActiveRecord::SchemaMigration ActiveRecord::InternalMetadata ApplicationRecord)) }
100
+ ar_descendants.reject! {|m| !m.table_exists? }
101
+
102
+ g = GraphViz.new('ERD', :type => :digraph, :rankdir => 'LR', :labelloc => :t, :ranksep => '1.5', :nodesep => '1.8', :margin => '0,0', :splines => 'spline') {|g|
103
+ nodes = ar_descendants.each_with_object({}) do |model, hash|
104
+ next if model.name.start_with? 'HABTM_'
105
+ hash[model.name] = model.columns.reject {|c| c.name.in? %w(id created_at updated_at) }.map {|c| [c.name, c.type]}
106
+ end
107
+
108
+ edges = []
109
+ ar_descendants.each do |model|
110
+ model.reflect_on_all_associations.each do |reflection|
111
+ next unless nodes.key? model.name
112
+ next if reflection.macro == :belongs_to
113
+ next unless nodes.key?(reflection.klass.name)
114
+
115
+ edges << [model.name, reflection.klass.name]
116
+ # don't include the FKs in the diagram
117
+ nodes[reflection.klass.name].delete_if {|col, _type| col == reflection.foreign_key }
118
+ end
119
+ end
120
+
121
+ nodes.each_pair do |model_name, cols|
122
+ g.add_nodes model_name, 'shape' => 'record', 'label' => "#{model_name}|#{cols.map {|name, type| "#{name}(#{type})"}.join('\l')}"
95
123
  end
124
+ edges.each do |from, to|
125
+ g.add_edge g.search_node(from), g.search_node(to)
126
+ end
127
+ }
128
+ g.output('plain' => String)
129
+ end
130
+
131
+ def render_plain(plain, positions, edit_mode = false)
132
+ _scale, svg_width, svg_height = plain.scan(/\Agraph ([\d\.]+) ([\d\.]+) ([\d\.]+)$/).first
133
+ # node name x y width height label style shape color fillcolor
134
+ models = plain.scan(/^node ([^ ]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([\d\.]+) ([^ ]+) [^ ]+ [^ ]+ [^ ]+ [^ ]+\n/m).map {|model_name, x, y, width, height, label|
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} }
96
136
  custom_x, custom_y = positions[model_name.tableize].try(:split, ',')
97
- h = {:model => model_name, :x => (custom_x || (BigDecimal(x) * 72).round), :y => (custom_y || (BigDecimal(y) * 72).round), :width => (BigDecimal(width) * 72).round, :height => height, :columns => columns}
98
- 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
99
- 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}
100
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].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].min + 150, 768].max
101
143
  # edge tail head n x1 y1 .. xn yn [label xl yl] style color
102
- edges = plain.scan(/^edge ([^ ]+)+ ([^ ]+)/).map {|from, to| {:from => from.sub(/^m_/, ''), :to => to.sub(/^m_/, '')}}
103
- render_to_string 'erd/erd/erd', :layout => nil, :locals => {:width => [(BigDecimal(svg_width) * 72).round, max_model_x].max, :height => [(BigDecimal(svg_height) * 72).round, max_model_y].max, :models => models, :edges => edges}
144
+ edges = plain.scan(/^edge ([^ ]+)+ ([^ ]+)/).map {|from, to| {:from => from, :to => to}}
145
+ render_to_string 'erd/erd/erd', :layout => nil, :locals => {:width => erd_width, :height => erd_height, :models => models, :edges => edges, :edit_mode => edit_mode}
104
146
  end
105
147
 
106
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
- <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>
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].delete(" ∗") %>"><%= column[:type] %></span>
10
- <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>
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
- <a href="#!" class="close"><%= image_tag("/assets/erd/close.png") %></a>
3
- <div class="model_name">
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
- <form class="rename_model_form">
6
- <table>
7
- <tr>
8
- <td>
9
- <input name="model" type="hidden" value="<%= model[:model] %>"/>
10
- </td>
11
- <td>
12
- <input name="to" type="text" /><input type="submit" value="Change"/>
13
- </td>
14
- </tr>
15
- </table>
16
- <a href="#!" class="cancel">Cancel</a>
17
- </form>
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
- <a href="#!" class="add_column">add column</a>
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>