erd 0.6.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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>