dmattes-railroad_xing 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/AUTHORS +13 -0
- data/COPYING +340 -0
- data/ChangeLog +96 -0
- data/INSTALL +66 -0
- data/README +159 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/bin/railroad +52 -0
- data/lib/railroad/aasm_diagram.rb +116 -0
- data/lib/railroad/app_diagram.rb +109 -0
- data/lib/railroad/ar_model.rb +105 -0
- data/lib/railroad/controllers_diagram.rb +81 -0
- data/lib/railroad/diagram_graph.rb +139 -0
- data/lib/railroad/dm_model.rb +148 -0
- data/lib/railroad/framework_factory.rb +35 -0
- data/lib/railroad/merb_framework.rb +55 -0
- data/lib/railroad/model_factory.rb +25 -0
- data/lib/railroad/models_diagram.rb +86 -0
- data/lib/railroad/options_struct.rb +173 -0
- data/lib/railroad/rails_framework.rb +54 -0
- data/railroad_xing.gemspec +59 -0
- metadata +77 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
#
|
7
|
+
# Dec 2008 - Roy Wright
|
8
|
+
# added FrameworkFactory to support multiple application frameworks
|
9
|
+
|
10
|
+
require 'railroad/diagram_graph'
|
11
|
+
require 'railroad/framework_factory'
|
12
|
+
|
13
|
+
# Root class for RailRoad diagrams
|
14
|
+
class AppDiagram
|
15
|
+
|
16
|
+
def initialize(options)
|
17
|
+
@options = options
|
18
|
+
@graph = DiagramGraph.new
|
19
|
+
@graph.show_label = @options.label
|
20
|
+
|
21
|
+
STDERR.print "Loading application environment\n" if @options.verbose
|
22
|
+
load_environment
|
23
|
+
|
24
|
+
STDERR.print "Loading application classes\n" if @options.verbose
|
25
|
+
load_classes
|
26
|
+
end
|
27
|
+
|
28
|
+
# Print diagram
|
29
|
+
def print
|
30
|
+
if @options.output
|
31
|
+
old_stdout = STDOUT.dup
|
32
|
+
begin
|
33
|
+
STDOUT.reopen(@options.output)
|
34
|
+
rescue
|
35
|
+
STDERR.print "Error: Cannot write diagram to #{@options.output}\n\n"
|
36
|
+
exit 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if @options.xmi
|
41
|
+
STDERR.print "Generating XMI diagram\n" if @options.verbose
|
42
|
+
STDOUT.print @graph.to_xmi
|
43
|
+
else
|
44
|
+
STDERR.print "Generating DOT graph\n" if @options.verbose
|
45
|
+
STDOUT.print @graph.to_dot
|
46
|
+
end
|
47
|
+
|
48
|
+
if @options.output
|
49
|
+
STDOUT.reopen(old_stdout)
|
50
|
+
end
|
51
|
+
end # print
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Prevents Rails application from writing to STDOUT
|
56
|
+
def disable_stdout
|
57
|
+
@old_stdout = STDOUT.dup
|
58
|
+
STDOUT.reopen(PLATFORM =~ /mswin/ ? "NUL" : "/dev/null")
|
59
|
+
end
|
60
|
+
|
61
|
+
# Restore STDOUT
|
62
|
+
def enable_stdout
|
63
|
+
STDOUT.reopen(@old_stdout)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# Print error when loading Rails application
|
68
|
+
def print_error(type)
|
69
|
+
STDERR.print "Error loading #{type}.\n (Are you running " +
|
70
|
+
"#{APP_NAME} on the application's root directory?)\n\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Load Rails application's environment
|
74
|
+
def load_environment
|
75
|
+
begin
|
76
|
+
disable_stdout
|
77
|
+
@framework = FrameworkFactory.getFramework
|
78
|
+
raise LoadError.new if @framework.nil?
|
79
|
+
@graph.migration_version = @framework.migration_version
|
80
|
+
enable_stdout
|
81
|
+
rescue LoadError
|
82
|
+
enable_stdout
|
83
|
+
print_error "application environment"
|
84
|
+
raise
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# is the given class a subclass of the application controller?
|
89
|
+
def is_application_subclass?(klass)
|
90
|
+
@framework.is_application_subclass?(klass)
|
91
|
+
end
|
92
|
+
|
93
|
+
# get the controller's files returning the application controller first in returned array
|
94
|
+
def get_controller_files(options)
|
95
|
+
@framework.get_controller_files(options)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Extract class name from filename
|
99
|
+
def extract_class_name(filename)
|
100
|
+
@framework.extract_class_name(filename)
|
101
|
+
end
|
102
|
+
|
103
|
+
# convert the give string to a constant
|
104
|
+
def constantize(str)
|
105
|
+
@framework.constantize(str)
|
106
|
+
end
|
107
|
+
|
108
|
+
end # class AppDiagram
|
109
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# The "model" used to interact with ActiveRecord models.
|
2
|
+
#
|
3
|
+
# Dec 2008 - Roy Wright
|
4
|
+
# created class an refactored logic from models_diagram.rb
|
5
|
+
#
|
6
|
+
class AR_Model
|
7
|
+
def initialize(klass, options)
|
8
|
+
@klass = klass
|
9
|
+
@options = options
|
10
|
+
# Processed habtm associations
|
11
|
+
@habtm = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# return an Array of attribute (column) "name:type" strings for the
|
15
|
+
# model.
|
16
|
+
# if @options.hide_magic is asserted, then remove some standard
|
17
|
+
# attribute names from the array.
|
18
|
+
# if @options.hide_types is asserted, then the returned strings are
|
19
|
+
# just the names "name".
|
20
|
+
def attributes
|
21
|
+
attribs = []
|
22
|
+
|
23
|
+
if @options.hide_magic
|
24
|
+
# From patch #13351
|
25
|
+
# http://wiki.rubyonrails.org/rails/pages/MagicFieldNames
|
26
|
+
magic_fields = [
|
27
|
+
"created_at", "created_on", "updated_at", "updated_on",
|
28
|
+
"lock_version", "type", "id", "position", "parent_id", "lft",
|
29
|
+
"rgt", "quote", "template"
|
30
|
+
]
|
31
|
+
magic_fields << @klass.table_name + "_count" if @klass.respond_to? 'table_name'
|
32
|
+
content_columns = @klass.content_columns.select {|c| ! magic_fields.include? c.name}
|
33
|
+
elsif @options.show_magic
|
34
|
+
content_columns = @klass.columns
|
35
|
+
else
|
36
|
+
content_columns = @klass.content_columns
|
37
|
+
end
|
38
|
+
|
39
|
+
content_columns.each do |a|
|
40
|
+
content_column = a.name
|
41
|
+
content_column += ' :' + a.type.to_s unless @options.hide_types
|
42
|
+
attribs << content_column
|
43
|
+
end
|
44
|
+
attribs
|
45
|
+
end
|
46
|
+
|
47
|
+
# is the model abstract?
|
48
|
+
def abstract?
|
49
|
+
@klass.abstract_class?
|
50
|
+
end
|
51
|
+
|
52
|
+
# return the model edges (relationships)
|
53
|
+
def edges
|
54
|
+
found_edges = []
|
55
|
+
# Process class associations
|
56
|
+
associations = @klass.reflect_on_all_associations
|
57
|
+
if @options.inheritance && ! @options.transitive
|
58
|
+
superclass_associations = @klass.superclass.reflect_on_all_associations
|
59
|
+
|
60
|
+
associations = associations.select{|a| ! superclass_associations.include? a}
|
61
|
+
# This doesn't works!
|
62
|
+
# associations -= current_class.superclass.reflect_on_all_associations
|
63
|
+
end
|
64
|
+
associations.each do |a|
|
65
|
+
found_edges << process_association(@klass.name, a)
|
66
|
+
end
|
67
|
+
found_edges.compact
|
68
|
+
end
|
69
|
+
|
70
|
+
# is the model meaningful?
|
71
|
+
def meaningful?
|
72
|
+
(@klass.superclass != ActiveRecord::Base) && (@klass.superclass != Object)
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
# Process a model association
|
78
|
+
def process_association(class_name, assoc)
|
79
|
+
STDERR.print "\t\tProcessing model association #{assoc.name.to_s}\n" if @options.verbose
|
80
|
+
|
81
|
+
assoc_type = nil
|
82
|
+
# Skip "belongs_to" associations
|
83
|
+
unless assoc.macro.to_s == 'belongs_to'
|
84
|
+
# Only non standard association names needs a label
|
85
|
+
assoc_class_name = (assoc.class_name.respond_to? 'underscore') ? assoc.class_name.underscore.singularize.camelize : assoc.class_name
|
86
|
+
if assoc_class_name == assoc.name.to_s.singularize.camelize
|
87
|
+
assoc_name = ''
|
88
|
+
else
|
89
|
+
assoc_name = assoc.name.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
if assoc.macro.to_s == 'has_one'
|
93
|
+
assoc_type = 'one-one'
|
94
|
+
elsif assoc.macro.to_s == 'has_many' && (! assoc.options[:through])
|
95
|
+
assoc_type = 'one-many'
|
96
|
+
else # habtm or has_many, :through
|
97
|
+
return if @habtm.include? [assoc.class_name, class_name, assoc_name]
|
98
|
+
assoc_type = 'many-many'
|
99
|
+
@habtm << [class_name, assoc.class_name, assoc_name]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
assoc_type.nil? ? nil : [assoc_type, class_name, assoc_class_name, assoc_name]
|
103
|
+
end # process_association
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
#
|
7
|
+
# Dec 2008
|
8
|
+
# minor isolation from framework
|
9
|
+
|
10
|
+
require 'railroad/app_diagram'
|
11
|
+
|
12
|
+
# RailRoad controllers diagram
|
13
|
+
class ControllersDiagram < AppDiagram
|
14
|
+
|
15
|
+
def initialize(options)
|
16
|
+
#options.exclude.map! {|e| "app/controllers/" + e}
|
17
|
+
super options
|
18
|
+
@graph.diagram_type = 'Controllers'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Process controller files
|
22
|
+
def generate
|
23
|
+
STDERR.print "Generating controllers diagram\n" if @options.verbose
|
24
|
+
|
25
|
+
files = get_controller_files(@options)
|
26
|
+
files.each do |f|
|
27
|
+
class_name = extract_class_name(f)
|
28
|
+
process_class constantize(class_name)
|
29
|
+
end
|
30
|
+
end # generate
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Load controller classes
|
35
|
+
def load_classes
|
36
|
+
begin
|
37
|
+
disable_stdout
|
38
|
+
# ApplicationController must be loaded first
|
39
|
+
files = get_controller_files(@options)
|
40
|
+
files.each {|c| require c }
|
41
|
+
enable_stdout
|
42
|
+
rescue LoadError
|
43
|
+
enable_stdout
|
44
|
+
print_error "controller classes"
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
end # load_classes
|
48
|
+
|
49
|
+
# Proccess a controller class
|
50
|
+
def process_class(current_class)
|
51
|
+
|
52
|
+
STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
|
53
|
+
|
54
|
+
if @options.brief
|
55
|
+
@graph.add_node ['controller-brief', current_class.name]
|
56
|
+
elsif current_class.is_a? Class
|
57
|
+
# Collect controller's methods
|
58
|
+
node_attribs = {:public => [],
|
59
|
+
:protected => [],
|
60
|
+
:private => []}
|
61
|
+
current_class.public_instance_methods(false).sort.each { |m|
|
62
|
+
node_attribs[:public] << m
|
63
|
+
} unless @options.hide_public
|
64
|
+
current_class.protected_instance_methods(false).sort.each { |m|
|
65
|
+
node_attribs[:protected] << m
|
66
|
+
} unless @options.hide_protected
|
67
|
+
current_class.private_instance_methods(false).sort.each { |m|
|
68
|
+
node_attribs[:private] << m
|
69
|
+
} unless @options.hide_private
|
70
|
+
@graph.add_node ['controller', current_class.name, node_attribs]
|
71
|
+
elsif @options.modules && current_class.is_a?(Module)
|
72
|
+
@graph.add_node ['module', current_class.name]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Generate the inheritance edge (only for ApplicationControllers)
|
76
|
+
if @options.inheritance && is_application_subclass?(current_class)
|
77
|
+
@graph.add_edge ['is-a', current_class.superclass.name, current_class.name]
|
78
|
+
end
|
79
|
+
end # process_class
|
80
|
+
|
81
|
+
end # class ControllersDiagram
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# RailRoad - RoR diagrams generator
|
2
|
+
# http://railroad.rubyforge.org
|
3
|
+
#
|
4
|
+
# Copyright 2007-2008 - Javier Smaldone (http://www.smaldone.com.ar)
|
5
|
+
# See COPYING for more details
|
6
|
+
#
|
7
|
+
# Dec 2008 - Roy Wright
|
8
|
+
# minor isolation from framework
|
9
|
+
|
10
|
+
# RailRoad diagram structure
|
11
|
+
class DiagramGraph
|
12
|
+
attr_accessor :migration_version
|
13
|
+
def initialize
|
14
|
+
@migration_version = nil
|
15
|
+
@diagram_type = ''
|
16
|
+
@show_label = false
|
17
|
+
@nodes = []
|
18
|
+
@edges = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_node(node)
|
22
|
+
@nodes << node
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_edge(edge)
|
26
|
+
@edges << edge
|
27
|
+
end
|
28
|
+
|
29
|
+
def diagram_type= (type)
|
30
|
+
@diagram_type = type
|
31
|
+
end
|
32
|
+
|
33
|
+
def show_label= (value)
|
34
|
+
@show_label = value
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
# Generate DOT graph
|
39
|
+
def to_dot
|
40
|
+
return dot_header +
|
41
|
+
@nodes.uniq.map{|n| dot_node n[0], n[1], n[2]}.join +
|
42
|
+
@edges.uniq.map{|e| dot_edge e[0], e[1], e[2], e[3]}.join +
|
43
|
+
dot_footer
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate XMI diagram (not yet implemented)
|
47
|
+
def to_xmi
|
48
|
+
STDERR.print "Sorry. XMI output not yet implemented.\n\n"
|
49
|
+
return ""
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Build DOT diagram header
|
55
|
+
def dot_header
|
56
|
+
result = "digraph #{@diagram_type.downcase}_diagram {\n" +
|
57
|
+
"\tgraph[overlap=false, splines=true]\n"
|
58
|
+
result += dot_label if @show_label
|
59
|
+
return result
|
60
|
+
end
|
61
|
+
|
62
|
+
# Build DOT diagram footer
|
63
|
+
def dot_footer
|
64
|
+
return "}\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Build diagram label
|
68
|
+
def dot_label
|
69
|
+
buf = []
|
70
|
+
buf << "\t_diagram_info [shape=\"plaintext\", "
|
71
|
+
buf << "label=\"#{@diagram_type} diagram\\l"
|
72
|
+
buf << "Date: #{Time.now.strftime "%b %d %Y - %H:%M"}\\l"
|
73
|
+
unless @migration_version.nil?
|
74
|
+
buf << "Migration version: #{@migration_version}\\l"
|
75
|
+
end
|
76
|
+
buf << "Generated by #{APP_HUMAN_NAME} #{APP_VERSION.join('.')}"
|
77
|
+
buf << "\\l\", fontsize=14]\n"
|
78
|
+
buf.join('')
|
79
|
+
end
|
80
|
+
|
81
|
+
# Build a DOT graph node
|
82
|
+
def dot_node(type, name, attributes=nil)
|
83
|
+
case type
|
84
|
+
when 'model'
|
85
|
+
options = 'shape=Mrecord, label="{' + name + '|'
|
86
|
+
options += attributes.join('\l')
|
87
|
+
options += '\l}"'
|
88
|
+
when 'model-brief'
|
89
|
+
options = ''
|
90
|
+
when 'class'
|
91
|
+
options = 'shape=record, label="{' + name + '|}"'
|
92
|
+
when 'class-brief'
|
93
|
+
options = 'shape=box'
|
94
|
+
when 'controller'
|
95
|
+
options = 'shape=Mrecord, label="{' + name + '|'
|
96
|
+
public_methods = attributes[:public].join('\l')
|
97
|
+
protected_methods = attributes[:protected].join('\l')
|
98
|
+
private_methods = attributes[:private].join('\l')
|
99
|
+
options += public_methods + '\l|' + protected_methods + '\l|' +
|
100
|
+
private_methods + '\l'
|
101
|
+
options += '}"'
|
102
|
+
when 'controller-brief'
|
103
|
+
options = ''
|
104
|
+
when 'module'
|
105
|
+
options = 'shape=box, style=dotted, label="' + name + '"'
|
106
|
+
when 'aasm'
|
107
|
+
# Return subgraph format
|
108
|
+
return "subgraph cluster_#{name.downcase} {\n\tlabel = #{quote(name)}\n\t#{attributes.join("\n ")}}"
|
109
|
+
end # case
|
110
|
+
return "\t#{quote(name)} [#{options}]\n"
|
111
|
+
end # dot_node
|
112
|
+
|
113
|
+
# Build a DOT graph edge
|
114
|
+
def dot_edge(type, from, to, name = '')
|
115
|
+
options = name != '' ? "label=\"#{name}\", " : ''
|
116
|
+
case type
|
117
|
+
when 'one-one'
|
118
|
+
#options += 'taillabel="1"'
|
119
|
+
options += 'arrowtail=odot, arrowhead=dot, dir=both'
|
120
|
+
when 'one-many'
|
121
|
+
#options += 'taillabel="n"'
|
122
|
+
options += 'arrowtail=crow, arrowhead=dot, dir=both'
|
123
|
+
when 'many-many'
|
124
|
+
#options += 'taillabel="n", headlabel="n", arrowtail="normal"'
|
125
|
+
options += 'arrowtail=crow, arrowhead=crow, dir=both'
|
126
|
+
when 'is-a'
|
127
|
+
options += 'arrowhead="none", arrowtail="onormal"'
|
128
|
+
when 'event'
|
129
|
+
options += "fontsize=10"
|
130
|
+
end
|
131
|
+
return "\t#{quote(from)} -> #{quote(to)} [#{options}]\n"
|
132
|
+
end # dot_edge
|
133
|
+
|
134
|
+
# Quotes a class name
|
135
|
+
def quote(name)
|
136
|
+
'"' + name.to_s + '"'
|
137
|
+
end
|
138
|
+
|
139
|
+
end # class DiagramGraph
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# The "model" used to interact with DataMapper models.
|
2
|
+
#
|
3
|
+
# Dec 2008 - Roy Wright
|
4
|
+
# created based on code from models_diagram adapted for DataMapper models.
|
5
|
+
#
|
6
|
+
class DM_Model
|
7
|
+
def initialize(klass, options)
|
8
|
+
@klass = klass
|
9
|
+
@options = options
|
10
|
+
# Processed habtm associations
|
11
|
+
@habtm = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# return an Array of attribute (column) "name:type" strings for the
|
15
|
+
# model.
|
16
|
+
# if @options.hide_magic is asserted, then remove some standard
|
17
|
+
# attribute names from the array.
|
18
|
+
# if @options.hide_types is asserted, then the returned strings are
|
19
|
+
# just the names "name".
|
20
|
+
def attributes
|
21
|
+
attribs = []
|
22
|
+
if @options.hide_magic
|
23
|
+
magic_fields = [
|
24
|
+
"created_at", "created_on", "updated_at", "updated_on",
|
25
|
+
"lock_version", "type", "id", "position", "parent_id", "lft",
|
26
|
+
"rgt", "quote", "template", "count"
|
27
|
+
]
|
28
|
+
content_columns = @klass.properties.select {|c| ! magic_fields.include? c.field}
|
29
|
+
else
|
30
|
+
content_columns = @klass.properties
|
31
|
+
end
|
32
|
+
|
33
|
+
content_columns.each do |a|
|
34
|
+
content_column = a.field
|
35
|
+
content_column += ' :' + a.type.to_s unless @options.hide_types
|
36
|
+
attribs << content_column
|
37
|
+
end
|
38
|
+
attribs
|
39
|
+
end
|
40
|
+
|
41
|
+
# is the model abstract?
|
42
|
+
def abstract?
|
43
|
+
# TODO: does datamapper support the abstract concept?
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
# return the model edges (relationships)
|
48
|
+
def edges
|
49
|
+
found_edges = []
|
50
|
+
# Process class relationships
|
51
|
+
relationships = @klass.relationships
|
52
|
+
|
53
|
+
if @options.inheritance && ! @options.transitive
|
54
|
+
if @klass.superclass.respond_to?'relationships'
|
55
|
+
superclass_relationships = @klass.superclass.relationships
|
56
|
+
relationships = relationships.select{|k,a| superclass_relationships[k].nil?}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
remove_joins(relationships).each do |k, a|
|
60
|
+
found_edges << process_relationship(@klass.name, a)
|
61
|
+
end
|
62
|
+
found_edges.compact
|
63
|
+
end
|
64
|
+
|
65
|
+
# is the model meaningful?
|
66
|
+
def meaningful?
|
67
|
+
(@klass.superclass != Object)
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
# datamapper's relationships for HABTM fully map the relationship
|
73
|
+
# from each end. We do not want to duplicate relationship arrows
|
74
|
+
# on the graph, so remove the duplicates here.
|
75
|
+
def remove_joins(relationships)
|
76
|
+
new_relationships = {}
|
77
|
+
join_names = []
|
78
|
+
relationships.each do |k,v|
|
79
|
+
if v.kind_of? DataMapper::Associations::RelationshipChain
|
80
|
+
join_names << v.name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
relationships.each do |k,v|
|
84
|
+
unless join_names.include? k
|
85
|
+
new_relationships[k] = v
|
86
|
+
end
|
87
|
+
end
|
88
|
+
new_relationships
|
89
|
+
end
|
90
|
+
|
91
|
+
# Process a model association
|
92
|
+
def process_relationship(class_name, relationship)
|
93
|
+
STDERR.print "\t\tProcessing model relationship #{relationship.name.to_s}\n" if @options.verbose
|
94
|
+
|
95
|
+
assoc_type = nil
|
96
|
+
# Skip "belongs_to" relationships
|
97
|
+
unless relationship.options.empty?
|
98
|
+
# Only non standard association names needs a label
|
99
|
+
assoc_class_name = (relationship.child_model.respond_to? 'underscore') ?
|
100
|
+
relationship.child_model.underscore.singularize.camelize :
|
101
|
+
relationship.child_model
|
102
|
+
if assoc_class_name == relationship.name.to_s.singularize.camel_case
|
103
|
+
assoc_name = ''
|
104
|
+
else
|
105
|
+
assoc_name = relationship.name.to_s
|
106
|
+
end
|
107
|
+
|
108
|
+
assoc_type = nil
|
109
|
+
if has_one_relationship?(relationship)
|
110
|
+
assoc_type = 'one-one'
|
111
|
+
elsif has_many_relationship?(relationship) && !has_through_relationship?(relationship)
|
112
|
+
assoc_type = 'one-many'
|
113
|
+
elsif has_many_relationship?(relationship) && has_through_relationship?(relationship)
|
114
|
+
if relationship.kind_of? DataMapper::Associations::RelationshipChain
|
115
|
+
assoc_name = relationship.options[:remote_relationship_name]
|
116
|
+
end
|
117
|
+
return if @habtm.include? [relationship.child_model, class_name, assoc_name]
|
118
|
+
assoc_type = 'many-many'
|
119
|
+
@habtm << [class_name, relationship.child_model, assoc_name]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
assoc_type.nil? ? nil : [assoc_type, class_name, assoc_class_name, assoc_name]
|
123
|
+
end # process_association
|
124
|
+
|
125
|
+
# is this relationship a has n, through?
|
126
|
+
def has_through_relationship?(relationship)
|
127
|
+
result = false
|
128
|
+
# names are symbols
|
129
|
+
near_name = relationship.options[:near_relationship_name]
|
130
|
+
remote_name = relationship.options[:remote_relationship_name]
|
131
|
+
unless near_name.nil? || remote_name.nil?
|
132
|
+
# ok, both near and remote have names
|
133
|
+
result = (near_name != remote_name)
|
134
|
+
end
|
135
|
+
result
|
136
|
+
end
|
137
|
+
|
138
|
+
# is this relationship a has 1?
|
139
|
+
def has_one_relationship?(relationship)
|
140
|
+
(relationship.options[:min] == 1) && (relationship.options[:max] == 1)
|
141
|
+
end
|
142
|
+
|
143
|
+
# is this relationship a has n?
|
144
|
+
def has_many_relationship?(relationship)
|
145
|
+
!relationship.options[:max].nil? && (relationship.options[:max] != 0) && (relationship.options[:max] != 1)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|