ar_diagram 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +47 -0
  3. data/Rakefile +25 -0
  4. data/app/assets/javascripts/ar_diagram/application.js +16 -0
  5. data/app/assets/javascripts/ar_diagram/ar_diagram.coffee +299 -0
  6. data/app/assets/stylesheets/ar_diagram/application.css +13 -0
  7. data/app/assets/stylesheets/ar_diagram/ar_diagram.scss +81 -0
  8. data/app/controllers/ar_diagram/application_controller.rb +4 -0
  9. data/app/controllers/ar_diagram/ar_diagram_controller.rb +103 -0
  10. data/app/helpers/ar_diagram/application_helper.rb +4 -0
  11. data/app/helpers/ar_diagram/ar_diagram_helper.rb +35 -0
  12. data/app/models/ar_diagram/base.rb +9 -0
  13. data/app/models/ar_diagram/field.rb +13 -0
  14. data/app/models/ar_diagram/label.rb +5 -0
  15. data/app/models/ar_diagram/table.rb +40 -0
  16. data/app/models/ar_diagram/view.rb +19 -0
  17. data/app/views/ar_diagram/ar_diagram/_diagram.html.erb +7 -0
  18. data/app/views/ar_diagram/ar_diagram/_table.html.erb +14 -0
  19. data/app/views/ar_diagram/ar_diagram/_tables.html.erb +4 -0
  20. data/app/views/ar_diagram/ar_diagram/_views.html.erb +5 -0
  21. data/app/views/ar_diagram/ar_diagram/add_table.js.erb +4 -0
  22. data/app/views/ar_diagram/ar_diagram/index.html.erb +0 -0
  23. data/app/views/ar_diagram/ar_diagram/show.html.erb +1 -0
  24. data/app/views/ar_diagram/ar_diagram/test.html.erb +1 -0
  25. data/app/views/ar_diagram/ar_diagram/update.js.erb +5 -0
  26. data/app/views/layouts/ar_diagram/application.html.erb +13 -0
  27. data/app/views/layouts/ar_diagram/ar_diagram.html.erb +65 -0
  28. data/app/views/layouts/ar_diagram/ar_diagram_print.html.erb +5 -0
  29. data/config/routes.rb +7 -0
  30. data/db/migrate/ar_diagram/20111114033632_create_ar_diagram_tables.rb +18 -0
  31. data/db/migrate/ar_diagram/20111114033653_create_ar_diagram_labels.rb +8 -0
  32. data/db/migrate/ar_diagram/20111114033826_create_ar_diagram_fields.rb +13 -0
  33. data/db/migrate/ar_diagram/20111114034012_create_ar_diagram_views.rb +10 -0
  34. data/lib/ar_diagram.rb +4 -0
  35. data/lib/ar_diagram/engine.rb +5 -0
  36. data/lib/ar_diagram/version.rb +3 -0
  37. data/lib/tasks/ar_diagram_tasks.rake +16 -0
  38. metadata +201 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,47 @@
1
+ = ArDiagram
2
+
3
+ == Heads Up
4
+
5
+ This is pretty much an alpha release. Although I have been using this code for quite a while now on many projects, it hasn't been tested on a wide range of environment.
6
+
7
+ I know the line drawing isn't as nice as it could be, it would be nice to have lines that turned corners and were more beautiful, but it's still useful the way it is.
8
+
9
+ Also it doesn't detect polymorphic associations either, something I'd like to add in at some point.
10
+
11
+ == Description
12
+
13
+ ArDiagram was created as I like to be able to see visualisations of my database. This project allows you to create live database diagrams, kind of like Visio.
14
+
15
+ Traditionally diagrams are difficult to keep up to date, because changes are invariably made to the database. ArDiagram is a mountable engine that will always have a live view of your ActiveRecord structure.
16
+
17
+ Because some databases are rather big, ArDiagram allows you to manage multiple views and place tables on those views. Relationships are automatically detected and relationship lines are drawn between the key fields.
18
+
19
+ The gem should only be used in development mode so that people can't hit up your production server for a nice diagram of your data.
20
+
21
+ This gem stores all of it's data in an sqlite databse, separate from your main app.
22
+
23
+ == Installation
24
+
25
+ It's a gem. Add the following to your Gemfile.
26
+
27
+ gem 'ar_record', :group => :development
28
+
29
+ Don't forget to run bundle afterwards.
30
+
31
+ Add the following to your routes. Notice this has no effect if the engine is not defined (which it won't be if you aren't running in the development environment)
32
+
33
+ mount ArDiagram::Engine => "/ar_diagram" if defined?(ArDiagram::Engine)
34
+
35
+ Run the migration to create the db/ar_diagram.sqlite3 database
36
+
37
+ rake ar_diagram:migrate
38
+
39
+ You should be ready to go at this point, navigate to where you have your server running eg:
40
+
41
+ http://localhost:3000/ar_diagram
42
+
43
+ and you should be ready to go...
44
+
45
+ == License
46
+
47
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ArDiagram'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ task :default => :build
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require twitter/bootstrap
16
+ //= require_tree .
@@ -0,0 +1,299 @@
1
+ jQuery.fn.exists = ->
2
+ return this.length > 0
3
+
4
+ # Utility Methods
5
+
6
+ getTargetURL = ->
7
+ $('#ar_diagram').data('target')
8
+
9
+ remoteScript = (path, successCallback, failCallback) ->
10
+ requestURL = getTargetURL() + "/" + path
11
+ $.ajax requestURL,
12
+ type: 'GET'
13
+ dataType: 'script'
14
+ error: (jqXHR, textStatus, errorThrown) ->
15
+ if failCallback
16
+ failCallback()
17
+ else
18
+ alert("AJAX Error in Remote Script #{path}: #{textStatus}")
19
+ success: (data, textStatus, jqXHR) ->
20
+ if successCallback
21
+ successCallback()
22
+
23
+ @viewUnsaved = (v) ->
24
+ if (typeof v == 'undefined')
25
+ result = $('#ar_diagram').data("view-unsaved")
26
+ return false unless result
27
+ return true
28
+ if(v)
29
+ $('#ar_diagram').data("view-unsaved", true)
30
+ else
31
+ $('#ar_diagram').removeData("view-unsaved")
32
+
33
+ return v
34
+
35
+ @zoom = (v) ->
36
+ unless v
37
+ result = $('#ar_diagram').data("zoom")
38
+ return 1 unless result
39
+ return result
40
+ $('#ar_diagram').data("zoom", v)
41
+ return v
42
+
43
+ selectedTable = (table) ->
44
+ unless table
45
+ return $(".diagram_table.selected").first
46
+ else
47
+ @selectedTable = table.attr("id")
48
+ $(".diagram_table").removeClass("selected")
49
+ table.addClass("selected")
50
+ return table
51
+
52
+ tableMouseDown = (event, table) ->
53
+ selectedTable(table)
54
+ table.addClass("drag")
55
+ position = table.position()
56
+ @offsetX = parseInt(event.pageX) - position.left
57
+ @offsetY = parseInt(event.pageY) - position.top
58
+ event.preventDefault()
59
+
60
+ tableMouseMove = (event) ->
61
+ return if !@selectedTable
62
+ table = $('#' + @selectedTable)
63
+ if table.hasClass("drag")
64
+ gridSize = 1
65
+ z = zoom()
66
+ x = Math.round(parseInt(event.pageX) / gridSize) * gridSize
67
+ y = Math.round(parseInt(event.pageY) / gridSize) * gridSize
68
+ table.data("x", (x - @offsetX) / z)
69
+ table.data("y", (y - @offsetY) / z)
70
+ viewUnsaved(true)
71
+ updateView()
72
+
73
+ @makeDraggable = (table) ->
74
+ table.mousedown((event) ->
75
+ tableMouseDown(event, table)
76
+ )
77
+
78
+ setCanvasSize = ->
79
+ canvas = $('#canvas')
80
+ unless canvas.exists()
81
+ canvas = $('<canvas/>', {'id': 'canvas'})
82
+ $('#content').prepend(canvas)
83
+
84
+ canvas_width = 0
85
+ canvas_height = 0
86
+
87
+ $('.diagram_table').each((index, table) ->
88
+ t = $(table)
89
+ width = parseInt(t.width() + t.position().left, 10)
90
+ canvas_width = width if (width > canvas_width)
91
+ height = parseInt(t.height() + t.position().top, 10)
92
+ canvas_height = height if (height > canvas_height)
93
+ )
94
+ canvas_width += 50
95
+ canvas_height += 50
96
+
97
+ canvas.width(canvas_width)
98
+ canvas.height(canvas_height)
99
+ canvas.attr("width", canvas_width)
100
+ canvas.attr("height", canvas_height)
101
+
102
+ drawRelationshipLine = (field, target, type) ->
103
+ target_field = $('#' + target)
104
+ return unless target_field.exists()
105
+
106
+ table = field.parents("table").first()
107
+ target_table = target_field.parents("table").first()
108
+
109
+ canvas = $('#canvas')
110
+ ctx = canvas[0].getContext("2d")
111
+
112
+ field.addClass("join")
113
+ target_field.addClass("join")
114
+
115
+ ctx.strokeStyle = "gray"
116
+ ctx.fillStyle = "gray"
117
+
118
+ if field.position() && target_field.position()
119
+ x1 = table.position().left
120
+ y1 = table.position().top + field.position().top + (field.height() / 2)
121
+ x2 = target_table.position().left
122
+ y2 = target_table.position().top + target_field.position().top + (target_field.height() / 2)
123
+
124
+ textY = y1 - 1
125
+
126
+ if (x2 > x1)
127
+ x1 += field.width()
128
+ textX = x1 + 20
129
+ textAlign = "left"
130
+ else
131
+ x2 += target_field.width() + 10
132
+ textX = x1 - 10
133
+ textAlign = "right"
134
+
135
+ unless (type == "belongs_to")
136
+ # Draw the main line
137
+
138
+ ctx.beginPath()
139
+ if (x2 > x1)
140
+ ctx.moveTo(x1, y1)
141
+ ctx.lineTo(x1 + 20, y1)
142
+ ctx.lineTo(x2 - 20, y2)
143
+ else
144
+ ctx.moveTo(x1, y1)
145
+ ctx.lineTo(x1 - 20, y1)
146
+ ctx.lineTo(x2 + 20, y2)
147
+ ctx.lineTo(x2, y2)
148
+ ctx.stroke()
149
+
150
+ # Draw the arrow
151
+ ctx.textBaseline = "bottom"
152
+ ctx.beginPath()
153
+ if (x2 > x1)
154
+ ctx.lineTo(x2 - 8, y2 - 8)
155
+ ctx.lineTo(x2 - 8, y2 + 8)
156
+ else
157
+ ctx.lineTo(x2 + 8, y2 - 8)
158
+ ctx.lineTo(x2 + 8, y2 + 8)
159
+
160
+ ctx.lineTo(x2, y2)
161
+ ctx.fill()
162
+
163
+ # Draw the text
164
+ ctx.fillStyle = "blue"
165
+ ctx.textAlign = textAlign
166
+ ctx.fillText(type, textX, textY)
167
+
168
+ drawRelationships = (table) ->
169
+ t = $(table)
170
+ t.find('tr').each((index, row) ->
171
+ r = $(row)
172
+ hasMany = r.data("has-many")
173
+ hasOne = r.data("has-one")
174
+ belongsTo = r.data("belongs-to")
175
+ if hasMany
176
+ relationships = hasMany.split(",")
177
+ $.each(relationships, (index, relationship) ->
178
+ drawRelationshipLine(r, relationship, "has_many")
179
+ )
180
+ if hasOne
181
+ relationships = hasOne.split(",")
182
+ $.each(relationships, (index, relationship) ->
183
+ drawRelationshipLine(r, relationship, "has_one")
184
+ )
185
+ if belongsTo
186
+ relationships = belongsTo.split(",")
187
+ $.each(relationships, (index, relationship) ->
188
+ drawRelationshipLine(r, relationship, "belongs_to")
189
+ )
190
+ )
191
+
192
+ @saveView = ->
193
+ p = new Object()
194
+ p["tables"] = new Object()
195
+
196
+ $(".diagram_table").each((index, table) ->
197
+ t = $(table)
198
+ table_id = t.attr("id")
199
+ pTable = new Object()
200
+ pTable["name"] = table_id
201
+ pTable["x"] = t.data("x")
202
+ pTable["y"] = t.data("y")
203
+ pTable["fields"] = new Object()
204
+ t.find('tr').each((index, field) ->
205
+ f = $(field)
206
+ field_id = f.attr("id")
207
+ pField = new Object()
208
+ pField["name"] = field_id
209
+ pTable["fields"][field_id] = pField
210
+ )
211
+ p["tables"][table_id] = pTable
212
+ p["zoom"] = zoom()
213
+ )
214
+ requestURL = getTargetURL()
215
+ $.ajax requestURL,
216
+ type: 'PUT'
217
+ data: p
218
+ dataType: 'script'
219
+ error: (jqXHR, textStatus, errorThrown) ->
220
+ alert("AJAX Error while saving view: #{textStatus}")
221
+ success: (data, textStatus, jqXHR) ->
222
+
223
+ updateTableMenu = ->
224
+ $('.table-select').each((index, item) ->
225
+ t = $(item)
226
+ tableName = t.data("table-name")
227
+ table = $("##{tableName}")
228
+ t.off("click")
229
+ if table.exists()
230
+ t.find("i").attr("class", "icon-ok").attr("style", "color:green;")
231
+ t.click(->
232
+ table.remove()
233
+ viewUnsaved(true)
234
+ updateView()
235
+ )
236
+ else
237
+ t.find("i").attr("class", "icon-plus").attr("style", "color:white;")
238
+ t.click(->
239
+ requestURL = getTargetURL() + "/add_table"
240
+ $.ajax requestURL,
241
+ type: 'POST'
242
+ dataType: 'script'
243
+ data:
244
+ {
245
+ model: tableName
246
+ }
247
+ error: (jqXHR, textStatus, errorThrown) ->
248
+ if failCallback
249
+ failCallback()
250
+ else
251
+ alert("AJAX Error in Remote Script #{path}: #{textStatus}")
252
+ success: (data, textStatus, jqXHR) ->
253
+ if successCallback
254
+ successCallback()
255
+ )
256
+ )
257
+
258
+ @updateView = ->
259
+ z = zoom()
260
+ $('#content').css("font-size", (12 * z) + "px")
261
+ $(".diagram_table").each((index, table) ->
262
+ t = $(table)
263
+ t.css("left", (t.data("x") * z) + "px")
264
+ t.css("top", (t.data("y") * z) + "px")
265
+ )
266
+ setCanvasSize()
267
+ $(".diagram_table").each((index, table) ->
268
+ drawRelationships(table)
269
+ )
270
+
271
+ if viewUnsaved()
272
+ $('#saveViewButton').show()
273
+ else
274
+ $('#saveViewButton').hide()
275
+
276
+ updateTableMenu()
277
+
278
+ $(document).ready ->
279
+ $('html').mousemove((event) ->
280
+ tableMouseMove(event)
281
+ )
282
+
283
+ $('html').mouseup((event) =>
284
+ $(".diagram_table").removeClass("drag")
285
+ )
286
+ $('.diagram_table').each(->
287
+ makeDraggable($(this))
288
+ )
289
+ $('#zoom-in-button').click(=>
290
+ zoom(zoom() + 0.1)
291
+ updateView()
292
+ )
293
+
294
+ $('#zoom-out-button').click(=>
295
+ zoom(zoom() - 0.1)
296
+ updateView()
297
+ )
298
+
299
+ updateView()
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,81 @@
1
+ // Place all the styles related to the ar_diagram controller here.
2
+ // They will automatically be included in application.css.
3
+ // You can use Sass (SCSS) here: http://sass-lang.com/
4
+
5
+ @import "twitter/bootstrap-no-sprites";
6
+ @import 'font-awesome';
7
+
8
+ body {
9
+ padding-top: 50px;
10
+ font-family: Verdana;
11
+ }
12
+
13
+ @import "compass";
14
+
15
+ #tables-dropdown {
16
+ height: 400px;
17
+ overflow: scroll;
18
+ }
19
+
20
+ #content {
21
+ position: absolute;
22
+
23
+ font-family: "Verdana";
24
+ font-size: 12px;
25
+
26
+ #canvas {
27
+ position: absolute;
28
+ }
29
+ table.diagram_table {
30
+ background: white;
31
+ position: absolute;
32
+ border: 1px solid #DDD;
33
+ -moz-box-shadow: 5px 5px 15px #555555;
34
+ -webkit-box-shadow: 5px 5px 15px #555555;
35
+ box-shadow: 5px 5px 15px #555555;
36
+
37
+ th {
38
+ font-size: 110%;
39
+ color: white;
40
+ padding: 2px 12px;
41
+
42
+ background: #07d;
43
+ background: -webkit-gradient(linear, left top, left bottom, from(#028), to(#07d));
44
+ background: -webkit-linear-gradient(top, #028, #07d);
45
+ background: -moz-linear-gradient(top, #028, #07d);
46
+ background: -ms-linear-gradient(top, #028, #07d);
47
+ background: -o-linear-gradient(top, #028, #07d);
48
+ }
49
+ tr:nth-child(odd) {
50
+ background-color: #eee;
51
+ }
52
+ tr:nth-child(even) {
53
+ background-color: #fff;
54
+ }
55
+
56
+ td {
57
+ font-size: 100%;
58
+ }
59
+
60
+ tr.join {
61
+ td {
62
+ color: blue;
63
+ }
64
+ }
65
+
66
+ tr {
67
+ td.type {
68
+ color: #999;
69
+ text-align: right;
70
+ }
71
+ }
72
+ }
73
+
74
+ table.selected {
75
+ border: 2px solid #7cfc00;
76
+ }
77
+
78
+ table.drag {
79
+ opacity: 0.5;
80
+ }
81
+ }
@@ -0,0 +1,4 @@
1
+ module ArDiagram
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,103 @@
1
+ module ArDiagram
2
+ class ArDiagramController < ApplicationController
3
+ before_filter :set_module
4
+ helper_method :css_id
5
+
6
+ # Force load of all models
7
+ ActiveSupport::Dependencies.autoload_paths.each do |path|
8
+ if /\/models$/.match(path)
9
+ Dir.glob(File.join(path, "**", "*.rb")).each do |file|
10
+ require_dependency file
11
+ end
12
+ end
13
+ end
14
+
15
+ def index
16
+ @views = View.all
17
+ end
18
+
19
+ def show
20
+ @view = View.find(params[:id])
21
+ @views = View.all
22
+ @tables = available_tables @db_module
23
+
24
+ if params[:print]
25
+ render "show", :layout => "ar_diagram/ar_diagram_print"
26
+ else
27
+ render
28
+ end
29
+ end
30
+
31
+ def create
32
+ @view = View.new(params[:view])
33
+ @view.zoom = 1
34
+ @view.save
35
+ redirect_to(ar_diagram_path(@view.id))
36
+ end
37
+
38
+ def update
39
+ @view = View.find(params[:id])
40
+ @view.tables.update_all(:archive => true)
41
+ params[:tables].each do |name, table|
42
+ t = @view.tables.find_or_initialize_by_name(model_name(name))
43
+ t.archive = false
44
+ t.position_x = table["x"]
45
+ t.position_y = table["y"]
46
+ t.save
47
+ end
48
+ @view.zoom = params[:zoom]
49
+ @view.save
50
+ @success = true
51
+ end
52
+
53
+ def destroy
54
+ @view = View.find(params[:id])
55
+ @view.destroy
56
+ redirect_to ar_diagram_index_path
57
+ end
58
+
59
+ def add_table
60
+ model = params[:model].camelize
61
+ @table = Table.new({:name => model})
62
+ end
63
+
64
+ protected
65
+
66
+ def set_module
67
+ unless params[:set_module].nil?
68
+ @db_module = eval(params[:set_module])
69
+ session[:ar_diagram_module] = @db_module
70
+ else
71
+ @db_module = session[:ar_diagram_module] || Module
72
+ end
73
+ end
74
+
75
+ def available_tables(m)
76
+ prefix = ""
77
+ prefix = "#{m.name}::" unless m.name == "Module"
78
+ all_tables = [].tap do |array|
79
+ m.constants.select do |constant_name|
80
+ class_name = "#{prefix}#{constant_name.to_s}"
81
+ unless [:VERSION, :Base, :Engine, :View, :Label, :Table, :Field].include?(constant_name)
82
+ begin
83
+ constant = eval class_name
84
+ if not constant.nil? and constant.is_a? Class and constant.superclass == ActiveRecord::Base
85
+ array << class_name
86
+ end
87
+ rescue
88
+ end
89
+ end
90
+ end
91
+ end
92
+ (all_tables).sort
93
+ end
94
+
95
+ def css_id table_name
96
+ table_name.underscore.gsub(/\//, "-")
97
+ end
98
+
99
+ def model_name css_id
100
+ css_id.gsub(/\-/, "/").camelize
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,4 @@
1
+ module ArDiagram
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,35 @@
1
+ module ArDiagram
2
+ module ArDiagramHelper
3
+ def field_relationships(table, field)
4
+ relationship = {
5
+ :belongs_to => [],
6
+ :has_many => [],
7
+ :has_one => [],
8
+ }
9
+ relationship[:belongs_to] = []
10
+ relationship[:has_many] = []
11
+ relationship[:has_one] = []
12
+
13
+ prefix_match = /(^.*)\:\:/.match(table.name)
14
+ prefix = prefix_match.nil? ? "" : "#{prefix_match[1].underscore}-"
15
+ table_name_match = /.*\/(.*)/.match(table.name.underscore)
16
+ table_name = table_name_match.nil? ? table.name.underscore : table_name_match[1]
17
+
18
+ table.field_relationships.each_pair do |f, r|
19
+ unless r.options[:through]
20
+ case r.macro
21
+ when :belongs_to
22
+ relationship[r.macro] << "#{prefix}#{css_id(f.to_s.pluralize)}__id" if ("#{r.name}_id" == field)
23
+ when :has_many, :has_one
24
+ relationship[r.macro] << "#{prefix}#{css_id(f.to_s)}__#{table_name}_id" if (field == "id")
25
+ end
26
+ end
27
+ end
28
+ [].tap do |result|
29
+ result << "data-belongs-to=#{relationship[:belongs_to].join(",")}" if relationship[:belongs_to].length > 0
30
+ result << "data-has-many=#{relationship[:has_many].join(",")}" if relationship[:has_many].length > 0
31
+ result << "data-has-one=#{relationship[:has_one].join(",")}" if relationship[:has_one].length > 0
32
+ end.join(" ")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ module ArDiagram
2
+ class Base < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ establish_connection(
5
+ :adapter => "sqlite3",
6
+ :database => "db/ar_diagram.sqlite3"
7
+ )
8
+ end
9
+ end
@@ -0,0 +1,13 @@
1
+ module ArDiagram
2
+ class Field < ArDiagram::Base
3
+ belongs_to :table
4
+
5
+ attr_accessible :name
6
+
7
+ validates_uniqueness_of :name, :scope => :table_id
8
+
9
+ def relationship
10
+ return self.dbd_table.field_relationship self.name
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module ArDiagram
2
+ class Label < ArDiagram::Base
3
+ has_many :tables, :dependent => :nullify
4
+ end
5
+ end
@@ -0,0 +1,40 @@
1
+ module ArDiagram
2
+ class Table < ArDiagram::Base
3
+ belongs_to :label
4
+ belongs_to :view
5
+ has_many :fields, :dependent => :delete_all
6
+
7
+ validates_uniqueness_of :name, :scope => :view_id
8
+
9
+ attr_accessible :name
10
+ attr_reader :field_relationships
11
+
12
+ def add_fields fields = nil
13
+ add_fields = fields || self.model.attribute_names
14
+ add_fields.each do |field|
15
+ self.dbd_fields.create({:name => field})
16
+ end
17
+ end
18
+
19
+ def model
20
+ begin
21
+ return Object.const_get self.name
22
+ rescue
23
+ return nil
24
+ end
25
+ end
26
+
27
+ def field_relationships
28
+ return @field_relationships unless @field_relationships.nil?
29
+ @field_relationships = {}
30
+ self.model.reflect_on_all_associations.each do |relationship|
31
+ @field_relationships[relationship.name] = relationship
32
+ end
33
+ @field_relationships
34
+ end
35
+
36
+ def field_relationship field
37
+ return field_relationships[field]
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,19 @@
1
+ module ArDiagram
2
+ class View < ArDiagram::Base
3
+ attr_accessible :name
4
+
5
+ has_many :tables, :dependent => :destroy
6
+ has_many :visible_tables, :class_name => "Table", :conditions => { :archive => false }
7
+
8
+ validates_presence_of :name
9
+ validates_uniqueness_of :name
10
+
11
+ def to_param
12
+ "#{id}-#{name.downcase.strip.gsub(/[^a-z0-9\- ]/i, '').gsub(/[ \-]+/, '-')}"
13
+ end
14
+
15
+ def new_table model
16
+ self.dbd_tables.create({:name => model})
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ <div id="ar_diagram" data-target="<%= request.path %>" data-selected-view="<%= @view.id %>">
2
+ <div id="content">
3
+ <% @view.visible_tables.each do |table| %>
4
+ <%= render :partial => "table", :locals => {:table => table}, :formats => [:html] %>
5
+ <% end %>
6
+ </div>
7
+ </div>
@@ -0,0 +1,14 @@
1
+ <% unless table.model.nil? %>
2
+ <table id="<%= css_id table.name %>" class="diagram_table" data-x="<%= table.position_x %>" data-y="<%= table.position_y %>">
3
+ <tr>
4
+ <th colspan=2><%= table.name.camelize %></th>
5
+ </tr>
6
+ <%# table.model.attribute_names.each do |field| %>
7
+ <% table.model.columns_hash.each do |name, attributes| %>
8
+ <tr id="<%= css_id table.name.pluralize %>__<%= name %>" <%= field_relationships(table, name) %>>
9
+ <td><%= name %></td>
10
+ <td class="type"><%= attributes.type %></td>
11
+ </tr>
12
+ <% end %>
13
+ </table>
14
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <% @tables.each do |table| %>
2
+ <div class="table_select"><%= link_to table, add_table_path(table), :class => "select", :remote => true %></div>
3
+ <% end %>
4
+
@@ -0,0 +1,5 @@
1
+ <% @views.each do |view| %>
2
+ <%= tb_nav_item tb_link(view.name, show_view_path(view), :remote => true) %>
3
+ <div class="view_select"><%= link_to view.name, show_view_path(view), :class => "select", :remote => true %></div>
4
+ <% end %>
5
+
@@ -0,0 +1,4 @@
1
+ $('#content').append("<%= escape_javascript render :partial => "table", :locals => {:table => @table} %>");
2
+ makeDraggable($('#<%= css_id @table.name %>'));
3
+ viewUnsaved(true)
4
+ updateView();
File without changes
@@ -0,0 +1 @@
1
+ <%= render :partial => "diagram", :formats => [:html] %>
@@ -0,0 +1 @@
1
+ <%= render :partial => "diagram", :formats => [:html] %>
@@ -0,0 +1,5 @@
1
+ <% if @success %>
2
+ viewUnsaved(false);
3
+ updateView();
4
+ <% else %>
5
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>ArDiagram</title>
5
+ <%= stylesheet_link_tag "ar_diagram/application", :media => "all" %>
6
+ <%= javascript_include_tag "ar_diagram/application" %>
7
+ <%= yield :application_head %>
8
+ <%= csrf_meta_tags %>
9
+ </head>
10
+ <body>
11
+ <%= content_for?(:application_content) ? yield(:application_content) : yield %>
12
+ </body>
13
+ </html>
@@ -0,0 +1,65 @@
1
+ <% content_for :application_content do %>
2
+ <div class="container-fluid">
3
+ <div class="navbar navbar-fixed-top">
4
+ <div class="navbar-inner">
5
+ <div class="container">
6
+ <span id="brand">
7
+ <%= link_to "ArDiagram", ar_diagram.ar_diagram_index_path, :class => "brand" %>
8
+ </span>
9
+ <%= tb_nav do %>
10
+ <%= tb_nav_item(:dropdown => (@view ? @view.name : "Views"), :html => {:id => "views-button"}) do %>
11
+ <%= tb_nav(:dropdown_menu, :html => {:id => "views-dropdown"}) do %>
12
+ <% @views.each do |view| %>
13
+ <%= tb_nav_item tb_link(view.name, ar_diagram_path(view)) %>
14
+ <% end %>
15
+ <%= tb_nav_item :divider %>
16
+ <%= tb_nav_item tb_modal_button(" Add View", "addViewModal", :icon => "icon-plus") %>
17
+ <% end %>
18
+ <% end %>
19
+ <% if @tables %>
20
+ <%= tb_nav_item(:dropdown => "Tables", :html => {:id => "tables-button"}) do %>
21
+ <%= tb_nav(:dropdown_menu, :html => {:id => "tables-dropdown"}) do %>
22
+ <% @tables.each do |table| %>
23
+ <%= tb_nav_item tb_link(" #{table}", "#", :class => "table-select", :icon => "icon-minus", "data-table-name" => css_id(table)) %>
24
+ <% end %>
25
+ <% end %>
26
+ <% end %>
27
+ <% end %>
28
+ <% if @view %>
29
+ <span class="btn-group pull-left">
30
+ <%= tb_link("", "#", :icon => "icon-minus", :class => "btn", :id => "zoom-out-button") %>
31
+ <%= tb_link("", "#", :icon => "icon-plus", :class => "btn", :id => "zoom-in-button") %>
32
+ </span>
33
+ <span class="btn-group pull-left">
34
+ <%= tb_nav_item tb_link("Save", "javascript:saveView();", :id => "saveViewButton", :class => "btn btn-primary") %>
35
+ </span>
36
+ <span class="btn-group pull-left">
37
+ <%= tb_link("", ar_diagram_path(@view.id, :print => true), :icon => "icon-print", :class => "btn btn", :id => "print-button") %>
38
+ </span>
39
+ <span class="btn-group pull-left">
40
+ <%= tb_link("", ar_diagram_path(@view.id), :icon => "icon-trash", :class => "btn btn-danger", :id => "delete-view-button", :method => :delete, :confirm => "Are you sure you want to permanently delete this view?") %>
41
+ </span>
42
+ <% end %>
43
+ <% end %>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <div class="row-fluid">
48
+ <div id="main" class="span12">
49
+ <div id="flash_messages">
50
+ <% unless @no_flash_messages_in_layout %>
51
+ <%#= flash_messages %>
52
+ <% end %>
53
+ </div>
54
+ <%= content_for?(:page_content) ? yield(:page_content) : yield %>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ <%= tb_modal "addViewModal", :title => "Add View", :ok_id => "add-view-button", :ok_label => "Add View", :ok_link => "javascript:$('#new_view').submit()", :ok_class => "btn btn-success" do %>
59
+ <%= simple_form_for ArDiagram::View.new, :url => url_for(:action => :create) do |f| %>
60
+ <%= f.input :name %>
61
+ <% end %>
62
+ <% end %>
63
+ <% end %>
64
+
65
+ <%= render :file => 'layouts/ar_diagram/application' %>
@@ -0,0 +1,5 @@
1
+ <% content_for :application_content do %>
2
+ <%= yield %>
3
+ <% end %>
4
+
5
+ <%= render :file => 'layouts/ar_diagram/application' %>
data/config/routes.rb ADDED
@@ -0,0 +1,7 @@
1
+ ArDiagram::Engine.routes.draw do
2
+ resources :ar_diagram, :path => "", :path_names => { :new => "" } do
3
+ member do
4
+ post :add_table, :format => :js, :as => "add_table"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ class CreateArDiagramTables < ActiveRecord::Migration
2
+ def change
3
+ create_table :ar_diagram_tables do |t|
4
+ t.string :name
5
+ t.string :display_name
6
+ t.boolean :archive
7
+ t.text :description
8
+ t.references :label
9
+ t.references :view
10
+
11
+ t.integer :position_x
12
+ t.integer :position_y
13
+
14
+ t.timestamps
15
+ end
16
+ add_index :ar_diagram_tables, :label_id
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ class CreateArDiagramLabels < ActiveRecord::Migration
2
+ def change
3
+ create_table :ar_diagram_labels do |t|
4
+ t.string :name
5
+ t.string :color
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,13 @@
1
+ class CreateArDiagramFields < ActiveRecord::Migration
2
+ def change
3
+ create_table :ar_diagram_fields do |t|
4
+ t.string :name
5
+ t.string :display_name
6
+ t.string :description
7
+ t.integer :relationship
8
+ t.references :table
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ class CreateArDiagramViews < ActiveRecord::Migration
2
+ def change
3
+ create_table :ar_diagram_views do |t|
4
+ t.string :name
5
+ t.float :zoom
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
data/lib/ar_diagram.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "ar_diagram/engine"
2
+
3
+ module ArDiagram
4
+ end
@@ -0,0 +1,5 @@
1
+ module ArDiagram
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ArDiagram
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ module ArDiagram
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ namespace :ar_diagram do
2
+ desc 'Migrates the db_diagram database'
3
+ task :migrate => :environment do
4
+ config = {
5
+ :adapter => "sqlite3",
6
+ :database => "db/ar_diagram.sqlite3"
7
+ }
8
+ @migration_path = File.expand_path("../../../db/migrate/ar_diagram", __FILE__)
9
+ puts "Migrating from directory: #{@migration_path}"
10
+ ActiveRecord::Base.establish_connection config
11
+ ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
12
+ ActiveRecord::Migrator.migrate(@migration_path, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration|
13
+ ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ar_diagram
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Monagle
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.2'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.2'
30
+ - !ruby/object:Gem::Dependency
31
+ name: jquery-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: bootstrap-sass-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: font-awesome-sass-rails
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: twitter_bootstrap_helper
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: compass-rails
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: sqlite3
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ description: Allows you to create graphical database diagrams of your ActiveRecord
127
+ models from within your rails app.
128
+ email:
129
+ - david.monagle@intrica.com.au
130
+ executables: []
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - app/assets/javascripts/ar_diagram/application.js
135
+ - app/assets/javascripts/ar_diagram/ar_diagram.coffee
136
+ - app/assets/stylesheets/ar_diagram/application.css
137
+ - app/assets/stylesheets/ar_diagram/ar_diagram.scss
138
+ - app/controllers/ar_diagram/application_controller.rb
139
+ - app/controllers/ar_diagram/ar_diagram_controller.rb
140
+ - app/helpers/ar_diagram/application_helper.rb
141
+ - app/helpers/ar_diagram/ar_diagram_helper.rb
142
+ - app/models/ar_diagram/base.rb
143
+ - app/models/ar_diagram/field.rb
144
+ - app/models/ar_diagram/label.rb
145
+ - app/models/ar_diagram/table.rb
146
+ - app/models/ar_diagram/view.rb
147
+ - app/views/ar_diagram/ar_diagram/_diagram.html.erb
148
+ - app/views/ar_diagram/ar_diagram/_table.html.erb
149
+ - app/views/ar_diagram/ar_diagram/_tables.html.erb
150
+ - app/views/ar_diagram/ar_diagram/_views.html.erb
151
+ - app/views/ar_diagram/ar_diagram/add_table.js.erb
152
+ - app/views/ar_diagram/ar_diagram/index.html.erb
153
+ - app/views/ar_diagram/ar_diagram/show.html.erb
154
+ - app/views/ar_diagram/ar_diagram/test.html.erb
155
+ - app/views/ar_diagram/ar_diagram/update.js.erb
156
+ - app/views/layouts/ar_diagram/application.html.erb
157
+ - app/views/layouts/ar_diagram/ar_diagram.html.erb
158
+ - app/views/layouts/ar_diagram/ar_diagram_print.html.erb
159
+ - config/routes.rb
160
+ - db/migrate/ar_diagram/20111114033632_create_ar_diagram_tables.rb
161
+ - db/migrate/ar_diagram/20111114033653_create_ar_diagram_labels.rb
162
+ - db/migrate/ar_diagram/20111114033826_create_ar_diagram_fields.rb
163
+ - db/migrate/ar_diagram/20111114034012_create_ar_diagram_views.rb
164
+ - lib/ar_diagram/engine.rb
165
+ - lib/ar_diagram/version.rb
166
+ - lib/ar_diagram.rb
167
+ - lib/tasks/ar_diagram_tasks.rake
168
+ - MIT-LICENSE
169
+ - Rakefile
170
+ - README.rdoc
171
+ homepage: http://www.intrica.com.au
172
+ licenses: []
173
+ post_install_message:
174
+ rdoc_options: []
175
+ require_paths:
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ none: false
179
+ requirements:
180
+ - - ! '>='
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ segments:
184
+ - 0
185
+ hash: 2353600505694086881
186
+ required_rubygems_version: !ruby/object:Gem::Requirement
187
+ none: false
188
+ requirements:
189
+ - - ! '>='
190
+ - !ruby/object:Gem::Version
191
+ version: '0'
192
+ segments:
193
+ - 0
194
+ hash: 2353600505694086881
195
+ requirements: []
196
+ rubyforge_project:
197
+ rubygems_version: 1.8.24
198
+ signing_key:
199
+ specification_version: 3
200
+ summary: Database diagram tool for ActiveRecord on Rails.
201
+ test_files: []