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.
- data/.gitignore +1 -0
- data/CHANGES.rdoc +17 -1
- data/Gemfile +3 -2
- data/Gemfile.lock +8 -4
- data/README.md +60 -0
- data/Rakefile +10 -50
- data/VERSION +1 -1
- data/lib/rails_erd.rb +28 -1
- data/lib/rails_erd/diagram.rb +66 -33
- data/lib/rails_erd/diagram/graphviz.rb +123 -92
- data/lib/rails_erd/diagram/templates/node.erb +2 -2
- data/lib/rails_erd/domain.rb +51 -23
- data/lib/rails_erd/domain/attribute.rb +102 -0
- data/lib/rails_erd/domain/entity.rb +102 -0
- data/lib/rails_erd/domain/relationship.rb +189 -0
- data/lib/rails_erd/domain/relationship/cardinality.rb +118 -0
- data/lib/rails_erd/domain/specialization.rb +58 -0
- data/lib/rails_erd/railtie.rb +1 -1
- data/rails-erd.gemspec +19 -16
- data/test/test_helper.rb +21 -5
- data/test/unit/attribute_test.rb +35 -8
- data/test/unit/cardinality_test.rb +41 -35
- data/test/unit/diagram_test.rb +130 -43
- data/test/unit/domain_test.rb +131 -8
- data/test/unit/entity_test.rb +150 -46
- data/test/unit/graphviz_test.rb +52 -14
- data/test/unit/rake_task_test.rb +2 -2
- data/test/unit/relationship_test.rb +73 -24
- data/test/unit/specialization_test.rb +57 -0
- metadata +15 -13
- data/README.rdoc +0 -51
- data/lib/rails_erd/attribute.rb +0 -95
- data/lib/rails_erd/entity.rb +0 -73
- data/lib/rails_erd/relationship.rb +0 -177
- data/lib/rails_erd/relationship/cardinality.rb +0 -118
@@ -19,13 +19,10 @@ end
|
|
19
19
|
module RailsERD
|
20
20
|
class Diagram
|
21
21
|
# Create Graphviz-based diagrams based on the domain model. For easy
|
22
|
-
# command line graph generation, you can use
|
22
|
+
# command line graph generation, you can use:
|
23
23
|
#
|
24
24
|
# % rake erd
|
25
25
|
#
|
26
|
-
# Please see the README.rdoc file for more details on how to use Rails ERD
|
27
|
-
# from the command line.
|
28
|
-
#
|
29
26
|
# === Options
|
30
27
|
#
|
31
28
|
# The following options are supported:
|
@@ -37,7 +34,7 @@ module RailsERD
|
|
37
34
|
# worse than a PDF file. The available formats depend on your installation
|
38
35
|
# of Graphviz.
|
39
36
|
# notation:: The cardinality notation to be used. Can be +:simple+ or
|
40
|
-
# +:
|
37
|
+
# +:bachman+. Refer to README.rdoc or to the examples on the project
|
41
38
|
# homepage for more information and examples.
|
42
39
|
# orientation:: The direction of the hierarchy of entities. Either +:horizontal+
|
43
40
|
# or +:vertical+. Defaults to +horizontal+. The orientation of the
|
@@ -60,9 +57,7 @@ module RailsERD
|
|
60
57
|
:concentrate => true,
|
61
58
|
:labelloc => :t,
|
62
59
|
:fontsize => 13,
|
63
|
-
:fontname => "Arial Bold"
|
64
|
-
:remincross => true,
|
65
|
-
:outputorder => :edgesfirst
|
60
|
+
:fontname => "Arial Bold"
|
66
61
|
}
|
67
62
|
|
68
63
|
# Default node attributes.
|
@@ -86,94 +81,139 @@ module RailsERD
|
|
86
81
|
:fontsize => 7
|
87
82
|
}
|
88
83
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
options[:arrowhead] = relationship.to_many? ? :normal : :none
|
94
|
-
options[:arrowtail] = relationship.many_to? ? :normal : :none
|
95
|
-
},
|
96
|
-
|
97
|
-
# Closed arrow for to/from many, UML ranges at each end.
|
98
|
-
:uml => lambda { |relationship, options|
|
99
|
-
options[:arrowsize] = 0.7
|
100
|
-
options[:arrowhead] = relationship.to_many? ? :vee : :none
|
101
|
-
options[:arrowtail] = relationship.many_to? ? :vee : :none
|
102
|
-
ranges = [relationship.cardinality.destination_range, relationship.cardinality.source_range].map do |range|
|
103
|
-
if range.min == range.max
|
104
|
-
"#{range.min}"
|
105
|
-
else
|
106
|
-
"#{range.min}..#{range.max == Relationship::Cardinality::Infinity ? "∗" : range.max}"
|
107
|
-
end
|
84
|
+
module Simple
|
85
|
+
def entity_style(entity, attributes)
|
86
|
+
{}.tap do |options|
|
87
|
+
options[:fontcolor] = options[:color] = :grey60 if entity.abstract?
|
108
88
|
end
|
109
|
-
|
110
|
-
},
|
89
|
+
end
|
111
90
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
src = relationship.source_optional? ? "odot" : "dot"
|
116
|
-
dst << "normal" if relationship.to_many?
|
117
|
-
src << "normal" if relationship.many_to?
|
118
|
-
options[:arrowsize] = 0.6
|
119
|
-
options[:arrowhead], options[:arrowtail] = dst, src
|
120
|
-
}
|
121
|
-
}
|
91
|
+
def relationship_style(relationship)
|
92
|
+
{}.tap do |options|
|
93
|
+
options[:style] = :dotted if relationship.indirect?
|
122
94
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
EDGE_ATTRIBUTES.each { |attribute, value| graph.edge[attribute] = value }
|
95
|
+
# Closed arrows for to/from many.
|
96
|
+
options[:arrowhead] = relationship.to_many? ? "normal" : "none"
|
97
|
+
options[:arrowtail] = relationship.many_to? ? "normal" : "none"
|
98
|
+
end
|
99
|
+
end
|
129
100
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
# Title of the graph itself.
|
134
|
-
graph[:label] = "#{title}\\n\\n" if title
|
101
|
+
def specialization_style(specialization)
|
102
|
+
{ :color => :grey60, :arrowtail => :onormal, :arrowhead => :none, :arrowsize => 1.2 }
|
135
103
|
end
|
136
104
|
end
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
raise "Saving diagram failed. Verify that Graphviz is installed or select filetype=dot."
|
144
|
-
end
|
105
|
+
|
106
|
+
module Bachman
|
107
|
+
include Simple
|
108
|
+
def relationship_style(relationship)
|
109
|
+
{}.tap do |options|
|
110
|
+
options[:style] = :dotted if relationship.indirect?
|
145
111
|
|
146
|
-
|
112
|
+
# Participation is "look-here".
|
113
|
+
dst = relationship.source_optional? ? "odot" : "dot"
|
114
|
+
src = relationship.destination_optional? ? "odot" : "dot"
|
147
115
|
|
148
|
-
|
149
|
-
|
116
|
+
# Cardinality is "look-across".
|
117
|
+
dst << "normal" if relationship.to_many?
|
118
|
+
src << "normal" if relationship.many_to?
|
119
|
+
options[:arrowsize] = 0.6
|
120
|
+
options[:arrowhead], options[:arrowtail] = dst, src
|
121
|
+
end
|
122
|
+
end
|
150
123
|
end
|
124
|
+
|
125
|
+
module Uml
|
126
|
+
include Simple
|
127
|
+
def relationship_style(relationship)
|
128
|
+
{}.tap do |options|
|
129
|
+
options[:style] = :dotted if relationship.indirect?
|
151
130
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
131
|
+
options[:arrowsize] = 0.7
|
132
|
+
options[:arrowhead] = relationship.to_many? ? "vee" : "none"
|
133
|
+
options[:arrowtail] = relationship.many_to? ? "vee" : "none"
|
156
134
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
135
|
+
ranges = [relationship.cardinality.destination_range, relationship.cardinality.source_range].map do |range|
|
136
|
+
if range.min == range.max
|
137
|
+
"#{range.min}"
|
138
|
+
else
|
139
|
+
"#{range.min}..#{range.max == Domain::Relationship::N ? "∗" : range.max}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
options[:headlabel], options[:taillabel] = *ranges
|
143
|
+
end
|
144
|
+
end
|
161
145
|
end
|
146
|
+
|
147
|
+
attr_accessor :graph
|
162
148
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
149
|
+
setup do
|
150
|
+
self.graph = GraphViz.digraph(domain.name)
|
151
|
+
|
152
|
+
# Set all default attributes.
|
153
|
+
GRAPH_ATTRIBUTES.each { |attribute, value| graph[attribute] = value }
|
154
|
+
NODE_ATTRIBUTES.each { |attribute, value| graph.node[attribute] = value }
|
155
|
+
EDGE_ATTRIBUTES.each { |attribute, value| graph.edge[attribute] = value }
|
156
|
+
|
157
|
+
# Switch rank direction if we're creating a vertically oriented graph.
|
158
|
+
graph[:rankdir] = :TB if options.orientation == :vertical
|
159
|
+
|
160
|
+
# Title of the graph itself.
|
161
|
+
graph[:label] = "#{title}\\n\\n" if title
|
162
|
+
|
163
|
+
# Setup notation options.
|
164
|
+
extend self.class.const_get(options.notation.to_s.capitalize.to_sym)
|
165
|
+
end
|
166
|
+
|
167
|
+
save do
|
168
|
+
raise "Saving diagram failed. Output directory '#{File.dirname(filename)}' does not exist." unless File.directory?(File.dirname(filename))
|
169
|
+
begin
|
170
|
+
graph.output(filetype => filename)
|
171
|
+
filename
|
172
|
+
rescue StandardError => e
|
173
|
+
raise "Saving diagram failed. Verify that Graphviz is installed or select filetype=dot."
|
174
|
+
end
|
167
175
|
end
|
168
176
|
|
177
|
+
each_entity do |entity, attributes|
|
178
|
+
draw_node entity.name, entity_options(entity, attributes)
|
179
|
+
end
|
180
|
+
|
181
|
+
each_specialization do |specialization|
|
182
|
+
from, to = specialization.generalized, specialization.specialized
|
183
|
+
draw_edge from.name, to.name, specialization_options(specialization)
|
184
|
+
end
|
185
|
+
|
186
|
+
each_relationship do |relationship|
|
187
|
+
from, to = relationship.source, relationship.destination
|
188
|
+
unless draw_edge from.name, to.name, relationship_options(relationship)
|
189
|
+
if from.children.any?
|
190
|
+
from.children.each do |child|
|
191
|
+
draw_edge child.name, to.name, relationship_options(relationship)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
169
197
|
private
|
170
198
|
|
199
|
+
def node_exists?(name)
|
200
|
+
!!graph.get_node(name)
|
201
|
+
end
|
202
|
+
|
203
|
+
def draw_node(name, options)
|
204
|
+
graph.add_node name, options
|
205
|
+
end
|
206
|
+
|
207
|
+
def draw_edge(from, to, options)
|
208
|
+
graph.add_edge graph.get_node(from), graph.get_node(to), options if node_exists?(from) and node_exists?(to)
|
209
|
+
end
|
210
|
+
|
171
211
|
# Returns the title to be used for the graph.
|
172
212
|
def title
|
173
213
|
case options.title
|
174
214
|
when false then nil
|
175
|
-
when true
|
176
|
-
if
|
215
|
+
when true
|
216
|
+
if domain.name then "#{domain.name} domain model" else "Domain model" end
|
177
217
|
else options.title
|
178
218
|
end
|
179
219
|
end
|
@@ -188,32 +228,23 @@ module RailsERD
|
|
188
228
|
if options.filetype.to_sym == :dot then :none else options.filetype.to_sym end
|
189
229
|
end
|
190
230
|
|
191
|
-
# Returns an options hash based on the given entity and its attributes.
|
192
231
|
def entity_options(entity, attributes)
|
193
|
-
|
232
|
+
entity_style(entity, attributes).merge :label => "<#{NODE_LABEL_TEMPLATE.result(binding)}>"
|
194
233
|
end
|
195
234
|
|
196
|
-
# Returns an options hash
|
197
235
|
def relationship_options(relationship)
|
198
|
-
|
236
|
+
relationship_style(relationship).tap do |options|
|
199
237
|
# Edges with a higher weight are optimised to be shorter and straighter.
|
200
|
-
|
238
|
+
options[:weight] = relationship.strength
|
201
239
|
|
202
240
|
# Indirect relationships should not influence node ranks.
|
203
|
-
|
241
|
+
options[:constraint] = false if relationship.indirect?
|
204
242
|
end
|
205
243
|
end
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
def relationship_style_options(relationship)
|
210
|
-
{}.tap do |opts|
|
211
|
-
opts[:style] = :dotted if relationship.indirect?
|
212
|
-
|
213
|
-
# Let cardinality style callbacks draw arrow heads and tails.
|
214
|
-
CARDINALITY_STYLES[options.notation][relationship, opts]
|
215
|
-
end
|
244
|
+
|
245
|
+
def specialization_options(specialization)
|
246
|
+
specialization_style(specialization)
|
216
247
|
end
|
217
248
|
end
|
218
249
|
end
|
219
|
-
end
|
250
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<% if vertical
|
1
|
+
<% if options.orientation == :vertical %>{<% end %>
|
2
2
|
<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="<%= NODE_WIDTH + 4 %>">
|
3
3
|
<tr><td align="center" valign="bottom" width="<%= NODE_WIDTH %>"><font face="Arial Bold" point-size="11"><%= entity.name %></font></td></tr>
|
4
4
|
</table>
|
@@ -11,4 +11,4 @@
|
|
11
11
|
</table>
|
12
12
|
<% else %>
|
13
13
|
<% end %>
|
14
|
-
<% if vertical
|
14
|
+
<% if options.orientation == :vertical %>}<% end %>
|
data/lib/rails_erd/domain.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require "set"
|
2
1
|
require "rails_erd"
|
3
|
-
require "rails_erd/
|
4
|
-
require "rails_erd/
|
5
|
-
require "rails_erd/
|
2
|
+
require "rails_erd/domain/attribute"
|
3
|
+
require "rails_erd/domain/entity"
|
4
|
+
require "rails_erd/domain/relationship"
|
5
|
+
require "rails_erd/domain/specialization"
|
6
6
|
|
7
7
|
module RailsERD
|
8
8
|
# The domain describes your Rails domain model. This class is the starting
|
@@ -28,13 +28,16 @@ module RailsERD
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
extend Inspectable
|
32
|
+
inspection_attributes
|
33
|
+
|
31
34
|
# The options that are used to generate this domain model.
|
32
35
|
attr_reader :options
|
33
36
|
|
34
37
|
# Create a new domain model object based on the given array of models.
|
35
38
|
# The given models are assumed to be subclasses of <tt>ActiveRecord::Base</tt>.
|
36
39
|
def initialize(models = [], options = {})
|
37
|
-
@
|
40
|
+
@source_models, @options = models, RailsERD.options.merge(options)
|
38
41
|
end
|
39
42
|
|
40
43
|
# Returns the domain model name, which is the name of your Rails
|
@@ -45,7 +48,7 @@ module RailsERD
|
|
45
48
|
|
46
49
|
# Returns all entities of your domain model.
|
47
50
|
def entities
|
48
|
-
@entities ||= Entity.from_models(self,
|
51
|
+
@entities ||= Entity.from_models(self, models)
|
49
52
|
end
|
50
53
|
|
51
54
|
# Returns all relationships in your domain model.
|
@@ -53,19 +56,23 @@ module RailsERD
|
|
53
56
|
@relationships ||= Relationship.from_associations(self, associations)
|
54
57
|
end
|
55
58
|
|
59
|
+
# Returns all specializations in your domain model.
|
60
|
+
def specializations
|
61
|
+
@specializations ||= Specialization.from_models(self, models)
|
62
|
+
end
|
63
|
+
|
56
64
|
# Returns a specific entity object for the given Active Record model.
|
57
|
-
def
|
58
|
-
entity_mapping[
|
65
|
+
def entity_by_name(name) # @private :nodoc:
|
66
|
+
entity_mapping[name]
|
59
67
|
end
|
60
68
|
|
61
69
|
# Returns an array of relationships for the given Active Record model.
|
62
|
-
def
|
63
|
-
relationships_mapping[
|
70
|
+
def relationships_by_entity_name(name) # @private :nodoc:
|
71
|
+
relationships_mapping[name] or []
|
64
72
|
end
|
65
|
-
|
66
|
-
def
|
67
|
-
|
68
|
-
[object_id << 1, relationships.map { |rel| "#{rel.source} => #{rel.destination}" } * ", "]
|
73
|
+
|
74
|
+
def specializations_by_entity_name(name)
|
75
|
+
specializations_mapping[name] or []
|
69
76
|
end
|
70
77
|
|
71
78
|
def warn(message) # @private :nodoc:
|
@@ -77,7 +84,7 @@ module RailsERD
|
|
77
84
|
def entity_mapping
|
78
85
|
@entity_mapping ||= {}.tap do |mapping|
|
79
86
|
entities.each do |entity|
|
80
|
-
mapping[entity.
|
87
|
+
mapping[entity.name] = entity
|
81
88
|
end
|
82
89
|
end
|
83
90
|
end
|
@@ -85,25 +92,46 @@ module RailsERD
|
|
85
92
|
def relationships_mapping
|
86
93
|
@relationships_mapping ||= {}.tap do |mapping|
|
87
94
|
relationships.each do |relationship|
|
88
|
-
(mapping[relationship.source.
|
89
|
-
(mapping[relationship.destination.
|
95
|
+
(mapping[relationship.source.name] ||= []) << relationship
|
96
|
+
(mapping[relationship.destination.name] ||= []) << relationship
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def specializations_mapping
|
102
|
+
@specializations_mapping ||= {}.tap do |mapping|
|
103
|
+
specializations.each do |specialization|
|
104
|
+
(mapping[specialization.generalized.name] ||= []) << specialization
|
105
|
+
(mapping[specialization.specialized.name] ||= []) << specialization
|
90
106
|
end
|
91
107
|
end
|
92
108
|
end
|
93
109
|
|
110
|
+
def models
|
111
|
+
@models ||= @source_models.reject(&:abstract_class?).select { |model| check_model_validity(model) }
|
112
|
+
end
|
113
|
+
|
94
114
|
def associations
|
95
|
-
@associations ||=
|
115
|
+
@associations ||= models.collect(&:reflect_on_all_associations).flatten.select { |assoc| check_association_validity(assoc) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_model_validity(model)
|
119
|
+
model.table_exists? or raise "table #{model.table_name} does not exist"
|
120
|
+
rescue => e
|
121
|
+
warn "Ignoring invalid model #{model.name} (#{e.message})"
|
96
122
|
end
|
97
123
|
|
98
124
|
def check_association_validity(association)
|
99
125
|
# Raises an ActiveRecord::ActiveRecordError if the association is broken.
|
100
126
|
association.check_validity!
|
101
127
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
128
|
+
if association.options[:polymorphic]
|
129
|
+
entity_name = association.class_name
|
130
|
+
entity_by_name(entity_name) or raise "polymorphic interface #{entity_name} does not exist"
|
131
|
+
else
|
132
|
+
entity_name = association.klass.name # Raises NameError if the associated class cannot be found.
|
133
|
+
entity_by_name(entity_name) or raise "model #{entity_name} exists, but is not included in domain"
|
134
|
+
end
|
107
135
|
rescue => e
|
108
136
|
warn "Ignoring invalid association #{association_description(association)} (#{e.message})"
|
109
137
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module RailsERD
|
3
|
+
class Domain
|
4
|
+
# Describes an entity's attribute. Attributes correspond directly to
|
5
|
+
# database columns.
|
6
|
+
class Attribute
|
7
|
+
TIMESTAMP_NAMES = %w{created_at created_on updated_at updated_on} # @private :nodoc:
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def from_model(domain, model) # @private :nodoc:
|
11
|
+
model.columns.collect { |column| new(domain, model, column) }.sort
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
extend Inspectable
|
16
|
+
inspection_attributes :name, :type
|
17
|
+
|
18
|
+
attr_reader :column # @private :nodoc:
|
19
|
+
|
20
|
+
def initialize(domain, model, column) # @private :nodoc:
|
21
|
+
@domain, @model, @column = domain, model, column
|
22
|
+
end
|
23
|
+
|
24
|
+
# The name of the attribute, equal to the column name.
|
25
|
+
def name
|
26
|
+
column.name
|
27
|
+
end
|
28
|
+
|
29
|
+
# The type of the attribute, equal to the Rails migration type. Can be any
|
30
|
+
# of +:string+, +:integer+, +:boolean+, +:text+, etc.
|
31
|
+
def type
|
32
|
+
column.type
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns +true+ if this attribute is a content column, that is, if it
|
36
|
+
# is not a primary key, foreign key, timestamp, or inheritance column.
|
37
|
+
def content?
|
38
|
+
!primary_key? and !foreign_key? and !timestamp? and !inheritance?
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns +true+ if this attribute is mandatory. Mandatory attributes
|
42
|
+
# either have a presence validation (+validates_presence_of+), or have a
|
43
|
+
# <tt>NOT NULL</tt> database constraint.
|
44
|
+
def mandatory?
|
45
|
+
!column.null or @model.validators_on(name).map(&:kind).include?(:presence)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns +true+ if this attribute is the primary key of the entity.
|
49
|
+
def primary_key?
|
50
|
+
column.primary
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns +true+ if this attribute is used as a foreign key for any
|
54
|
+
# relationship.
|
55
|
+
def foreign_key?
|
56
|
+
@domain.relationships_by_entity_name(@model.name).map(&:associations).flatten.map(&:primary_key_name).include?(name)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns +true+ if this attribute is used for single table inheritance.
|
60
|
+
# These attributes are typically named +type+.
|
61
|
+
def inheritance?
|
62
|
+
@model.inheritance_column == name
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns +true+ if this attribute is one of the standard 'magic' Rails
|
66
|
+
# timestamp columns, being +created_at+, +updated_at+, +created_on+ or
|
67
|
+
# +updated_on+.
|
68
|
+
def timestamp?
|
69
|
+
TIMESTAMP_NAMES.include? name
|
70
|
+
end
|
71
|
+
|
72
|
+
def <=>(other) # @private :nodoc:
|
73
|
+
name <=> other.name
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s # @private :nodoc:
|
77
|
+
name
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns a description of the attribute type. If the attribute has
|
81
|
+
# a non-standard limit or if it is mandatory, this information is included.
|
82
|
+
#
|
83
|
+
# Example output:
|
84
|
+
# <tt>:integer</tt>:: integer
|
85
|
+
# <tt>:string, :limit => 255</tt>:: string
|
86
|
+
# <tt>:string, :limit => 128</tt>:: string (128)
|
87
|
+
# <tt>:boolean, :null => false</tt>:: boolean *
|
88
|
+
def type_description
|
89
|
+
type.to_s.tap do |desc|
|
90
|
+
desc << " (#{limit})" if limit
|
91
|
+
desc << " ∗" if mandatory? # Add a hair space + low asterisk (Unicode characters).
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns any non-standard limit for this attribute. If a column has no
|
96
|
+
# limit or uses a default database limit, this method returns +nil+.
|
97
|
+
def limit
|
98
|
+
column.limit if column.limit != @model.connection.native_database_types[type][:limit]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|