jw-rails-erd 1.4.5

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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +86 -0
  3. data/Rakefile +20 -0
  4. data/bin/erd +4 -0
  5. data/lib/generators/erd/USAGE +4 -0
  6. data/lib/generators/erd/install_generator.rb +14 -0
  7. data/lib/generators/erd/templates/auto_generate_diagram.rake +6 -0
  8. data/lib/rails-erd.rb +1 -0
  9. data/lib/rails_erd/cli.rb +164 -0
  10. data/lib/rails_erd/config.rb +97 -0
  11. data/lib/rails_erd/custom.rb +99 -0
  12. data/lib/rails_erd/diagram/graphviz.rb +295 -0
  13. data/lib/rails_erd/diagram/templates/node.html.erb +14 -0
  14. data/lib/rails_erd/diagram/templates/node.record.erb +4 -0
  15. data/lib/rails_erd/diagram.rb +188 -0
  16. data/lib/rails_erd/domain/attribute.rb +160 -0
  17. data/lib/rails_erd/domain/entity.rb +104 -0
  18. data/lib/rails_erd/domain/relationship/cardinality.rb +118 -0
  19. data/lib/rails_erd/domain/relationship.rb +203 -0
  20. data/lib/rails_erd/domain/specialization.rb +90 -0
  21. data/lib/rails_erd/domain.rb +153 -0
  22. data/lib/rails_erd/railtie.rb +10 -0
  23. data/lib/rails_erd/tasks.rake +58 -0
  24. data/lib/rails_erd/version.rb +4 -0
  25. data/lib/rails_erd.rb +73 -0
  26. data/lib/tasks/auto_generate_diagram.rake +21 -0
  27. data/test/support_files/erdconfig.another_example +3 -0
  28. data/test/support_files/erdconfig.example +19 -0
  29. data/test/support_files/erdconfig.exclude.example +19 -0
  30. data/test/test_helper.rb +160 -0
  31. data/test/unit/attribute_test.rb +316 -0
  32. data/test/unit/cardinality_test.rb +123 -0
  33. data/test/unit/config_test.rb +110 -0
  34. data/test/unit/diagram_test.rb +352 -0
  35. data/test/unit/domain_test.rb +258 -0
  36. data/test/unit/entity_test.rb +252 -0
  37. data/test/unit/graphviz_test.rb +461 -0
  38. data/test/unit/rake_task_test.rb +174 -0
  39. data/test/unit/relationship_test.rb +476 -0
  40. data/test/unit/specialization_test.rb +67 -0
  41. metadata +155 -0
@@ -0,0 +1,58 @@
1
+ def say(message)
2
+ puts message unless Rake.application.options.silent
3
+ end
4
+
5
+ namespace :erd do
6
+ task :options do
7
+ (RailsERD.options.keys.map(&:to_s) & ENV.keys).each do |option|
8
+ RailsERD.options[option.to_sym] = case ENV[option]
9
+ when "true", "yes" then true
10
+ when "false", "no" then false
11
+ when /,/ then ENV[option].split(/\s*,\s*/)
12
+ else ENV[option].to_sym
13
+ end
14
+ end
15
+ end
16
+
17
+ task :load_models do
18
+ say "Loading application environment..."
19
+ Rake::Task[:environment].invoke
20
+
21
+ say "Loading code in search of Active Record models..."
22
+ begin
23
+ Rails.application.eager_load!
24
+ rescue Exception => err
25
+ if Rake.application.options.trace
26
+ raise
27
+ else
28
+ trace = Rails.backtrace_cleaner.clean(err.backtrace)
29
+ error = (["Loading models failed!\nError occurred while loading application: #{err} (#{err.class})"] + trace).join("\n ")
30
+ raise error
31
+ end
32
+ end
33
+
34
+ raise "Active Record was not loaded." unless defined? ActiveRecord
35
+ end
36
+
37
+ task :generate => [:options, :load_models] do
38
+ say "Generating Entity-Relationship Diagram for #{ActiveRecord::Base.descendants.length} models..."
39
+
40
+ require "rails_erd/diagram/graphviz"
41
+ file = RailsERD::Diagram::Graphviz.create
42
+
43
+ say "Done! Saved diagram to #{file}."
44
+ end
45
+ end
46
+
47
+ desc "Generate an Entity-Relationship Diagram based on your models"
48
+ task :erd => "erd:generate", :arg1, :arg2, :arg3 do |t, args|
49
+ say "Usage: rake erd <classname> <outputfile> <traversal depth>"
50
+ if args[0] == nil
51
+ say "Required field: Root Class node to start with"
52
+ end
53
+ if args[1] == nil
54
+ say "Required field: output file name"
55
+ end
56
+ args[2] ||= 1
57
+ RailsERD::Custom.new(args[0], args[1], args[2])
58
+ end
@@ -0,0 +1,4 @@
1
+ module RailsERD
2
+ VERSION = "1.4.5"
3
+ BANNER = "RailsERD #{VERSION}"
4
+ end
data/lib/rails_erd.rb ADDED
@@ -0,0 +1,73 @@
1
+ require "active_support/ordered_options"
2
+ require "rails_erd/railtie" if defined? Rails
3
+ require "rails_erd/config"
4
+
5
+ # Welcome to the API documentation of Rails ERD. If you wish to extend or
6
+ # customise the output that is generated by Rails ERD, you have come to the
7
+ # right place.
8
+ #
9
+ # == Creating custom output
10
+ #
11
+ # If you want to create your own kind of diagrams, or some other output, a
12
+ # good starting point is the RailsERD::Diagram class. It can serve as the base
13
+ # of your output generation code.
14
+ #
15
+ # == Options
16
+ #
17
+ # Rails ERD provides several options that allow you to customise the
18
+ # generation of the diagram and the domain model itself. For an overview of
19
+ # all options available in Rails ERD, see README.rdoc.
20
+ #
21
+ # You can specify the option on the command line if you use Rails ERD with
22
+ # Rake:
23
+ #
24
+ # % rake erd orientation=vertical title='My model diagram'
25
+ #
26
+ # When using Rails ERD from within Ruby, you can set the options on the
27
+ # RailsERD namespace module:
28
+ #
29
+ # RailsERD.options.orientation = :vertical
30
+ # RailsERD.options.title = "My model diagram"
31
+ module RailsERD
32
+ class << self
33
+ # Access to default options. Any instance of RailsERD::Domain and
34
+ # RailsERD::Diagram will use these options unless overridden.
35
+ attr_accessor :options
36
+
37
+ def default_options
38
+ ActiveSupport::OrderedOptions[
39
+ :attributes, :content,
40
+ :disconnected, true,
41
+ :filename, "erd",
42
+ :filetype, :pdf,
43
+ :indirect, true,
44
+ :inheritance, false,
45
+ :markup, true,
46
+ :notation, :simple,
47
+ :orientation, :horizontal,
48
+ :polymorphism, false,
49
+ :sort, true,
50
+ :warn, true,
51
+ :title, true,
52
+ :exclude, nil,
53
+ :only, nil,
54
+ :prepend_primary, false
55
+ ]
56
+ end
57
+ end
58
+
59
+ module Inspectable # @private :nodoc:
60
+ def inspection_attributes(*attributes)
61
+ attribute_inspection = attributes.collect { |attribute|
62
+ " @#{attribute}=\#{[Symbol, String].include?(#{attribute}.class) ? #{attribute}.inspect : #{attribute}}"
63
+ }.join
64
+ class_eval <<-RUBY
65
+ def inspect
66
+ "#<\#{self.class}:0x%.14x#{attribute_inspection}>" % (object_id << 1)
67
+ end
68
+ RUBY
69
+ end
70
+ end
71
+
72
+ self.options = default_options.merge(Config.load)
73
+ end
@@ -0,0 +1,21 @@
1
+ namespace :db do
2
+ task :migrate do
3
+ ERDGraph::Migration.update_model
4
+ end
5
+
6
+ namespace :migrate do
7
+ [:change, :up, :down, :reset, :redo].each do |t|
8
+ task t do
9
+ ERDGraph::Migration.update_model
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ module ERDGraph
16
+ class Migration
17
+ def self.update_model
18
+ Rake::Task['erd'].invoke
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ attributes:
2
+ - primary_key
3
+
@@ -0,0 +1,19 @@
1
+ attributes:
2
+ - content
3
+ - foreign_key
4
+ - inheritance
5
+ - false
6
+ disconnected: true
7
+ filename: erd
8
+ filetype: pdf
9
+ indirect: true
10
+ inheritance: false
11
+ markup: true
12
+ notation: simple
13
+ orientation: horizontal
14
+ polymorphism: false
15
+ warn: true
16
+ title: sample title
17
+ exclude:
18
+ only:
19
+
@@ -0,0 +1,19 @@
1
+ attributes:
2
+ - content
3
+ - foreign_key
4
+ - inheritance
5
+ - false
6
+ disconnected: true
7
+ filename: erd
8
+ filetype: pdf
9
+ indirect: true
10
+ inheritance: false
11
+ markup: true
12
+ notation: simple
13
+ orientation: horizontal
14
+ polymorphism: false
15
+ warn: true
16
+ title: sample title
17
+ exclude: Book,Author
18
+ only:
19
+
@@ -0,0 +1,160 @@
1
+ require "rubygems"
2
+ require "bundler/setup"
3
+
4
+ require "active_record"
5
+
6
+ if ActiveSupport::VERSION::MAJOR >= 4
7
+ require "minitest/autorun"
8
+ require 'mocha/mini_test'
9
+ else
10
+ require "test/unit"
11
+ require 'mocha/test_unit'
12
+ end
13
+
14
+ require "rails_erd/domain"
15
+
16
+ ActiveRecord::Base.establish_connection :adapter => "sqlite3", :database => ":memory:"
17
+
18
+ if ActiveSupport::TestCase.respond_to?(:test_order=)
19
+ ActiveSupport::TestCase.test_order = :random
20
+ end
21
+
22
+ class ActiveSupport::TestCase
23
+ include RailsERD
24
+
25
+ setup :reset_config_file
26
+ teardown :reset_domain
27
+
28
+ def create_table(table, columns = {}, pk = nil)
29
+ opts = if pk then { :primary_key => pk } else { :id => false } end
30
+ ActiveRecord::Schema.instance_eval do
31
+ suppress_messages do
32
+ create_table table, opts do |t|
33
+ columns.each do |column, type|
34
+ t.send type, column
35
+ end
36
+ end
37
+ end
38
+ end
39
+ ActiveRecord::Base.clear_cache!
40
+ end
41
+
42
+ def add_column(*args)
43
+ ActiveRecord::Schema.instance_eval do
44
+ suppress_messages do
45
+ add_column(*args)
46
+ end
47
+ end
48
+ ActiveRecord::Base.clear_cache!
49
+ end
50
+
51
+ def create_model(name, *args, &block)
52
+ superklass = args.first.kind_of?(Class) ? args.shift : ActiveRecord::Base
53
+ columns = args.first || {}
54
+ klass = Object.const_set name.to_sym, Class.new(superklass)
55
+ if superklass == ActiveRecord::Base || superklass.abstract_class?
56
+ create_table Object.const_get(name.to_sym).table_name, columns, Object.const_get(name.to_sym).primary_key rescue nil
57
+ end
58
+ klass.class_eval(&block) if block_given?
59
+ Object.const_get(name.to_sym)
60
+ end
61
+
62
+ def create_models(*names)
63
+ names.each do |name|
64
+ create_model name
65
+ end
66
+ end
67
+
68
+ def collect_stdout
69
+ stdout = $stdout
70
+ $stdout = StringIO.new
71
+ yield
72
+ $stdout.rewind
73
+ $stdout.read
74
+ ensure
75
+ $stdout = stdout
76
+ end
77
+
78
+ def create_simple_domain
79
+ create_model "Beer", :bar => :references do
80
+ belongs_to :bar
81
+ end
82
+ create_model "Bar"
83
+ end
84
+
85
+ def create_one_to_one_assoc_domain
86
+ create_model "One" do
87
+ has_one :other
88
+ end
89
+ create_model "Other", :one => :references do
90
+ belongs_to :one
91
+ end
92
+ end
93
+
94
+ def create_one_to_many_assoc_domain
95
+ create_model "One" do
96
+ has_many :many
97
+ end
98
+ create_model "Many", :one => :references do
99
+ belongs_to :one
100
+ end
101
+ end
102
+
103
+ def create_many_to_many_assoc_domain
104
+ create_model "Many" do
105
+ has_and_belongs_to_many :more
106
+ end
107
+ create_model "More" do
108
+ has_and_belongs_to_many :many
109
+ end
110
+ create_table "manies_mores", :many_id => :integer, :more_id => :integer
111
+ end
112
+
113
+ def create_specialization
114
+ create_model "Beverage", :type => :string
115
+ create_model "Beer", Beverage
116
+ end
117
+
118
+ def create_polymorphic_generalization
119
+ create_model "Cannon"
120
+ create_model "Galleon" do
121
+ has_many :cannons, :as => :defensible
122
+ end
123
+ end
124
+
125
+ def create_abstract_generalization
126
+ create_model "Structure" do
127
+ self.abstract_class = true
128
+ end
129
+ create_model "Palace", Structure
130
+ end
131
+
132
+ private
133
+
134
+ def reset_config_file
135
+ RailsERD::Config.send :remove_const, :USER_WIDE_CONFIG_FILE
136
+ RailsERD::Config.send :const_set, :USER_WIDE_CONFIG_FILE,
137
+ File.expand_path("../../examples/erdconfig.not_exists", __FILE__)
138
+
139
+ RailsERD::Config.send :remove_const, :CURRENT_CONFIG_FILE
140
+ RailsERD::Config.send :const_set, :CURRENT_CONFIG_FILE,
141
+ File.expand_path("../../examples/erdconfig.not_exists", __FILE__)
142
+
143
+ RailsERD.options = RailsERD.default_options.merge(Config.load)
144
+ end
145
+
146
+ def reset_domain
147
+ if defined? ActiveRecord
148
+ ActiveRecord::Base.descendants.each do |model|
149
+ model.reset_column_information
150
+ Object.send :remove_const, model.name.to_sym if Object.const_defined? model.name.to_sym
151
+ end
152
+ ActiveRecord::Base.connection.tables.each do |table|
153
+ ActiveRecord::Base.connection.drop_table table
154
+ end
155
+ ActiveRecord::Base.direct_descendants.clear
156
+ ActiveSupport::Dependencies::Reference.clear!
157
+ ActiveRecord::Base.clear_cache!
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,316 @@
1
+ # encoding: utf-8
2
+ require File.expand_path("../test_helper", File.dirname(__FILE__))
3
+
4
+ class AttributeTest < ActiveSupport::TestCase
5
+ def with_native_limit(type, new_limit)
6
+ ActiveRecord::Base.connection.class_eval do
7
+ define_method :native_database_types do
8
+ super().tap do |types|
9
+ types[type][:limit] = new_limit
10
+ end
11
+ end
12
+ end
13
+ yield
14
+ ensure
15
+ ActiveRecord::Base.connection.class_eval do
16
+ define_method :native_database_types do
17
+ super()
18
+ end
19
+ end
20
+ end
21
+
22
+ def create_attribute(model, name)
23
+ Domain::Attribute.new(Domain.generate, model, model.columns_hash[name])
24
+ end
25
+
26
+ # Attribute ================================================================
27
+ test "column should return database column" do
28
+ create_model "Foo", :my_column => :string
29
+ assert_equal Foo.columns_hash["my_column"],
30
+ Domain::Attribute.from_model(Domain.new, Foo).reject(&:primary_key?).first.column
31
+ end
32
+
33
+ test "from_model should return attributes with sorted order if sort is true" do
34
+ RailsERD.options[:sort] = true
35
+ create_model "Foo"
36
+ add_column :foos, :a, :string
37
+ assert_equal %w{a id}, Domain::Attribute.from_model(Domain.new, Foo).map(&:name)
38
+ end
39
+
40
+ test "from_model should return attributes with original order if sort is false" do
41
+ RailsERD.options[:sort] = false
42
+ create_model "Foo"
43
+ add_column :foos, :a, :string
44
+ assert_equal %w{id a}, Domain::Attribute.from_model(Domain.new, Foo).map(&:name)
45
+ end
46
+
47
+ test "from_model should return attributes with PK first if prepend_primary is true" do
48
+ RailsERD.options[:sort] = true
49
+ RailsERD.options[:prepend_primary] = true
50
+
51
+ create_model "Foo"
52
+ add_column :foos, :a, :string
53
+
54
+ assert_equal %w{id a}, Domain::Attribute.from_model(Domain.new, Foo).map(&:name)
55
+ end
56
+
57
+ test "spaceship should sort attributes by name" do
58
+ create_model "Foo", :a => :string, :b => :string, :c => :string
59
+ a = create_attribute(Foo, "a")
60
+ b = create_attribute(Foo, "b")
61
+ c = create_attribute(Foo, "c")
62
+ assert_equal [a, b, c], [c, a, b].sort
63
+ end
64
+
65
+ test "inspect should show column" do
66
+ create_model "Foo", :my_column => :string
67
+ assert_match %r{#<RailsERD::Domain::Attribute:.* @name="my_column" @type=:string>},
68
+ Domain::Attribute.new(Domain.new, Foo, Foo.columns_hash["my_column"]).inspect
69
+ end
70
+
71
+ test "type should return attribute type" do
72
+ create_model "Foo", :a => :binary
73
+ assert_equal :binary, create_attribute(Foo, "a").type
74
+ end
75
+
76
+ test "type should return native type if unsupported by rails" do
77
+ create_model "Foo"
78
+ ActiveRecord::Schema.define do
79
+ suppress_messages do
80
+ add_column "foos", "a", "REAL"
81
+ end
82
+ end
83
+ assert_equal :real, create_attribute(Foo, "a").type
84
+ end
85
+
86
+ # Attribute properties =====================================================
87
+ test "mandatory should return false by default" do
88
+ create_model "Foo", :column => :string
89
+ assert_equal false, create_attribute(Foo, "column").mandatory?
90
+ end
91
+
92
+ test "mandatory should return true if attribute has a presence validator" do
93
+ create_model "Foo", :column => :string do
94
+ validates :column, :presence => true
95
+ end
96
+ assert_equal true, create_attribute(Foo, "column").mandatory?
97
+ end
98
+
99
+ test "mandatory should return true if attribute has a not null constraint" do
100
+ create_model "Foo"
101
+ add_column :foos, :column, :string, :null => false, :default => ""
102
+ assert_equal true, create_attribute(Foo, "column").mandatory?
103
+ end
104
+
105
+ test "primary_key should return false by default" do
106
+ create_model "Bar", :my_key => :integer
107
+ assert_equal false, create_attribute(Bar, "my_key").primary_key?
108
+ end
109
+
110
+ test "primary_key should return true if column is used as primary key" do
111
+ create_model "Bar", :my_key => :integer do
112
+ self.primary_key = :my_key
113
+ end
114
+ assert_equal true, create_attribute(Bar, "my_key").primary_key?
115
+ end
116
+
117
+ test "foreign_key should return false by default" do
118
+ create_model "Foo", :bar => :references
119
+ assert_equal false, create_attribute(Foo, "bar_id").foreign_key?
120
+ end
121
+
122
+ test "foreign_key should return true if it is used in an association" do
123
+ create_model "Foo", :bar => :references do
124
+ belongs_to :bar
125
+ end
126
+ create_model "Bar"
127
+ assert_equal true, create_attribute(Foo, "bar_id").foreign_key?
128
+ end
129
+
130
+ test "foreign_key should return true if it is used in a remote association" do
131
+ create_model "Foo", :bar => :references
132
+ create_model "Bar" do
133
+ has_many :foos
134
+ end
135
+ assert_equal true, create_attribute(Foo, "bar_id").foreign_key?
136
+ end
137
+
138
+ test "timestamp should return false by default" do
139
+ create_model "Foo", :created => :datetime
140
+ assert_equal false, create_attribute(Foo, "created").timestamp?
141
+ end
142
+
143
+ test "timestamp should return true if it is named created_at/on or updated_at/on" do
144
+ create_model "Foo", :created_at => :string, :updated_at => :string, :created_on => :string, :updated_on => :string
145
+ assert_equal [true] * 4, [create_attribute(Foo, "created_at"), create_attribute(Foo, "updated_at"),
146
+ create_attribute(Foo, "created_on"), create_attribute(Foo, "updated_on")].collect(&:timestamp?)
147
+ end
148
+
149
+ test "inheritance should return false by default" do
150
+ create_model "Foo", :type => :string, :alternative => :string do
151
+ self.inheritance_column = :alternative
152
+ end
153
+ assert_equal false, create_attribute(Foo, "type").inheritance?
154
+ end
155
+
156
+ test "inheritance should return if this column is used for single table inheritance" do
157
+ create_model "Foo", :type => :string, :alternative => :string do
158
+ self.inheritance_column = :alternative
159
+ end
160
+ assert_equal true, create_attribute(Foo, "alternative").inheritance?
161
+ end
162
+
163
+ test "content should return true by default" do
164
+ create_model "Foo", :my_first_column => :string
165
+ assert_equal true, create_attribute(Foo, "my_first_column").content?
166
+ end
167
+
168
+ test "content should return false for primary keys, foreign keys, timestamps and inheritance columns" do
169
+ create_model "Book", :type => :string, :created_at => :datetime, :case => :references do
170
+ belongs_to :case
171
+ end
172
+ create_model "Case"
173
+ assert_equal [false] * 4, %w{id type created_at case_id}.map { |a| create_attribute(Book, a).content? }
174
+ end
175
+
176
+ # Type descriptions ========================================================
177
+ test "type_description should return short type description" do
178
+ create_model "Foo", :a => :binary
179
+ assert_equal "binary", create_attribute(Foo, "a").type_description
180
+ end
181
+
182
+ test "type_description should return short type description if unsupported by rails" do
183
+ create_model "Foo"
184
+ ActiveRecord::Schema.define do
185
+ suppress_messages do
186
+ add_column "foos", "a", "REAL"
187
+ end
188
+ end
189
+ assert_equal "real", create_attribute(Foo, "a").type_description
190
+ end
191
+
192
+ test "type_description should return short type description without limit if standard" do
193
+ with_native_limit :string, 456 do
194
+ create_model "Foo"
195
+ add_column :foos, :my_str, :string, :limit => 255
196
+ ActiveRecord::Base.connection.native_database_types[:string]
197
+ assert_equal "string (255)", create_attribute(Foo, "my_str").type_description
198
+ end
199
+ end
200
+
201
+ test "type_description should return short type description with limit if nonstandard" do
202
+ with_native_limit :string, 456 do
203
+ create_model "Foo"
204
+ add_column :foos, :my_str, :string, :limit => 456
205
+ assert_equal "string", create_attribute(Foo, "my_str").type_description
206
+ end
207
+ end
208
+
209
+ test "type_description should append hair space and low asterisk if field is mandatory" do
210
+ create_model "Foo", :a => :integer do
211
+ validates_presence_of :a
212
+ end
213
+ assert_equal "integer ∗", create_attribute(Foo, "a").type_description
214
+ end
215
+
216
+ test "type_description should return short type description with scale and precision for decimal types if nonstandard" do
217
+ create_model "Foo"
218
+ add_column :foos, :num, :decimal, :precision => 5, :scale => 2
219
+ assert_equal "decimal (5,2)", create_attribute(Foo, "num").type_description
220
+ end
221
+
222
+ test "limit should return nil if there is no limit" do
223
+ create_model "Foo"
224
+ add_column :foos, :my_txt, :text
225
+ assert_equal nil, create_attribute(Foo, "my_txt").limit
226
+ end
227
+
228
+ test "limit should return nil if equal to standard database limit" do
229
+ with_native_limit :string, 456 do
230
+ create_model "Foo"
231
+ add_column :foos, :my_str, :string, :limit => 456
232
+ assert_equal nil, create_attribute(Foo, "my_str").limit
233
+ end
234
+ end
235
+
236
+ test "limit should return limit if nonstandard" do
237
+ with_native_limit :string, 456 do
238
+ create_model "Foo"
239
+ add_column :foos, :my_str, :string, :limit => 255
240
+ assert_equal 255, create_attribute(Foo, "my_str").limit
241
+ end
242
+ end
243
+
244
+ test "limit should return precision for decimal columns if nonstandard" do
245
+ create_model "Foo"
246
+ add_column :foos, :num, :decimal, :precision => 5, :scale => 2
247
+ assert_equal 5, create_attribute(Foo, "num").limit
248
+ end
249
+
250
+ test "limit should return nil for decimal columns if equal to standard database limit" do
251
+ create_model "Foo"
252
+ add_column :foos, :num, :decimal
253
+ assert_equal nil, create_attribute(Foo, "num").limit
254
+ end
255
+
256
+ test "limit should return nil if type is unsupported by rails" do
257
+ create_model "Foo"
258
+ ActiveRecord::Schema.define do
259
+ suppress_messages do
260
+ add_column "foos", "a", "REAL"
261
+ end
262
+ end
263
+ assert_equal nil, create_attribute(Foo, "a").limit
264
+ end
265
+
266
+ test "limit should return nil for oddball column types that misuse the limit attribute" do
267
+ create_model "Business", :location => :integer
268
+ attribute = create_attribute(Business, "location")
269
+ attribute.column.class_eval do
270
+ define_method :limit do
271
+ # https://github.com/voormedia/rails-erd/issues/21
272
+ { :srid => 4326, :type => "point", :geographic => true }
273
+ end
274
+ end
275
+ assert_equal nil, attribute.limit
276
+ end
277
+
278
+ test "scale should return scale for decimal columns if nonstandard" do
279
+ create_model "Foo"
280
+ add_column :foos, :num, :decimal, :precision => 5, :scale => 2
281
+ assert_equal 2, create_attribute(Foo, "num").scale
282
+ end
283
+
284
+ test "scale should return nil for decimal columns if equal to standard database limit" do
285
+ create_model "Foo"
286
+ add_column :foos, :num, :decimal
287
+ assert_equal nil, create_attribute(Foo, "num").scale
288
+ end
289
+
290
+ test "scale should return zero for decimal columns if left to default setting when specifying precision" do
291
+ create_model "Foo"
292
+ add_column :foos, :num, :decimal, :precision => 5
293
+ assert_equal 0, create_attribute(Foo, "num").scale
294
+ end
295
+
296
+ test "scale should return nil if type is unsupported by rails" do
297
+ create_model "Foo"
298
+ ActiveRecord::Schema.define do
299
+ suppress_messages do
300
+ add_column "foos", "a", "REAL"
301
+ end
302
+ end
303
+ assert_equal nil, create_attribute(Foo, "a").scale
304
+ end
305
+
306
+ test "scale should return nil for oddball column types that misuse the scale attribute" do
307
+ create_model "Kobold", :size => :integer
308
+ attribute = create_attribute(Kobold, "size")
309
+ attribute.column.class_eval do
310
+ define_method :scale do
311
+ 1..5
312
+ end
313
+ end
314
+ assert_equal nil, attribute.scale
315
+ end
316
+ end