royw-railroad_xing 0.5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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