rails-erd 0.3.0 → 0.4.0

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.
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
7
+ - 4
8
8
  - 0
9
- version: 0.3.0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Rolf Timmermans
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-10-03 00:00:00 +02:00
17
+ date: 2010-10-12 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -28,8 +28,7 @@ dependencies:
28
28
  segments:
29
29
  - 3
30
30
  - 0
31
- - 0
32
- version: 3.0.0
31
+ version: "3.0"
33
32
  type: :runtime
34
33
  version_requirements: *id001
35
34
  - !ruby/object:Gem::Dependency
@@ -57,8 +56,8 @@ dependencies:
57
56
  segments:
58
57
  - 0
59
58
  - 9
60
- - 17
61
- version: 0.9.17
59
+ - 18
60
+ version: 0.9.18
62
61
  type: :runtime
63
62
  version_requirements: *id003
64
63
  - !ruby/object:Gem::Dependency
@@ -82,27 +81,28 @@ extensions: []
82
81
 
83
82
  extra_rdoc_files:
84
83
  - LICENSE
85
- - README.rdoc
84
+ - README.md
86
85
  files:
87
86
  - .gitignore
88
87
  - CHANGES.rdoc
89
88
  - Gemfile
90
89
  - Gemfile.lock
91
90
  - LICENSE
92
- - README.rdoc
91
+ - README.md
93
92
  - Rakefile
94
93
  - VERSION
95
94
  - lib/rails-erd.rb
96
95
  - lib/rails_erd.rb
97
- - lib/rails_erd/attribute.rb
98
96
  - lib/rails_erd/diagram.rb
99
97
  - lib/rails_erd/diagram/graphviz.rb
100
98
  - lib/rails_erd/diagram/templates/node.erb
101
99
  - lib/rails_erd/domain.rb
102
- - lib/rails_erd/entity.rb
100
+ - lib/rails_erd/domain/attribute.rb
101
+ - lib/rails_erd/domain/entity.rb
102
+ - lib/rails_erd/domain/relationship.rb
103
+ - lib/rails_erd/domain/relationship/cardinality.rb
104
+ - lib/rails_erd/domain/specialization.rb
103
105
  - lib/rails_erd/railtie.rb
104
- - lib/rails_erd/relationship.rb
105
- - lib/rails_erd/relationship/cardinality.rb
106
106
  - lib/rails_erd/tasks.rake
107
107
  - rails-erd.gemspec
108
108
  - test/test_helper.rb
@@ -114,6 +114,7 @@ files:
114
114
  - test/unit/graphviz_test.rb
115
115
  - test/unit/rake_task_test.rb
116
116
  - test/unit/relationship_test.rb
117
+ - test/unit/specialization_test.rb
117
118
  has_rdoc: true
118
119
  homepage: http://rails-erd.rubyforge.org/
119
120
  licenses: []
@@ -156,3 +157,4 @@ test_files:
156
157
  - test/unit/graphviz_test.rb
157
158
  - test/unit/rake_task_test.rb
158
159
  - test/unit/relationship_test.rb
160
+ - test/unit/specialization_test.rb
data/README.rdoc DELETED
@@ -1,51 +0,0 @@
1
- = Rails ERD - Generate Entity-Relationship Diagrams for Rails applications
2
-
3
- {Rails ERD}[http://rails-erd.rubyforge.org/] is a Rails plugin that allows
4
- you to easily generate a diagram based on your Active Record models. The
5
- diagram gives an overview of how your models are related. Having a diagram
6
- that describes your models is perfect documentation for your application.
7
-
8
- The second goal of Rails ERD is to provide you with a tool to inspect your
9
- application's domain model. If you don't like the default output, it is very
10
- easy to use the API to build your own diagrams.
11
-
12
- Rails ERD was created specifically for Rails 3. It uses Active Record's
13
- built-in reflection capabilities to figure out how your models are associated.
14
-
15
- == Preview
16
-
17
- Here's an example entity-relationship diagram that was generated by Rails ERD:
18
-
19
- http://rails-erd.rubyforge.org/examples/event-forms.png
20
-
21
- == Learn more
22
-
23
- Homepage:
24
- http://rails-erd.rubyforge.org/
25
-
26
- Diagram gallery:
27
- http://rails-erd.rubyforge.org/gallery.html
28
-
29
- Installation instructions:
30
- http://rails-erd.rubyforge.org/install.html
31
-
32
- Internal API documentation:
33
- http://rails-erd.rubyforge.org/doc/
34
-
35
- Source code at Github:
36
- http://github.com/voormedia/rails-erd
37
-
38
- == About Rails ERD
39
-
40
- Author: Rolf Timmermans (r.timmermans <i>at</i> voormedia.com)
41
-
42
- Copyright 2010 Voormedia B.V.
43
-
44
- == License
45
-
46
- Rails ERD is released under the MIT license. See the LICENSE.
47
-
48
- == Credits
49
-
50
- Rails ERD depends on the Ruby-Graphviz library to generate diagrams:
51
- http://github.com/glejeune/Ruby-Graphviz/
@@ -1,95 +0,0 @@
1
- # -*- encoding: utf-8
2
- module RailsERD
3
- # Describes an entity's attribute. Attributes correspond directly to
4
- # database columns.
5
- class Attribute
6
- TIMESTAMP_NAMES = %w{created_at created_on updated_at updated_on} # @private :nodoc:
7
-
8
- class << self
9
- def from_model(domain, model) # @private :nodoc:
10
- model.arel_table.columns.collect { |column| Attribute.new(domain, model, column) }.sort
11
- end
12
- end
13
-
14
- attr_reader :column # @private :nodoc:
15
-
16
- def initialize(domain, model, column) # @private :nodoc:
17
- @domain, @model, @column = domain, model, column
18
- end
19
-
20
- # The name of the attribute, equal to the column name.
21
- def name
22
- column.name
23
- end
24
-
25
- # The type of the attribute, equal to the Rails migration type. Can be any
26
- # of +:string+, +:integer+, +:boolean+, +:text+, etc.
27
- def type
28
- column.type
29
- end
30
-
31
- # Returns +true+ if this attribute has no special meaning, that is, if it
32
- # is not a primary key, foreign key, or timestamp.
33
- def regular?
34
- !primary_key? and !foreign_key? and !timestamp?
35
- end
36
-
37
- # Returns +true+ if this attribute is mandatory. Mandatory attributes
38
- # either have a presence validation (+validates_presence_of+), or have a
39
- # <tt>NOT NULL</tt> database constraint.
40
- def mandatory?
41
- !column.null or @model.validators_on(name).map(&:kind).include?(:presence)
42
- end
43
-
44
- # Returns +true+ if this attribute is the primary key of the entity.
45
- def primary_key?
46
- @model.arel_table.primary_key == name
47
- end
48
-
49
- # Returns +true+ if this attribute is used as a foreign key for any
50
- # relationship.
51
- def foreign_key?
52
- @domain.relationships_for(@model).map(&:associations).flatten.map(&:primary_key_name).include?(name)
53
- end
54
-
55
- # Returns +true+ if this attribute is one of the standard 'magic' Rails
56
- # timestamp columns, being +created_at+, +updated_at+, +created_on+ or
57
- # +updated_on+.
58
- def timestamp?
59
- TIMESTAMP_NAMES.include? name
60
- end
61
-
62
- def <=>(other) # @private :nodoc:
63
- name <=> other.name
64
- end
65
-
66
- def inspect # @private :nodoc:
67
- "#<#{self.class.name}:0x%.14x @column=#{name.inspect} @type=#{type.inspect}>" % (object_id << 1)
68
- end
69
-
70
- def to_s # @private :nodoc:
71
- name
72
- end
73
-
74
- # Returns a description of the attribute type. If the attribute has
75
- # a non-standard limit or if it is mandatory, this information is included.
76
- #
77
- # Example output:
78
- # <tt>:integer</tt>:: integer
79
- # <tt>:string, :limit => 255</tt>:: string
80
- # <tt>:string, :limit => 128</tt>:: string (128)
81
- # <tt>:boolean, :null => false</tt>:: boolean *
82
- def type_description
83
- type.to_s.tap do |desc|
84
- desc << " (#{limit})" if limit
85
- desc << " ∗" if mandatory? # Add a hair space + low asterisk (Unicode characters).
86
- end
87
- end
88
-
89
- # Returns any non-standard limit for this attribute. If a column has no
90
- # limit or uses a default database limit, this method returns +nil+.
91
- def limit
92
- column.limit if column.limit != @model.connection.native_database_types[type][:limit]
93
- end
94
- end
95
- end
@@ -1,73 +0,0 @@
1
- module RailsERD
2
- # Entities represent your Active Record models. Entities may be connected
3
- # to other entities.
4
- class Entity
5
- class << self
6
- def from_models(domain, models) # @private :nodoc:
7
- models.collect { |model| new domain, model }.sort
8
- end
9
- end
10
-
11
- # The domain in which this entity resides.
12
- attr_reader :domain
13
-
14
- # The Active Record model that this entity corresponds to.
15
- attr_reader :model
16
-
17
- def initialize(domain, model) # @private :nodoc:
18
- @domain, @model = domain, model
19
- end
20
-
21
- # Returns an array of attributes for this entity.
22
- def attributes
23
- @attributes ||= Attribute.from_model @domain, @model
24
- end
25
-
26
- # Returns an array of all relationships that this entity has with other
27
- # entities in the domain model.
28
- def relationships
29
- @domain.relationships_for(@model)
30
- end
31
-
32
- # Returns the parent entity, if this entity is a descendant.
33
- def parent
34
- @domain.entity_for(@model.superclass) if descendant?
35
- end
36
-
37
- # Returns +true+ if this entity has any relationships with other models,
38
- # +false+ otherwise.
39
- def connected?
40
- relationships.any?
41
- end
42
-
43
- # Returns +true+ if this entity has no relationships with any other models,
44
- # +false+ otherwise. Opposite of +connected?+.
45
- def disconnected?
46
- relationships.none?
47
- end
48
-
49
- # Returns +true+ if this entity descends from another entity, and is
50
- # represented in the same table as its parent.
51
- def descendant?
52
- !@model.descends_from_active_record?
53
- end
54
-
55
- # Returns the name of this entity, which is the class name of the
56
- # corresponding model.
57
- def name
58
- model.name
59
- end
60
-
61
- def inspect # @private :nodoc:
62
- "#<#{self.class}:0x%.14x @model=#{name}>" % (object_id << 1)
63
- end
64
-
65
- def to_s # @private :nodoc:
66
- name
67
- end
68
-
69
- def <=>(other) # @private :nodoc:
70
- self.name <=> other.name
71
- end
72
- end
73
- end
@@ -1,177 +0,0 @@
1
- require "rails_erd/relationship/cardinality"
2
-
3
- module RailsERD
4
- # Describes a relationship between two entities. A relationship is detected
5
- # based on Active Record associations. One relationship may represent more
6
- # than one association, however. Related associations are grouped together.
7
- # Associations are related if they share the same foreign key, or the same
8
- # join table in the case of many-to-many associations.
9
- class Relationship
10
- N = Cardinality::N
11
-
12
- class << self
13
- def from_associations(domain, associations) # @private :nodoc:
14
- assoc_groups = associations.group_by { |assoc| association_identity(assoc) }
15
- assoc_groups.collect { |_, assoc_group| Relationship.new(domain, assoc_group.to_a) }
16
- end
17
-
18
- private
19
-
20
- def association_identity(assoc)
21
- identifier = assoc.options[:join_table] || assoc.primary_key_name.to_s
22
- Set[identifier, assoc.active_record, assoc.klass]
23
- end
24
- end
25
-
26
- # The domain in which this relationship is defined.
27
- attr_reader :domain
28
-
29
- # The source entity. It corresponds to the model that has defined a
30
- # +has_one+ or +has_many+ association with the other model.
31
- attr_reader :source
32
-
33
- # The destination entity. It corresponds to the model that has defined
34
- # a +belongs_to+ association with the other model.
35
- attr_reader :destination
36
-
37
- delegate :one_to_one?, :one_to_many?, :many_to_many?, :source_optional?,
38
- :destination_optional?, :to => :cardinality
39
-
40
- def initialize(domain, associations) # @private :nodoc:
41
- @domain = domain
42
- @reverse_associations, @forward_associations = *unless any_habtm?(associations)
43
- associations.partition(&:belongs_to?)
44
- else
45
- # Many-to-many associations don't have a clearly defined direction.
46
- # We sort by name and use the first model as the source.
47
- source = associations.first.active_record
48
- associations.partition { |association| association.active_record == source }
49
- end
50
-
51
- assoc = @forward_associations.first || @reverse_associations.first
52
- @source, @destination = @domain.entity_for(assoc.active_record), @domain.entity_for(assoc.klass)
53
- @source, @destination = @destination, @source if assoc.belongs_to?
54
- end
55
-
56
- # Returns all Active Record association objects that describe this
57
- # relationship.
58
- def associations
59
- @forward_associations + @reverse_associations
60
- end
61
-
62
- # Returns the cardinality of this relationship.
63
- def cardinality
64
- @cardinality ||= begin
65
- reverse_max = any_habtm?(associations) ? N : 1
66
- forward_range = associations_range(@source.model, @forward_associations, N)
67
- reverse_range = associations_range(@destination.model, @reverse_associations, reverse_max)
68
- Cardinality.new(reverse_range, forward_range)
69
- end
70
- end
71
-
72
- # Indicates if a relationship is indirect, that is, if it is defined
73
- # through other relationships. Indirect relationships are created in
74
- # Rails with <tt>has_many :through</tt> or <tt>has_one :through</tt>
75
- # association macros.
76
- def indirect?
77
- !@forward_associations.empty? and @forward_associations.all?(&:through_reflection)
78
- end
79
-
80
- # Indicates whether or not the relationship is defined by two inverse
81
- # associations (e.g. a +has_many+ and a corresponding +belongs_to+
82
- # association).
83
- def mutual?
84
- @forward_associations.any? and @reverse_associations.any?
85
- end
86
-
87
- # Indicates whether or not this relationship connects an entity with itself.
88
- def recursive?
89
- @source == @destination
90
- end
91
-
92
- # Indicates whether the destination cardinality class of this relationship
93
- # is equal to one. This is +true+ for one-to-one relationships only.
94
- def to_one?
95
- cardinality.cardinality_class[1] == 1
96
- end
97
-
98
- # Indicates whether the destination cardinality class of this relationship
99
- # is equal to infinity. This is +true+ for one-to-many or
100
- # many-to-many relationships only.
101
- def to_many?
102
- cardinality.cardinality_class[1] != 1
103
- end
104
-
105
- # Indicates whether the source cardinality class of this relationship
106
- # is equal to one. This is +true+ for one-to-one or
107
- # one-to-many relationships only.
108
- def one_to?
109
- cardinality.cardinality_class[0] == 1
110
- end
111
-
112
- # Indicates whether the source cardinality class of this relationship
113
- # is equal to infinity. This is +true+ for many-to-many relationships only.
114
- def many_to?
115
- cardinality.cardinality_class[0] != 1
116
- end
117
-
118
- # The strength of a relationship is equal to the number of associations
119
- # that describe it.
120
- def strength
121
- associations.size
122
- end
123
-
124
- def inspect # @private :nodoc:
125
- "#<#{self.class}:0x%.14x @source=#{source} @destination=#{destination}>" % (object_id << 1)
126
- end
127
-
128
- def <=>(other) # @private :nodoc:
129
- (source.name <=> other.source.name).nonzero? or (destination.name <=> other.destination.name)
130
- end
131
-
132
- private
133
-
134
- def associations_range(model, associations, absolute_max)
135
- # The minimum of the range is the maximum value of each association
136
- # minimum. If there is none, it is zero by definition. The reasoning is
137
- # that from all associations, if only one has a required minimum, then
138
- # this side of the relationship has a cardinality of at least one.
139
- min = associations.map { |assoc| association_minimum(model, assoc) }.max || 0
140
-
141
- # The maximum of the range is the maximum value of each association
142
- # maximum. If there is none, it is equal to the absolute maximum. If
143
- # only one association has a high cardinality on this side, the
144
- # relationship itself has the same maximum cardinality.
145
- max = associations.map { |assoc| association_maximum(model, assoc) }.max || absolute_max
146
-
147
- min..max
148
- end
149
-
150
- def association_minimum(model, association)
151
- minimum = association_validators(:presence, model, association).any? ||
152
- foreign_key_required?(model, association) ? 1 : 0
153
- length_validators = association_validators(:length, model, association)
154
- length_validators.map { |v| v.options[:minimum] }.compact.max or minimum
155
- end
156
-
157
- def association_maximum(model, association)
158
- maximum = association.collection? ? N : 1
159
- length_validators = association_validators(:length, model, association)
160
- length_validators.map { |v| v.options[:maximum] }.compact.min or maximum
161
- end
162
-
163
- def association_validators(kind, model, association)
164
- model.validators_on(association.name).select { |v| v.kind == kind }
165
- end
166
-
167
- def any_habtm?(associations)
168
- associations.any? { |association| association.macro == :has_and_belongs_to_many }
169
- end
170
-
171
- def foreign_key_required?(model, association)
172
- if association.belongs_to?
173
- key = model.arel_table.columns.find { |column| column.name == association.primary_key_name } and !key.null
174
- end
175
- end
176
- end
177
- end