rails-erd 0.1.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 +4 -0
- data/LICENSE +19 -0
- data/README.rdoc +56 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/rails-erd.rb +1 -0
- data/lib/rails_erd.rb +38 -0
- data/lib/rails_erd/attribute.rb +74 -0
- data/lib/rails_erd/diagram.rb +88 -0
- data/lib/rails_erd/domain.rb +102 -0
- data/lib/rails_erd/entity.rb +50 -0
- data/lib/rails_erd/railtie.rb +7 -0
- data/lib/rails_erd/relationship.rb +85 -0
- data/lib/rails_erd/relationship/cardinality.rb +35 -0
- data/lib/rails_erd/tasks.rake +38 -0
- data/lib/rails_erd/templates/node.erb +13 -0
- data/test/test_helper.rb +72 -0
- data/test/unit/attribute_test.rb +144 -0
- data/test/unit/cardinality_test.rb +8 -0
- data/test/unit/diagram_test.rb +0 -0
- data/test/unit/domain_test.rb +125 -0
- data/test/unit/entity_test.rb +82 -0
- data/test/unit/relationship_test.rb +308 -0
- metadata +123 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
module RailsERD
|
2
|
+
class Relationship
|
3
|
+
class << self
|
4
|
+
def from_associations(domain, associations) #:nodoc:
|
5
|
+
assoc_groups = associations.group_by { |assoc| association_identity(assoc) }
|
6
|
+
assoc_groups.collect { |_, assoc_group| Relationship.new(domain, assoc_group.to_a) }
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def association_identity(assoc)
|
12
|
+
identifier = assoc.options[:join_table] || assoc.primary_key_name.to_s
|
13
|
+
Set[identifier, assoc.active_record, assoc.klass]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the domain in which this relationship is defined.
|
18
|
+
attr_reader :domain
|
19
|
+
|
20
|
+
# Returns the source entity. The source entity corresponds to the model
|
21
|
+
# that has defined a +has_one+ or +has_many+ association with the other
|
22
|
+
# model.
|
23
|
+
attr_reader :source
|
24
|
+
|
25
|
+
# Returns the destination entity. The destination entity corresponds to the
|
26
|
+
# model that has defined a +belongs_to+ association with the other model.
|
27
|
+
attr_reader :destination
|
28
|
+
|
29
|
+
def initialize(domain, associations) #:nodoc:
|
30
|
+
@domain = domain
|
31
|
+
@reverse_associations, @forward_associations = *associations.partition(&:belongs_to?)
|
32
|
+
|
33
|
+
assoc = @forward_associations.first || @reverse_associations.first
|
34
|
+
@source, @destination = @domain.entity_for(assoc.active_record), @domain.entity_for(assoc.klass)
|
35
|
+
@source, @destination = @destination, @source if assoc.belongs_to?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns all +ActiveRecord+ association objects that describe this
|
39
|
+
# relationship.
|
40
|
+
def associations
|
41
|
+
@forward_associations + @reverse_associations
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the cardinality of this relationship. The cardinality may be
|
45
|
+
# one of Cardinality::OneToOne, Cardinality::OneToMany, or
|
46
|
+
# Cardinality::ManyToMany.
|
47
|
+
def cardinality
|
48
|
+
@forward_associations.collect { |assoc| Cardinality.from_macro(assoc.macro) }.max or Cardinality::OneToMany
|
49
|
+
end
|
50
|
+
|
51
|
+
# Indicates if a relationship is indirect, that is, if it is defined
|
52
|
+
# through other relationships. Indirect relationships are created in
|
53
|
+
# Rails with <tt>has_many :through</tt> or <tt>has_one :through</tt>
|
54
|
+
# association macros.
|
55
|
+
def indirect?
|
56
|
+
@forward_associations.all?(&:through_reflection)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Indicates whether or not the relationship is defined by two inverse
|
60
|
+
# associations (e.g. a +has_many+ and a corresponding +belongs_to+
|
61
|
+
# association).
|
62
|
+
def mutual?
|
63
|
+
@forward_associations.any? and @reverse_associations.any?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Indicates whether or not this relationship connects an entity with itself.
|
67
|
+
def recursive?
|
68
|
+
@source == @destination
|
69
|
+
end
|
70
|
+
|
71
|
+
# The strength of a relationship is equal to the number of associations
|
72
|
+
# that describe it.
|
73
|
+
def strength
|
74
|
+
associations.size
|
75
|
+
end
|
76
|
+
|
77
|
+
def inspect #:nodoc:
|
78
|
+
"#<#{self.class}:0x%.14x @source=#{source} @destination=#{destination}>" % (object_id << 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
def <=>(other) #:nodoc:
|
82
|
+
(source.name <=> other.source.name).nonzero? or (destination.name <=> other.destination.name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RailsERD
|
2
|
+
class Relationship
|
3
|
+
class Cardinality
|
4
|
+
CARDINALITY_NAMES = %w{one_to_one one_to_many many_to_many} #:nodoc:
|
5
|
+
ORDER = {} #:nodoc:
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_reader :type
|
9
|
+
|
10
|
+
def from_macro(macro) #:nodoc:
|
11
|
+
case macro
|
12
|
+
when :has_and_belongs_to_many then ManyToMany
|
13
|
+
when :has_many then OneToMany
|
14
|
+
when :has_one then OneToOne
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def <=>(other) #:nodoc:
|
19
|
+
ORDER[self] <=> ORDER[other]
|
20
|
+
end
|
21
|
+
|
22
|
+
CARDINALITY_NAMES.each do |cardinality|
|
23
|
+
define_method :"#{cardinality}?" do
|
24
|
+
type == cardinality
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
CARDINALITY_NAMES.each_with_index do |cardinality, i|
|
30
|
+
klass = Cardinality.const_set cardinality.camelize.to_sym, Class.new(Cardinality) { @@type = cardinality }
|
31
|
+
ORDER[klass] = i
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
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" then true
|
10
|
+
when "false" then false
|
11
|
+
else ENV[option].to_sym
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
task :load_models do
|
17
|
+
say "Loading ActiveRecord models..."
|
18
|
+
|
19
|
+
Rake::Task[:environment].invoke
|
20
|
+
Rails.application.config.paths.app.models.paths.each do |model_path|
|
21
|
+
Dir["#{model_path}/**/*.rb"].sort.each do |file|
|
22
|
+
require_dependency file
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task :generate => [:options, :load_models] do
|
28
|
+
say "Generating ERD diagram..."
|
29
|
+
|
30
|
+
require "rails_erd/diagram"
|
31
|
+
diagram = RailsERD::Diagram.generate
|
32
|
+
|
33
|
+
say "Done! Saved diagram to #{diagram.file_name}."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Generate an Entity-Relationship Diagram based on your models"
|
38
|
+
task :erd => "erd:generate"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% if vertical? %>{<% end %>
|
2
|
+
<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="<%= NODE_WIDTH + 4 %>">
|
3
|
+
<tr><td align="center" valign="bottom" width="<%= NODE_WIDTH %>"><font face="Arial Bold" point-size="11"><%= entity.name %></font></td></tr>
|
4
|
+
</table>|<% if attributes.any? %>
|
5
|
+
<table border="0" align="left" cellspacing="2" cellpadding="0" width="<%= NODE_WIDTH + 4 %>">
|
6
|
+
<% attributes.each do |attribute| %>
|
7
|
+
<tr>
|
8
|
+
<td align="left" width="<%= NODE_WIDTH %>" port="<%= attribute %>"><%= attribute %> <font face="Arial Italic" color="grey60"><%= attribute.type_description %></font></td>
|
9
|
+
</tr>
|
10
|
+
<% end %>
|
11
|
+
</table>
|
12
|
+
<% end %>
|
13
|
+
<% if vertical? %>}<% end %>
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "active_support/test_case"
|
3
|
+
|
4
|
+
require "rails_erd/domain"
|
5
|
+
|
6
|
+
require "rails"
|
7
|
+
require "active_record"
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection :adapter => "sqlite3", :database => ":memory:"
|
10
|
+
|
11
|
+
include RailsERD
|
12
|
+
|
13
|
+
class ActiveSupport::TestCase
|
14
|
+
teardown :reset_domain
|
15
|
+
|
16
|
+
def create_table(table, columns = {}, pk = nil)
|
17
|
+
opts = if pk then { :primary_key => pk } else { :id => false } end
|
18
|
+
ActiveRecord::Schema.define do
|
19
|
+
suppress_messages do
|
20
|
+
create_table table, opts do |t|
|
21
|
+
columns.each do |column, type|
|
22
|
+
t.send type, column
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_column(*args)
|
30
|
+
ActiveRecord::Schema.define do
|
31
|
+
suppress_messages do
|
32
|
+
add_column *args
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_model(name, columns = {}, &block)
|
38
|
+
klass = Object.const_set name.to_sym, Class.new(ActiveRecord::Base)
|
39
|
+
klass.class_eval(&block) if block_given?
|
40
|
+
create_table Object.const_get(name.to_sym).table_name, columns, Object.const_get(name.to_sym).primary_key rescue nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_models(*names)
|
44
|
+
names.each do |name|
|
45
|
+
create_model name
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def collect_stdout
|
50
|
+
stdout = $stdout
|
51
|
+
$stdout = StringIO.new
|
52
|
+
yield
|
53
|
+
$stdout.rewind
|
54
|
+
$stdout.read
|
55
|
+
ensure
|
56
|
+
$stdout = stdout
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def reset_domain
|
62
|
+
ActiveRecord::Base.descendants.each do |model|
|
63
|
+
Object.send :remove_const, model.name.to_sym
|
64
|
+
end
|
65
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
66
|
+
ActiveRecord::Base.connection.drop_table table
|
67
|
+
end
|
68
|
+
ActiveRecord::Base.direct_descendants.clear
|
69
|
+
Arel::Relation.class_variable_set :@@connection_tables_primary_keys, {}
|
70
|
+
ActiveSupport::Dependencies::Reference.clear!
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,144 @@
|
|
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
|
+
Attribute.new(Domain.generate, model, model.arel_table.attributes[name].column)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Attribute ================================================================
|
27
|
+
test "column should return database column" do
|
28
|
+
create_model "Foo", :my_column => :string
|
29
|
+
assert_equal Foo.arel_table.attributes["my_column"].column,
|
30
|
+
Attribute.from_model(Domain.new, Foo).reject(&:primary_key?).first.column
|
31
|
+
end
|
32
|
+
|
33
|
+
test "spaceship should sort attributes by name" do
|
34
|
+
create_model "Foo", :a => :string, :b => :string, :c => :string
|
35
|
+
a = Attribute.new(Domain.new, Foo, Foo.arel_table.attributes["a"].column)
|
36
|
+
b = Attribute.new(Domain.new, Foo, Foo.arel_table.attributes["b"].column)
|
37
|
+
c = Attribute.new(Domain.new, Foo, Foo.arel_table.attributes["c"].column)
|
38
|
+
assert_equal [a, b, c], [c, a, b].sort
|
39
|
+
end
|
40
|
+
|
41
|
+
test "inspect should show column" do
|
42
|
+
create_model "Foo", :my_column => :string
|
43
|
+
assert_match %r{#<RailsERD::Attribute:.* @column="my_column" @type=:string>},
|
44
|
+
Attribute.new(Domain.new, Foo, Foo.arel_table.attributes["my_column"].column).inspect
|
45
|
+
end
|
46
|
+
|
47
|
+
test "type should return attribute type" do
|
48
|
+
create_model "Foo", :a => :binary
|
49
|
+
assert_equal :binary, create_attribute(Foo, "a").type
|
50
|
+
end
|
51
|
+
|
52
|
+
# Attribute properties =====================================================
|
53
|
+
test "mandatory should return false by default" do
|
54
|
+
create_model "Foo", :column => :string
|
55
|
+
assert_equal false, create_attribute(Foo, "column").mandatory?
|
56
|
+
end
|
57
|
+
|
58
|
+
test "mandatory should return true if attribute has a presence validator" do
|
59
|
+
create_model "Foo", :column => :string do
|
60
|
+
validates :column, :presence => true
|
61
|
+
end
|
62
|
+
assert_equal true, create_attribute(Foo, "column").mandatory?
|
63
|
+
end
|
64
|
+
|
65
|
+
test "mandatory should return true if attribute has a not null constraint" do
|
66
|
+
create_model "Foo"
|
67
|
+
add_column :foos, :column, :string, :null => false, :default => ""
|
68
|
+
assert_equal true, create_attribute(Foo, "column").mandatory?
|
69
|
+
end
|
70
|
+
|
71
|
+
test "primary_key should return false by default" do
|
72
|
+
create_model "Bar", :my_key => :integer
|
73
|
+
assert_equal false, create_attribute(Bar, "my_key").primary_key?
|
74
|
+
end
|
75
|
+
|
76
|
+
test "primary_key should return true if column is used as primary key" do
|
77
|
+
create_model "Bar", :my_key => :integer do
|
78
|
+
set_primary_key :my_key
|
79
|
+
end
|
80
|
+
assert_equal true, create_attribute(Bar, "my_key").primary_key?
|
81
|
+
end
|
82
|
+
|
83
|
+
test "foreign_key should return false by default" do
|
84
|
+
create_model "Foo", :bar => :references
|
85
|
+
assert_equal false, create_attribute(Foo, "bar_id").foreign_key?
|
86
|
+
end
|
87
|
+
|
88
|
+
test "foreign_key should return true if it is used in an association" do
|
89
|
+
create_model "Foo", :bar => :references do
|
90
|
+
belongs_to :bar
|
91
|
+
end
|
92
|
+
create_model "Bar"
|
93
|
+
assert_equal true, create_attribute(Foo, "bar_id").foreign_key?
|
94
|
+
end
|
95
|
+
|
96
|
+
test "foreign_key should return true if it is used in a remote association" do
|
97
|
+
create_model "Foo", :bar => :references
|
98
|
+
create_model "Bar" do
|
99
|
+
has_many :foos
|
100
|
+
end
|
101
|
+
assert_equal true, create_attribute(Foo, "bar_id").foreign_key?
|
102
|
+
end
|
103
|
+
|
104
|
+
test "timestamp should return false by default" do
|
105
|
+
create_model "Foo", :created => :datetime
|
106
|
+
assert_equal false, create_attribute(Foo, "created").timestamp?
|
107
|
+
end
|
108
|
+
|
109
|
+
test "timestamp should return true if it is named created_at/on or updated_at/on" do
|
110
|
+
create_model "Foo", :created_at => :string, :updated_at => :string, :created_on => :string, :updated_on => :string
|
111
|
+
assert_equal [true] * 4, [create_attribute(Foo, "created_at"), create_attribute(Foo, "updated_at"),
|
112
|
+
create_attribute(Foo, "created_on"), create_attribute(Foo, "updated_on")].collect(&:timestamp?)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Type descriptions ========================================================
|
116
|
+
test "type_description should return short type description" do
|
117
|
+
create_model "Foo", :a => :binary
|
118
|
+
assert_equal "blob", create_attribute(Foo, "a").type_description
|
119
|
+
end
|
120
|
+
|
121
|
+
test "type_description should return short type description without limit if standard" do
|
122
|
+
with_native_limit :string, 456 do
|
123
|
+
create_model "Foo"
|
124
|
+
add_column :foos, :my_str, :string, :limit => 255
|
125
|
+
ActiveRecord::Base.connection.native_database_types[:string]
|
126
|
+
assert_equal "str (255)", create_attribute(Foo, "my_str").type_description
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
test "type_description should return short type description with limit if nonstandard" do
|
131
|
+
with_native_limit :string, 456 do
|
132
|
+
create_model "Foo"
|
133
|
+
add_column :foos, :my_str, :string, :limit => 456
|
134
|
+
assert_equal "str", create_attribute(Foo, "my_str").type_description
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
test "type_description should append hair space and low asterisk if field is mandatory" do
|
139
|
+
create_model "Foo", :a => :integer do
|
140
|
+
validates_presence_of :a
|
141
|
+
end
|
142
|
+
assert_equal "int ∗", create_attribute(Foo, "a").type_description
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class CardinalityTest < ActiveSupport::TestCase
|
4
|
+
test "cardinalities should be sorted in order of maniness" do
|
5
|
+
assert_equal [Relationship::Cardinality::OneToOne, Relationship::Cardinality::OneToMany, Relationship::Cardinality::ManyToMany],
|
6
|
+
[Relationship::Cardinality::OneToMany, Relationship::Cardinality::ManyToMany, Relationship::Cardinality::OneToOne].sort
|
7
|
+
end
|
8
|
+
end
|
File without changes
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require File.expand_path("../test_helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class DomainTest < ActiveSupport::TestCase
|
4
|
+
# Domain ===================================================================
|
5
|
+
test "generate should return domain" do
|
6
|
+
assert_kind_of Domain, Domain.generate
|
7
|
+
end
|
8
|
+
|
9
|
+
test "name should return rails application name" do
|
10
|
+
Object::Quux = Module.new
|
11
|
+
Object::Quux::Application = Class.new Rails::Application
|
12
|
+
assert_equal "Quux", Domain.generate.name
|
13
|
+
end
|
14
|
+
|
15
|
+
test "name should return nil outside rails" do
|
16
|
+
assert_nil Domain.generate.name
|
17
|
+
end
|
18
|
+
|
19
|
+
# Entity processing ========================================================
|
20
|
+
test "entity_for should return associated entity for given model" do
|
21
|
+
create_model "Foo"
|
22
|
+
assert_equal Foo, Domain.generate.entity_for(Foo).model
|
23
|
+
end
|
24
|
+
|
25
|
+
test "entities should return domain entities" do
|
26
|
+
create_models "Foo", "Bar"
|
27
|
+
assert_equal [Entity] * 2, Domain.generate.entities.collect(&:class)
|
28
|
+
end
|
29
|
+
|
30
|
+
test "entities should return all domain entities sorted by name" do
|
31
|
+
create_models "Foo", "Bar", "Baz", "Qux"
|
32
|
+
assert_equal [Bar, Baz, Foo, Qux], Domain.generate.entities.collect(&:model)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Relationship processing ==================================================
|
36
|
+
test "relationships should return empty array for empty domain" do
|
37
|
+
assert_equal [], Domain.generate.relationships
|
38
|
+
end
|
39
|
+
|
40
|
+
test "relationships should return relationships in domain model" do
|
41
|
+
create_models "Baz", "Qux"
|
42
|
+
create_model "Foo", :bar => :references, :qux => :references do
|
43
|
+
belongs_to :bar
|
44
|
+
belongs_to :qux
|
45
|
+
end
|
46
|
+
create_model "Bar", :baz => :references do
|
47
|
+
belongs_to :baz
|
48
|
+
end
|
49
|
+
assert_equal [Relationship] * 3, Domain.generate.relationships.collect(&:class)
|
50
|
+
end
|
51
|
+
|
52
|
+
test "relationships should count mutual relationship as one" do
|
53
|
+
create_model "Foo", :bar => :references do
|
54
|
+
belongs_to :bar
|
55
|
+
end
|
56
|
+
create_model "Bar" do
|
57
|
+
has_many :foos
|
58
|
+
end
|
59
|
+
assert_equal [Relationship], Domain.generate.relationships.collect(&:class)
|
60
|
+
end
|
61
|
+
|
62
|
+
test "relationships should count relationship between same models with distinct foreign key seperately" do
|
63
|
+
create_model "Foo", :bar => :references, :special_bar => :references do
|
64
|
+
belongs_to :bar
|
65
|
+
end
|
66
|
+
create_model "Bar" do
|
67
|
+
has_many :foos, :foreign_key => :special_bar_id
|
68
|
+
end
|
69
|
+
assert_equal [Relationship] * 2, Domain.generate.relationships.collect(&:class)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Erroneous associations ===================================================
|
73
|
+
test "relationships should omit bad has_many associations" do
|
74
|
+
create_model "Foo" do
|
75
|
+
has_many :flabs
|
76
|
+
end
|
77
|
+
assert_equal [], Domain.generate(:suppress_warnings => true).relationships
|
78
|
+
end
|
79
|
+
|
80
|
+
test "relationships should omit bad has_many through association" do
|
81
|
+
create_model "Foo" do
|
82
|
+
has_many :flabs, :through => :bars
|
83
|
+
end
|
84
|
+
assert_equal [], Domain.generate(:suppress_warnings => true).relationships
|
85
|
+
end
|
86
|
+
|
87
|
+
test "relationships should omit association to model outside domain" do
|
88
|
+
create_model "Foo" do
|
89
|
+
has_many :bars
|
90
|
+
end
|
91
|
+
create_model "Bar", :foo => :references
|
92
|
+
assert_equal [], Domain.new([Foo], :suppress_warnings => true).relationships
|
93
|
+
end
|
94
|
+
|
95
|
+
test "relationships should output a warning when a bad association is encountered" do
|
96
|
+
create_model "Foo" do
|
97
|
+
has_many :flabs
|
98
|
+
end
|
99
|
+
output = collect_stdout do
|
100
|
+
Domain.generate.relationships
|
101
|
+
end
|
102
|
+
assert_match /Invalid association :flabs on Foo/, output
|
103
|
+
end
|
104
|
+
|
105
|
+
test "relationships should output a warning when an association to model outside domain is encountered" do
|
106
|
+
create_model "Foo" do
|
107
|
+
has_many :bars
|
108
|
+
end
|
109
|
+
create_model "Bar", :foo => :references
|
110
|
+
output = collect_stdout do
|
111
|
+
Domain.new([Foo]).relationships
|
112
|
+
end
|
113
|
+
assert_match /model Bar exists, but is not included in the domain/, output
|
114
|
+
end
|
115
|
+
|
116
|
+
test "relationships should suppress warnings when a bad association is encountered if warning suppression is enabled" do
|
117
|
+
create_model "Foo" do
|
118
|
+
has_many :flabs
|
119
|
+
end
|
120
|
+
output = collect_stdout do
|
121
|
+
Domain.generate(:suppress_warnings => true).relationships
|
122
|
+
end
|
123
|
+
assert_equal "", output
|
124
|
+
end
|
125
|
+
end
|