royw-railroad_xing 0.5.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,35 @@
1
+ # A factory that determines if railroad is running in an application
2
+ # framework and if so, returns a "framework" object that handles interacting
3
+ # with the framework. If the factory can not determine find a framework,
4
+ # then the factory will return nil.
5
+ #
6
+ # === Usage
7
+ #
8
+ # framework = FrameworkFactory.getFramework
9
+ #
10
+ # Dec 2008 - Roy Wright
11
+ # created to support multiple frameworks
12
+ #
13
+ class FrameworkFactory
14
+
15
+ # the factory the returns a "framework" object or nil
16
+ def self.getFramework
17
+ framework = nil
18
+ if File.exist? 'merb'
19
+ require 'railroad/merb_framework'
20
+ framework = MerbFramework.new
21
+ end
22
+ if File.exist? 'script/server'
23
+ require 'railroad/rails_framework'
24
+ framework = RailsFramework.new
25
+ end
26
+ framework
27
+ end
28
+
29
+ private
30
+
31
+ # prevent instantiation
32
+ def initialize
33
+ end
34
+ end
35
+
@@ -0,0 +1,45 @@
1
+ # A class that encapsulates the interaction with the Merb framework.
2
+ #
3
+ # Note, will use the 'test' envirnoment and database.
4
+ #
5
+ # Warning, will automigrate the 'test' database
6
+ #
7
+ # Dec 2008 - Roy Wright
8
+ # created to support the Merb application framework
9
+ #
10
+ class MerbFramework
11
+ attr_reader :name, :migration_version
12
+
13
+ # enter the merb 'test' environment
14
+ def initialize
15
+ require 'merb-core'
16
+ Merb.start_environment(:testing => true, :adapter => 'runner', :environment => ENV['MERB_ENV'] || 'test')
17
+ DataMapper.auto_migrate!
18
+ @name = 'Merb'
19
+ @migration_version = nil
20
+ end
21
+
22
+ # is the given class a subclass of the application controller?
23
+ def is_application_subclass?(klass)
24
+ (Application.subclasses_list.include? klass.name)
25
+ end
26
+
27
+ # get the controller's files returning the application controller first in returned array
28
+ def get_controller_files(options)
29
+ files = []
30
+ files << 'app/controllers/application.rb'
31
+ files += Dir.glob("app/controllers/**/*.rb") - options.exclude
32
+ files.uniq
33
+ end
34
+
35
+ # Extract class name from filename
36
+ def extract_class_name(filename)
37
+ File.basename(filename).chomp(".rb").camel_case
38
+ end
39
+
40
+ # convert the give string to a constant
41
+ def constantize(str)
42
+ Object.full_const_get(str)
43
+ end
44
+
45
+ end
@@ -0,0 +1,25 @@
1
+ require 'railroad/ar_model'
2
+ require 'railroad/dm_model'
3
+
4
+ # A factory for discovering the ORM being used that will then return the
5
+ # corresponding "model" that will be used to interact with the ORM. Will
6
+ # return nil if unable to discover a supported ORM.
7
+ #
8
+ # Dec 2008 - Roy Wright
9
+ # created to support multiple ORMs
10
+ #
11
+ class ModelFactory
12
+ def self.getModel(klass, options)
13
+ model = nil
14
+ model = AR_Model.new(klass, options) if klass.respond_to?'reflect_on_all_associations'
15
+ model = DM_Model.new(klass, options) if klass.respond_to?'relationships'
16
+ model
17
+ end
18
+
19
+ private
20
+
21
+ # prevent instantiation
22
+ def initialize
23
+ end
24
+ end
25
+
@@ -0,0 +1,86 @@
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
+ # modified to support multiple application frameworks and ORMs
9
+
10
+ require 'railroad/app_diagram'
11
+ require 'railroad/model_factory'
12
+
13
+ # RailRoad models diagram
14
+ class ModelsDiagram < AppDiagram
15
+
16
+ def initialize(options)
17
+ #options.exclude.map! {|e| "app/models/" + e}
18
+ super options
19
+ @graph.diagram_type = 'Models'
20
+ end
21
+
22
+ # Process model files
23
+ def generate
24
+ STDERR.print "Generating models diagram\n" if @options.verbose
25
+ files = Dir.glob("app/models/**/*.rb")
26
+ files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
27
+ files -= @options.exclude
28
+ files.each do |f|
29
+ process_class constantize(extract_class_name(f))
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # Load model classes
36
+ def load_classes
37
+ begin
38
+ disable_stdout
39
+ files = Dir.glob("app/models/**/*.rb")
40
+ files += Dir.glob("vendor/plugins/**/app/models/*.rb") if @options.plugins_models
41
+ files -= @options.exclude
42
+ files.each {|m| require m }
43
+ enable_stdout
44
+ rescue LoadError
45
+ enable_stdout
46
+ print_error "model classes"
47
+ raise
48
+ end
49
+ end # load_classes
50
+
51
+ # Process a model class
52
+ def process_class(current_class)
53
+
54
+ STDERR.print "\tProcessing #{current_class}\n" if @options.verbose
55
+
56
+ model = ModelFactory.getModel(current_class, @options)
57
+ node_attribs = []
58
+ edges = []
59
+ nodes = []
60
+ if @options.brief || (!model.nil? && model.abstract?)
61
+ node_type = 'model-brief'
62
+ else
63
+ node_type = 'model'
64
+ node_attribs += model.attributes unless model.nil?
65
+ end
66
+ nodes << [node_type, current_class.name, node_attribs]
67
+
68
+ if !model.nil?
69
+ edges += model.edges
70
+ # Only consider meaningful inheritance relations classes
71
+ if @options.inheritance && model.meaningful?
72
+ edges << ['is-a', current_class.superclass.name, current_class.name]
73
+ end
74
+ elsif @options.all && (current_class.is_a? Class)
75
+ # Not database model
76
+ node_type = @options.brief ? 'class-brief' : 'class'
77
+ nodes << [node_type, current_class.name]
78
+ edges << ['is-a', current_class.superclass.name, current_class.name]
79
+ elsif @options.modules && (current_class.is_a? Module)
80
+ nodes << ['module', current_class.name]
81
+ end
82
+ nodes.compact.each {|node| @graph.add_node node}
83
+ edges.compact.each {|edge| @graph.add_edge edge}
84
+ end # process_class
85
+
86
+ end # class ModelsDiagram
@@ -0,0 +1,169 @@
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
+ require 'ostruct'
8
+
9
+ # RailRoad command line options parser
10
+ class OptionsStruct < OpenStruct
11
+
12
+ require 'optparse'
13
+
14
+ def initialize
15
+ init_options = { :all => false,
16
+ :brief => false,
17
+ :exclude => [],
18
+ :inheritance => false,
19
+ :join => false,
20
+ :label => false,
21
+ :modules => false,
22
+ :hide_magic => false,
23
+ :hide_types => false,
24
+ :hide_public => false,
25
+ :hide_protected => false,
26
+ :hide_private => false,
27
+ :plugins_models => false,
28
+ :root => '',
29
+ :transitive => false,
30
+ :verbose => false,
31
+ :xmi => false,
32
+ :command => '' }
33
+ super(init_options)
34
+ end # initialize
35
+
36
+ def parse(args)
37
+ @opt_parser = OptionParser.new do |opts|
38
+ opts.banner = "Usage: #{APP_NAME} [options] command"
39
+ opts.separator ""
40
+ opts.separator "Common options:"
41
+ opts.on("-b", "--brief", "Generate compact diagram",
42
+ " (no attributes nor methods)") do |b|
43
+ self.brief = b
44
+ end
45
+ opts.on("-e", "--exclude file1[,fileN]", Array, "Exclude given files") do |list|
46
+ self.exclude = list
47
+ end
48
+ opts.on("-i", "--inheritance", "Include inheritance relations") do |i|
49
+ self.inheritance = i
50
+ end
51
+ opts.on("-l", "--label", "Add a label with diagram information",
52
+ " (type, date, migration, version)") do |l|
53
+ self.label = l
54
+ end
55
+ opts.on("-o", "--output FILE", "Write diagram to file FILE") do |f|
56
+ self.output = f
57
+ end
58
+ opts.on("-r", "--root PATH", "Set PATH as the application root") do |r|
59
+ self.root = r
60
+ end
61
+ opts.on("-v", "--verbose", "Enable verbose output",
62
+ " (produce messages to STDOUT)") do |v|
63
+ self.verbose = v
64
+ end
65
+ opts.on("-x", "--xmi", "Produce XMI instead of DOT",
66
+ " (for UML tools)") do |x|
67
+ self.xmi = x
68
+ end
69
+ opts.separator ""
70
+ opts.separator "Models diagram options:"
71
+ opts.on("-a", "--all", "Include all models",
72
+ " (not only ActiveRecord::Base derived)") do |a|
73
+ self.all = a
74
+ end
75
+ opts.on("--hide-magic", "Hide magic field names") do |h|
76
+ self.hide_magic = h
77
+ end
78
+ opts.on("--hide-types", "Hide attributes type") do |h|
79
+ self.hide_types = h
80
+ end
81
+ opts.on("-j", "--join", "Concentrate edges") do |j|
82
+ self.join = j
83
+ end
84
+ opts.on("-m", "--modules", "Include modules") do |m|
85
+ self.modules = m
86
+ end
87
+ opts.on("-p", "--plugins-models", "Include plugins models") do |p|
88
+ self.plugins_models = p
89
+ end
90
+ opts.on("-t", "--transitive", "Include transitive associations",
91
+ "(through inheritance)") do |t|
92
+ self.transitive = t
93
+ end
94
+ opts.separator ""
95
+ opts.separator "Controllers diagram options:"
96
+ opts.on("--hide-public", "Hide public methods") do |h|
97
+ self.hide_public = h
98
+ end
99
+ opts.on("--hide-protected", "Hide protected methods") do |h|
100
+ self.hide_protected = h
101
+ end
102
+ opts.on("--hide-private", "Hide private methods") do |h|
103
+ self.hide_private = h
104
+ end
105
+ opts.separator ""
106
+ opts.separator "Other options:"
107
+ opts.on("-h", "--help", "Show this message") do
108
+ STDOUT.print "#{opts}\n"
109
+ exit
110
+ end
111
+ opts.on("--version", "Show version and copyright") do
112
+ STDOUT.print "#{APP_HUMAN_NAME} version #{APP_VERSION.join('.')}\n\n" +
113
+ "#{COPYRIGHT}\nThis is free software; see the source " +
114
+ "for copying conditions.\n\n"
115
+ exit
116
+ end
117
+ opts.separator ""
118
+ opts.separator "Commands (you must supply one of these):"
119
+ opts.on("-M", "--models", "Generate models diagram") do |c|
120
+ if self.command != ''
121
+ STDERR.print "Error: Can only generate one diagram type\n\n"
122
+ exit 1
123
+ else
124
+ self.command = 'models'
125
+ end
126
+ end
127
+ opts.on("-C", "--controllers", "Generate controllers diagram") do |c|
128
+ if self.command != ''
129
+ STDERR.print "Error: Can only generate one diagram type\n\n"
130
+ exit 1
131
+ else
132
+ self.command = 'controllers'
133
+ end
134
+ end
135
+ # From Ana Nelson's patch
136
+ opts.on("-A", "--aasm", "Generate \"acts as state machine\" diagram") do |c|
137
+ if self.command == 'controllers'
138
+ STDERR.print "Error: Can only generate one diagram type\n\n"
139
+ exit 1
140
+ else
141
+ self.command = 'aasm'
142
+ end
143
+ end
144
+ opts.separator ""
145
+ opts.separator "For bug reporting and additional information, please see:"
146
+ opts.separator "http://railroad.rubyforge.org/"
147
+ end # do
148
+
149
+ begin
150
+ @opt_parser.parse!(args)
151
+ rescue OptionParser::AmbiguousOption
152
+ option_error "Ambiguous option"
153
+ rescue OptionParser::InvalidOption
154
+ option_error "Invalid option"
155
+ rescue OptionParser::InvalidArgument
156
+ option_error "Invalid argument"
157
+ rescue OptionParser::MissingArgument
158
+ option_error "Missing argument"
159
+ end
160
+ end # parse
161
+
162
+ private
163
+
164
+ def option_error(msg)
165
+ STDERR.print "Error: #{msg}\n\n #{@opt_parser}\n"
166
+ exit 1
167
+ end
168
+
169
+ end # class OptionsStruct