le1t0-dige 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
File without changes
data/bin/dige ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+
5
+ $diagram_types=[]
6
+ $project_type=:unknown
7
+
8
+ def action(diagram_type, project_type)
9
+ if $project_type == :unknown
10
+ "generate_#{diagram_type}"
11
+ else
12
+ "generate_#{project_type}_#{diagram_type}"
13
+ end
14
+ end
15
+
16
+ def env
17
+ ENV.select { |k,v| [ "NO_RM", "DEBUG", "FILE", "IGNORE_CLASSES" ].include?(k) }.collect do |k,v|
18
+ "#{k}=#{v}"
19
+ end.compact.join(' ')
20
+ end
21
+
22
+ def run(runner, call_style)
23
+ $diagram_types.each do |diagram_type|
24
+ cmd = case call_style
25
+ when :argument
26
+ "#{env} #{runner} \"require 'dige' ; Dige.#{action(diagram_type, $project_type)}\""
27
+ when :piped
28
+ "echo Dige.#{action(diagram_type, $project_type)} | #{env} #{runner}"
29
+ end
30
+ puts cmd if ENV['DEBUG'] and ENV['DEBUG'].upcase == 'TRUE'
31
+ raise unless IO.popen(cmd) do |p|
32
+ while !p.eof? && l = p.readline do
33
+ puts l
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ OptionParser.new do |opts|
40
+ opts.banner = "Usage: #{File.basename($0)} [path]"
41
+
42
+ opts.on("-t", "--type TYPE", String, "Explicitly set project type to one of <rails3, rails2, datamapper>") do |v|
43
+ $project_type=v.to_sym
44
+ end
45
+
46
+ opts.on("-c", "Generate UML Class Diagram") do
47
+ $diagram_types << :uml_class_diagram
48
+ end
49
+
50
+ opts.on("-e", "Generate Entity Relationship Diagram") do
51
+ $diagram_types << :entity_relationship_diagram
52
+ end
53
+
54
+ opts.on("-h", "--help", "Displays this help info") do
55
+ puts opts
56
+ exit 0
57
+ end
58
+
59
+ begin
60
+ opts.parse!(ARGV)
61
+ rescue OptionParser::ParseError => e
62
+ warn e.message
63
+ puts opts
64
+ exit 1
65
+ end
66
+ end
67
+
68
+ begin
69
+ unless [
70
+ [ [ File.join(File.expand_path('.'), "script", "rails"), "runner" ], :argument ], # rails 3
71
+ [ File.join(File.expand_path('.'), "script", "runner"), :argument ], # rails 2 and other projects which provide a runner
72
+ [ File.join(File.expand_path('.'), "script", "console"), :piped ] # non-rails, but something that has console
73
+ ].inject(false) do |has_run, runner|
74
+ if File.exists?([runner.first].flatten.first)
75
+ run([runner.first].flatten.join(' '), runner.last)
76
+ true
77
+ else
78
+ false
79
+ end
80
+ end
81
+ abort "Can't find anything (script/runner, script/console, ..) to run Dige in your project"
82
+ end
83
+ rescue
84
+ abort "Failed to generate diagram(s)"
85
+ end
@@ -0,0 +1,25 @@
1
+ class Dige::Association
2
+ include Dige::ClassBase
3
+ attr_accessor :from_class_name, :from_table_name, :to_class_name, :to_table_name, :from_arity, :to_arity, :type, :table_name, :macro
4
+
5
+ def debug_inspect
6
+ "#{from_class_name}(#{from_table_name}) #{from_arity} =#{table_name ? "[#{table_name}]" : ""}> #{to_arity} #{to_class_name}(#{to_table_name})"
7
+ end
8
+
9
+ def ==(objekt)
10
+ self.type == objekt.type && (
11
+ (
12
+ self.from_class_name == objekt.from_class_name &&
13
+ self.from_table_name == objekt.from_table_name &&
14
+ self.to_class_name == objekt.to_class_name &&
15
+ self.to_table_name == objekt.to_table_name
16
+ ) ||
17
+ (
18
+ self.from_class_name == objekt.to_class_name &&
19
+ self.from_table_name == objekt.to_table_name &&
20
+ self.to_class_name == objekt.from_class_name &&
21
+ self.to_table_name == objekt.from_table_name
22
+ )
23
+ )
24
+ end
25
+ end
@@ -0,0 +1,39 @@
1
+ class Dige::Associations
2
+ include Dige::List
3
+
4
+ def register(from_class_name, from_table_name, to_class_name, to_table_name, type, assoc_macro, table_name)
5
+ # assoc_macro can be : :has_many, :belongs_to, :has_one
6
+
7
+ tail_class_name = [ :belongs_to ].include?(assoc_macro) ? from_class_name : to_class_name
8
+ tail_table_name = [ :belongs_to ].include?(assoc_macro) ? from_table_name : to_table_name
9
+ head_class_name = [ :belongs_to ].include?(assoc_macro) ? to_class_name : from_class_name
10
+ head_table_name = [ :belongs_to ].include?(assoc_macro) ? to_table_name : from_table_name
11
+
12
+ objekt = Dige::Association.new({
13
+ :from_class_name => tail_class_name,
14
+ :from_table_name => tail_table_name,
15
+ :to_class_name => head_class_name,
16
+ :to_table_name => head_table_name,
17
+ :type => type,
18
+ :table_name => table_name
19
+ })
20
+ objekt.macro = assoc_macro if type == :polymorphic
21
+ unless return_objekt = find(objekt)
22
+ list << objekt
23
+ return_objekt = objekt
24
+ end
25
+ if assoc_macro == :has_many || assoc_macro == :has_and_belongs_to_many
26
+ return_objekt.from_arity = '0..*'
27
+ elsif assoc_macro == :has_one
28
+ return_objekt.from_arity = '1'
29
+ elsif assoc_macro == :belongs_to
30
+ return_objekt.to_arity = '1'
31
+ end
32
+ return_objekt
33
+ end
34
+
35
+ def all(type = :normal)
36
+ list.select { |list_item| type == :all || list_item.type == type }
37
+ end
38
+
39
+ end
@@ -0,0 +1,7 @@
1
+ module Dige::ClassBase
2
+ def initialize(attributes = {})
3
+ attributes.each do |key, value|
4
+ self.send "#{key}=".to_sym, value
5
+ end
6
+ end
7
+ end
data/lib/dige/dige.rb ADDED
@@ -0,0 +1,53 @@
1
+ class Dige
2
+
3
+ IGNORE_CLASSES = (ENV['IGNORE_CLASSES'] || "").split(/,/) + [ 'ActiveRecord::Base' ]
4
+
5
+ DIAGRAM_TYPES = {
6
+ :entity_relationship_diagram => "Dige::GraphvizDiagram::EntityRelationshipDiagram",
7
+ :uml_class_diagram => "Dige::GraphvizDiagram::UMLClassDiagram"
8
+ }
9
+
10
+ PROJECT_TYPES = {
11
+ :rails2 => "Rails2",
12
+ :rails3 => "Rails3",
13
+ :data_mapper => "DataMapper"
14
+ }
15
+
16
+ DIAGRAM_TYPES.each do |diagram_type, diagram_klass|
17
+ class_eval("
18
+ def self.generate_#{diagram_type}(project_type = :unknown) ; generate_diagram(#{diagram_klass}, project_type) ; end
19
+ ")
20
+ PROJECT_TYPES.each_key do |project_type|
21
+ class_eval("
22
+ def self.generate_#{project_type}_#{diagram_type} ; generate_#{diagram_type}(:#{project_type}) ; end
23
+ ")
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def self.induce_project_type
30
+ PROJECT_TYPES.values.collect { |v| Dige::Recognizer.const_get(v) }.inject(nil) do |recognized_type, klass|
31
+ recognized_type || klass.new.recognize
32
+ end || :unknown
33
+ end
34
+
35
+ def self.initialize_instantiator(project_type)
36
+ project_type = induce_project_type if project_type == :unknown
37
+ instantiator = PROJECT_TYPES[project_type]
38
+ if instantiator.nil?
39
+ raise UnrecognizedProjectTypeError
40
+ else
41
+ Dige::Instantiator.const_get(instantiator).new
42
+ end
43
+ end
44
+
45
+ def self.generate_diagram(klass, project_type)
46
+ instantiator = initialize_instantiator(project_type)
47
+ instantiator.load_models
48
+ diagram = klass.new(instantiator.load_klasses)
49
+ puts diagram.debug_inspect.join("\n") if ENV['DEBUG'] and ENV['DEBUG'].upcase == 'TRUE'
50
+ diagram.generate
51
+ end
52
+
53
+ end
@@ -0,0 +1 @@
1
+ class UnrecognizedProjectTypeError < RuntimeError ; end
@@ -0,0 +1,18 @@
1
+ class Dige::GraphvizDiagram::EntityRelationshipDiagram < Dige::GraphvizDiagram
2
+ def generate(file = ENV['FILE'], type = 'png')
3
+ generate_diagram(file + "_entity_relationship_model", type) do
4
+ assocs = associations.all.collect do |assoc|
5
+ clr = random_color
6
+ if assoc.table_name
7
+ [{ :from => assoc.table_name, :to => assoc.from_table_name }, { :from => assoc.table_name, :to => assoc.to_table_name }]
8
+ else
9
+ [{ :from => assoc.from_table_name, :to => assoc.to_table_name }]
10
+ end
11
+ end.flatten
12
+
13
+ klasses.all.collect { |klass| node(klass.table_name, :associations_count => klass.associations_count) }.uniq.sort +
14
+ associations.all.collect { |assoc| assoc.table_name ? node(assoc.table_name) : nil }.compact.uniq.sort +
15
+ assocs.uniq.collect { |ass| edge(ass[:from], ass[:to], true, { :color => random_color}) }.uniq.sort
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ class Dige::GraphvizDiagram::UMLClassDiagram < Dige::GraphvizDiagram
2
+ def edge_settings
3
+ super.merge(
4
+ {
5
+ :arrowhead => :none
6
+ })
7
+ end
8
+
9
+ def generate(file = ENV['FILE'], type = 'png')
10
+ generate_diagram(file + "_uml_class_diagram", type) do
11
+ assocs = associations.all.collect do |assoc|
12
+ { :from => assoc.from_class_name, :to => assoc.to_class_name, :from_arity => assoc.from_arity, :to_arity => assoc.to_arity }
13
+ end
14
+
15
+ pcrs = parent_child_relations.all.collect do |pcr|
16
+ { :from => pcr.child_class_name, :to => pcr.parent_class_name}
17
+ end
18
+
19
+ klasses.all.collect { |klass| node(klass.class_name, :associations_count => klass.associations_count) }.uniq.sort +
20
+ pcrs.collect { |pcr| edge(pcr[:from], pcr[:to], true, {:color => random_color, :arrowhead => :empty}) }.uniq.sort +
21
+ assocs.collect { |ass| edge(ass[:from], ass[:to], true, { :color => random_color, :headlabel => ass[:to_arity], :taillabel => ass[:from_arity]}) }.uniq.sort
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,94 @@
1
+ class Dige::GraphvizDiagram
2
+ include Dige::ClassBase
3
+ attr_accessor :klasses, :parent_child_relations, :associations
4
+
5
+ DEFAULT_NODE_WIDTH = 3.0
6
+ DEFAULT_NODE_HEIGHT = 2.0
7
+
8
+ def generate_diagram(file = nil, type = 'png', graphtype = :digraph, &block)
9
+ output = [header(*([graphtype, file].compact)), settings, yield, "}"].flatten.compact.join("\n")
10
+ puts output if ENV['DEBUG'] and ENV['DEBUG'].upcase == 'TRUE'
11
+
12
+ unless file.nil?
13
+ File.open("#{file}.dot", "w") do |f| f.write output end
14
+ unless system("dot -T#{type} -o#{file}.#{type} #{file}.dot") && ((ENV['NO_RM'] && ENV['NO_RM'].upcase == 'TRUE') || FileUtils.rm_f("#{file}.dot"))
15
+ $stderr.puts "error: failed to generate png diagram from dot file #{file}.dot, file is left existing for investigation."
16
+ end
17
+ else
18
+ puts output
19
+ end
20
+ end
21
+
22
+ def header(type, name = "my_diagram")
23
+ "#{type} #{name} {"
24
+ end
25
+
26
+ def settings
27
+ [
28
+ (!graph_settings.empty? ? "graph [" + graph_settings.collect { |k,v| "#{k}=#{quote(v)}" }.join(",") + "];" : nil),
29
+ (!edge_settings.empty? ? "edge [" + edge_settings.collect { |k,v| "#{k}=#{quote(v)}" }.join(",") + "];" : nil),
30
+ (!node_settings.empty? ? "node [" + node_settings.collect { |k,v| "#{k}=#{quote(v)}" }.join(",") + "];" : nil)
31
+ ]
32
+ end
33
+
34
+ def graph_settings
35
+ {
36
+ :outputorder => :nodesfirst,
37
+ :concentrate => true,
38
+ :ranksep => 2.5
39
+ }
40
+ end
41
+
42
+ def node_settings
43
+ {
44
+ :shape => :box,
45
+ :fontsize => 36.0
46
+ }
47
+ end
48
+
49
+ def edge_settings
50
+ {
51
+ :arrowsize => 1.0,
52
+ :penwidth => 2.0
53
+ }
54
+ end
55
+
56
+ def node(name, options={})
57
+ associations_count = options.delete(:associations_count)
58
+ options[:width] = DEFAULT_NODE_WIDTH
59
+ options[:height] = DEFAULT_NODE_HEIGHT
60
+ node_options = options.empty? ? "" : "[" + options.collect { |k,v| "#{k}=#{quote(v)}" }.join(",") + "]"
61
+ "#{quote(name)}#{node_options};"
62
+ end
63
+
64
+ def edge(from, to, directed=true, options={})
65
+ edge_options = options.empty? ? "" : "[" + options.collect { |k,v| "#{k}=#{quote(v)}" }.join(",") + "]"
66
+ "#{quote(from)} #{directed ? "->" : "--"} #{quote(to)}#{edge_options};"
67
+ end
68
+
69
+ def quote(string)
70
+ "\"#{string}\""
71
+ end
72
+
73
+ def debug_inspect
74
+ [
75
+ klasses.debug_inspect,
76
+ associations.debug_inspect,
77
+ parent_child_relations.debug_inspect
78
+ ].flatten
79
+ end
80
+
81
+ def random_color
82
+ minimum = 10
83
+ maximum = 210
84
+ r = ((rand*maximum) + minimum).to_i
85
+ g = ((rand*maximum) + minimum).to_i
86
+ b = ((rand*maximum) + minimum).to_i
87
+
88
+ "#" + r.to_s(16).rjust(2, '0') + g.to_s(16).rjust(2, '0') + b.to_s(16).rjust(2, '0')
89
+ end
90
+
91
+ end
92
+
93
+ require 'dige/graphviz_diagram/entity_relationship_diagram'
94
+ require 'dige/graphviz_diagram/uml_class_diagram'
@@ -0,0 +1,11 @@
1
+ class Dige::Instantiator::DataMapper < Dige::Instantiator
2
+
3
+ def load_models
4
+ raise NotImplementedError.new("This method should be overridden in a subclass")
5
+ end
6
+
7
+ def load_klasses
8
+ raise NotImplementedError.new("This method should be overridden in a subclass")
9
+ end
10
+
11
+ end
@@ -0,0 +1,99 @@
1
+ class Dige::Instantiator::Rails2 < Dige::Instantiator
2
+
3
+ def load_models
4
+ models = Dir["app/models/**/*.rb"].collect do |model| File.expand_path(model) end
5
+ retries = {}
6
+ while !models.empty? do
7
+ model = models.shift
8
+ begin
9
+ require model
10
+ retries = {}
11
+ rescue Exception => e
12
+ retries[model] ||= 0
13
+ retries[model] += 1
14
+ break if retries[model] > 1
15
+ models.push model
16
+ end
17
+ end
18
+ $stderr.puts "warning: Failed to load models: #{retries.keys.collect { |m| m.gsub(/^#{RAILS_ROOT}\/app\/models\//, '') }.join(', ')}." unless retries.empty?
19
+ end
20
+
21
+ def load_klasses
22
+ klasses = Dige::Klasses.new
23
+ parent_child_relations = Dige::ParentChildRelations.new
24
+ associations = Dige::Associations.new
25
+
26
+ Class.subclasses_of(ActiveRecord::Base).each do |klass|
27
+ columns = ActiveRecord::Base.connection.table_exists?(klass.table_name) ? klass.columns.collect { |column| column.name.to_s } : []
28
+ klasses.register(klass.name, klass.table_name, columns) unless Dige::IGNORE_CLASSES.include?(klass.name)
29
+ parent_child_relations.register(klass.superclass.name, klass.name) unless Dige::IGNORE_CLASSES.include?(klass.superclass.name) || Dige::IGNORE_CLASSES.include?(klass.name)
30
+ end.each do |klass|
31
+ klass.reflect_on_all_associations.each do |assoc|
32
+ type = assoc.options[:polymorphic] ? :polymorphic : :normal
33
+
34
+ next if assoc.options[:through] || Dige::IGNORE_CLASSES.include?(klass.name)
35
+
36
+ if type == :polymorphic &&
37
+ (!klasses[klass.name].columns.include?((assoc.options[:foreign_key] || "#{assoc.name.to_s}_id").to_s) ||
38
+ !klasses[klass.name].columns.include?((assoc.options[:foreign_type] || "#{assoc.name.to_s}_type").to_s))
39
+ $stderr.puts "warning: class #{klass.name} defines a polymorphic relationship #{assoc.name.to_s}, for which there are not the necessary columns in the DB."
40
+ next
41
+ end
42
+
43
+ assoc_class_name = if type == :polymorphic
44
+ assoc.name.to_s
45
+ elsif assoc.options[:class_name]
46
+ assoc.options[:class_name].to_s
47
+ else
48
+ if assoc.macro == :has_many || assoc.macro == :has_and_belongs_to_many
49
+ assoc.name.to_s.singularize.camelize
50
+ else
51
+ assoc.name.to_s.camelize
52
+ end
53
+ end
54
+
55
+ next if Dige::IGNORE_CLASSES.include?(assoc_class_name)
56
+
57
+ if klasses[assoc_class_name].nil? && type != :polymorphic
58
+ polymorphic_name = (assoc.options[:foreign_key] || assoc.options[:foreign_type] || "").to_s.gsub(/_(id|type)$/, '')
59
+ if !polymorphic_name.empty? &&
60
+ (klasses[klass.name].columns.include?("#{polymorphic_name}_id")) &&
61
+ (klasses[klass.name].columns.include?("#{polymorphic_name}_type"))
62
+ assoc_class_name = polymorphic_name
63
+ type = :polymorphic
64
+ else
65
+ $stderr.puts "warning: class #{klass.name} references non-existing class #{assoc_class_name}" unless assoc.options[:through]
66
+ next
67
+ end
68
+ end
69
+
70
+ assoc_table_name = klasses[assoc_class_name].table_name unless type == :polymorphic
71
+
72
+ associations.register(klass.name, klass.table_name, assoc_class_name, assoc_table_name, type, assoc.macro, assoc.options[:join_table])
73
+ end
74
+
75
+ end
76
+
77
+ associations.all(:polymorphic).each do |assoc|
78
+ polymorphic_klasses = ActiveRecord::Base.connection.select_values("SELECT DISTINCT #{assoc.to_class_name}_type FROM #{assoc.from_table_name};").compact
79
+
80
+ klasses.all.each do |klass|
81
+ next unless polymorphic_klasses.include?(klass.class_name)
82
+ associations.register(assoc.from_class_name, assoc.from_table_name, klass.class_name, klass.table_name, :normal, assoc.macro, assoc.table_name)
83
+ end
84
+ end
85
+
86
+ associations.all.each do |assoc|
87
+ klasses[assoc.from_class_name].associations_count += 1
88
+ klasses[assoc.to_class_name].associations_count += 1
89
+ end
90
+
91
+ {
92
+ :klasses => klasses,
93
+ :parent_child_relations => parent_child_relations,
94
+ :associations => associations
95
+ }
96
+
97
+ end
98
+
99
+ end
@@ -0,0 +1,99 @@
1
+ class Dige::Instantiator::Rails3 < Dige::Instantiator
2
+
3
+ def load_models
4
+ models = Dir["app/models/**/*.rb"].collect do |model| File.expand_path(model) end
5
+ retries = {}
6
+ while !models.empty? do
7
+ model = models.shift
8
+ begin
9
+ require model
10
+ retries = {}
11
+ rescue Exception => e
12
+ retries[model] ||= 0
13
+ retries[model] += 1
14
+ break if retries[model] > 1
15
+ models.push model
16
+ end
17
+ end
18
+ $stderr.puts "warning: Failed to load models: #{retries.keys.collect { |m| m.gsub(/^#{RAILS_ROOT}\/app\/models\//, '') }.join(', ')}." unless retries.empty?
19
+ end
20
+
21
+ def load_klasses
22
+ klasses = Dige::Klasses.new
23
+ parent_child_relations = Dige::ParentChildRelations.new
24
+ associations = Dige::Associations.new
25
+
26
+ ActiveRecord::Base.subclasses.each do |klass|
27
+ columns = ActiveRecord::Base.connection.table_exists?(klass.table_name) ? klass.columns.collect { |column| column.name.to_s } : []
28
+ klasses.register(klass.name, klass.table_name, columns) unless Dige::IGNORE_CLASSES.include?(klass.name)
29
+ parent_child_relations.register(klass.superclass.name, klass.name) unless Dige::IGNORE_CLASSES.include?(klass.superclass.name) || Dige::IGNORE_CLASSES.include?(klass.name)
30
+ end.each do |klass|
31
+ klass.reflect_on_all_associations.each do |assoc|
32
+ type = assoc.options[:polymorphic] ? :polymorphic : :normal
33
+
34
+ next if assoc.options[:through] || Dige::IGNORE_CLASSES.include?(klass.name)
35
+
36
+ if type == :polymorphic &&
37
+ (!klasses[klass.name].columns.include?((assoc.options[:foreign_key] || "#{assoc.name.to_s}_id").to_s) ||
38
+ !klasses[klass.name].columns.include?((assoc.options[:foreign_type] || "#{assoc.name.to_s}_type").to_s))
39
+ $stderr.puts "warning: class #{klass.name} defines a polymorphic relationship #{assoc.name.to_s}, for which there are not the necessary columns in the DB."
40
+ next
41
+ end
42
+
43
+ assoc_class_name = if type == :polymorphic
44
+ assoc.name.to_s
45
+ elsif assoc.options[:class_name]
46
+ assoc.options[:class_name].to_s
47
+ else
48
+ if assoc.macro == :has_many || assoc.macro == :has_and_belongs_to_many
49
+ assoc.name.to_s.singularize.camelize
50
+ else
51
+ assoc.name.to_s.camelize
52
+ end
53
+ end
54
+
55
+ next if Dige::IGNORE_CLASSES.include?(assoc_class_name)
56
+
57
+ if klasses[assoc_class_name].nil? && type != :polymorphic
58
+ polymorphic_name = (assoc.options[:foreign_key] || assoc.options[:foreign_type] || "").to_s.gsub(/_(id|type)$/, '')
59
+ if !polymorphic_name.empty? &&
60
+ (klasses[klass.name].columns.include?("#{polymorphic_name}_id")) &&
61
+ (klasses[klass.name].columns.include?("#{polymorphic_name}_type"))
62
+ assoc_class_name = polymorphic_name
63
+ type = :polymorphic
64
+ else
65
+ $stderr.puts "warning: class #{klass.name} references non-existing class #{assoc_class_name}" unless assoc.options[:through]
66
+ next
67
+ end
68
+ end
69
+
70
+ assoc_table_name = klasses[assoc_class_name].table_name unless type == :polymorphic
71
+
72
+ associations.register(klass.name, klass.table_name, assoc_class_name, assoc_table_name, type, assoc.macro, assoc.options[:join_table])
73
+ end
74
+
75
+ end
76
+
77
+ associations.all(:polymorphic).each do |assoc|
78
+ polymorphic_klasses = ActiveRecord::Base.connection.select_values("SELECT DISTINCT #{assoc.to_class_name}_type FROM #{assoc.from_table_name};").compact
79
+
80
+ klasses.all.each do |klass|
81
+ next unless polymorphic_klasses.include?(klass.class_name)
82
+ associations.register(assoc.from_class_name, assoc.from_table_name, klass.class_name, klass.table_name, :normal, assoc.macro, assoc.table_name)
83
+ end
84
+ end
85
+
86
+ associations.all.each do |assoc|
87
+ klasses[assoc.from_class_name].associations_count += 1
88
+ klasses[assoc.to_class_name].associations_count += 1
89
+ end
90
+
91
+ {
92
+ :klasses => klasses,
93
+ :parent_child_relations => parent_child_relations,
94
+ :associations => associations
95
+ }
96
+
97
+ end
98
+
99
+ end
@@ -0,0 +1,15 @@
1
+ class Dige::Instantiator
2
+
3
+ def load_models
4
+ raise NotImplementedError.new("This method should be overridden in a subclass")
5
+ end
6
+
7
+ def load_klasses
8
+ raise NotImplementedError.new("This method should be overridden in a subclass")
9
+ end
10
+
11
+ end
12
+
13
+ require 'dige/instantiator/data_mapper'
14
+ require 'dige/instantiator/rails2'
15
+ require 'dige/instantiator/rails3'
data/lib/dige/klass.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Dige::Klass
2
+ include Dige::ClassBase
3
+ attr_accessor :class_name, :table_name, :columns, :associations_count
4
+
5
+ def debug_inspect
6
+ "#{class_name}(#{table_name})[ASSOCIATIONS_COUNT: #{associations_count}, COLUMNS: #{columns.join(",")}]"
7
+ end
8
+
9
+ def ==(objekt)
10
+ self.class_name == objekt.class_name &&
11
+ self.table_name == objekt.table_name
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ class Dige::Klasses
2
+ include Dige::List
3
+
4
+ def [](name)
5
+ @list.detect { |list_item| list_item.send("#{name.to_s =~ /^[A-Z]/ ? "class_name" : "table_name"}".to_sym).to_s == name.to_s }
6
+ end
7
+
8
+ def register(class_name, table_name, columns)
9
+ objekt = Dige::Klass.new({ :class_name => class_name, :table_name => table_name, :columns => columns, :associations_count => 0 })
10
+ unless return_objekt = find(objekt)
11
+ list << objekt
12
+ return_objekt = objekt
13
+ end
14
+ return_objekt
15
+ end
16
+
17
+ def all
18
+ list
19
+ end
20
+
21
+ end
data/lib/dige/list.rb ADDED
@@ -0,0 +1,19 @@
1
+ module Dige::List
2
+ def self.included(base)
3
+ base.send(:include, InstanceMethods)
4
+ end
5
+
6
+ module InstanceMethods
7
+ def find(objekt)
8
+ list.detect { |list_item| list_item == objekt }
9
+ end
10
+
11
+ def list
12
+ @list ||= []
13
+ end
14
+
15
+ def debug_inspect
16
+ [ "\n\n#{self.class.name}:\n" ] + all.collect { |objekt| objekt.debug_inspect }.flatten.sort
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ class Dige::ParentChildRelation
2
+ include Dige::ClassBase
3
+ attr_accessor :parent_class_name, :child_class_name
4
+
5
+ def debug_inspect
6
+ "#{parent_class_name} < #{child_class_name}"
7
+ end
8
+
9
+ def ==(objekt)
10
+ self.parent_class_name == objekt.parent_class_name &&
11
+ self.child_class_name == objekt.child_class_name
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ class Dige::ParentChildRelations
2
+ include Dige::List
3
+
4
+ def register(parent_class_name, child_class_name)
5
+ objekt = Dige::ParentChildRelation.new({ :parent_class_name => parent_class_name, :child_class_name => child_class_name })
6
+ unless return_objekt = find(objekt)
7
+ list << objekt
8
+ return_objekt = objekt
9
+ end
10
+ return_objekt
11
+ end
12
+
13
+ def all
14
+ list
15
+ end
16
+
17
+ end
@@ -0,0 +1,7 @@
1
+ class Dige::ProjectTypeRecognizer::DataMapper < Dige::ProjectTypeRecognizer
2
+
3
+ def recognize
4
+ raise NotImplementedError.new("This method should be overridden in a subclass")
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class Dige::ProjectTypeRecognizer::Rails2 < Dige::ProjectTypeRecognizer
2
+
3
+ def recognize
4
+ raise NotImplementedError.new("This method should be overridden in a subclass")
5
+ end
6
+
7
+ end
@@ -0,0 +1,7 @@
1
+ class Dige::ProjectTypeRecognizer::Rails3 < Dige::ProjectTypeRecognizer
2
+
3
+ def recognize
4
+ raise NotImplementedError.new("This method should be overridden in a subclass")
5
+ end
6
+
7
+ end
@@ -0,0 +1,11 @@
1
+ class Dige::ProjectTypeRecognizer
2
+
3
+ def recognize
4
+ raise NotImplementedError.new("This method should be overridden in a subclass")
5
+ end
6
+
7
+ end
8
+
9
+ require 'dige/project_type_recognizer/data_mapper'
10
+ require 'dige/project_type_recognizer/rails2'
11
+ require 'dige/project_type_recognizer/rails3'
data/lib/dige.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'dige/dige'
2
+ require 'dige/errors'
3
+ require 'dige/class_base'
4
+ require 'dige/klass'
5
+ require 'dige/association'
6
+ require 'dige/parent_child_relation'
7
+ require 'dige/list'
8
+ require 'dige/klasses'
9
+ require 'dige/associations'
10
+ require 'dige/parent_child_relations'
11
+ require 'dige/project_type_recognizer'
12
+ require 'dige/instantiator'
13
+ require 'dige/graphviz_diagram'
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: le1t0-dige
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.9.0
6
+ platform: ruby
7
+ authors:
8
+ - Le1t0
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-03-03 00:00:00 +01:00
14
+ default_executable: dige
15
+ dependencies: []
16
+
17
+ description: " This project provides code for generating ERD and class diagrams from rails projects.\n"
18
+ email: dev@ewout.to
19
+ executables:
20
+ - dige
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - bin/dige
27
+ - lib/dige/association.rb
28
+ - lib/dige/associations.rb
29
+ - lib/dige/class_base.rb
30
+ - lib/dige/dige.rb
31
+ - lib/dige/errors.rb
32
+ - lib/dige/graphviz_diagram/entity_relationship_diagram.rb
33
+ - lib/dige/graphviz_diagram/uml_class_diagram.rb
34
+ - lib/dige/graphviz_diagram.rb
35
+ - lib/dige/instantiator/data_mapper.rb
36
+ - lib/dige/instantiator/rails2.rb
37
+ - lib/dige/instantiator/rails3.rb
38
+ - lib/dige/instantiator.rb
39
+ - lib/dige/klass.rb
40
+ - lib/dige/klasses.rb
41
+ - lib/dige/list.rb
42
+ - lib/dige/parent_child_relation.rb
43
+ - lib/dige/parent_child_relations.rb
44
+ - lib/dige/project_type_recognizer/data_mapper.rb
45
+ - lib/dige/project_type_recognizer/rails2.rb
46
+ - lib/dige/project_type_recognizer/rails3.rb
47
+ - lib/dige/project_type_recognizer.rb
48
+ - lib/dige.rb
49
+ - README
50
+ has_rdoc: true
51
+ homepage: http://github.com/le1t0/dige
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options: []
56
+
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.5.2
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: (rails) diagram generator
78
+ test_files: []
79
+