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,123 @@
1
+ require File.expand_path("../test_helper", File.dirname(__FILE__))
2
+
3
+ class CardinalityTest < ActiveSupport::TestCase
4
+ def setup
5
+ @n = Domain::Relationship::Cardinality::N
6
+ end
7
+
8
+ # Cardinality ==============================================================
9
+ test "inspect should show source and destination ranges" do
10
+ assert_match %r{#<RailsERD::Domain::Relationship::Cardinality:.* @source_range=1\.\.1 @destination_range=1\.\.Infinity>},
11
+ Domain::Relationship::Cardinality.new(1, 1..@n).inspect
12
+ end
13
+
14
+ # Cardinality construction =================================================
15
+ test "new should return cardinality object" do
16
+ assert_kind_of Domain::Relationship::Cardinality, Domain::Relationship::Cardinality.new(1, 1..@n)
17
+ end
18
+
19
+ # Cardinality properties ===================================================
20
+ test "source_optional should return true if source range starts at zero" do
21
+ assert_equal true, Domain::Relationship::Cardinality.new(0..1, 1).source_optional?
22
+ end
23
+
24
+ test "source_optional should return false if source range starts at one or more" do
25
+ assert_equal false, Domain::Relationship::Cardinality.new(1..2, 0..1).source_optional?
26
+ end
27
+
28
+ test "destination_optional should return true if destination range starts at zero" do
29
+ assert_equal true, Domain::Relationship::Cardinality.new(1, 0..1).destination_optional?
30
+ end
31
+
32
+ test "destination_optional should return false if destination range starts at one or more" do
33
+ assert_equal false, Domain::Relationship::Cardinality.new(0..1, 1..2).destination_optional?
34
+ end
35
+
36
+ test "inverse should return inverse cardinality" do
37
+ assert_equal Domain::Relationship::Cardinality.new(23..45, 0..15), Domain::Relationship::Cardinality.new(0..15, 23..45).inverse
38
+ end
39
+
40
+ # Cardinality equality =====================================================
41
+ test "cardinalities are equal if they have the same boundaries" do
42
+ assert_equal Domain::Relationship::Cardinality.new(1, 1..Domain::Relationship::Cardinality::N),
43
+ Domain::Relationship::Cardinality.new(1, 1..Domain::Relationship::Cardinality::N)
44
+ end
45
+
46
+ test "cardinalities are not equal if they have a different source range" do
47
+ assert_not_equal Domain::Relationship::Cardinality.new(0..1, 1..Domain::Relationship::Cardinality::N),
48
+ Domain::Relationship::Cardinality.new(1..1, 1..Domain::Relationship::Cardinality::N)
49
+ end
50
+
51
+ test "cardinalities are not equal if they have a different destination range" do
52
+ assert_not_equal Domain::Relationship::Cardinality.new(0..1, 1..Domain::Relationship::Cardinality::N),
53
+ Domain::Relationship::Cardinality.new(0..1, 2..Domain::Relationship::Cardinality::N)
54
+ end
55
+
56
+ # Cardinal names ===========================================================
57
+ test "one_to_one should return true if source and destination are exactly one" do
58
+ assert_equal true, Domain::Relationship::Cardinality.new(1, 1).one_to_one?
59
+ end
60
+
61
+ test "one_to_one should return true if source and destination range are less than or equal to one" do
62
+ assert_equal true, Domain::Relationship::Cardinality.new(0..1, 0..1).one_to_one?
63
+ end
64
+
65
+ test "one_to_one should return false if source range upper limit is more than one" do
66
+ assert_equal false, Domain::Relationship::Cardinality.new(0..15, 0..1).one_to_one?
67
+ end
68
+
69
+ test "one_to_one should return false if destination range upper limit is more than one" do
70
+ assert_equal false, Domain::Relationship::Cardinality.new(0..1, 0..15).one_to_one?
71
+ end
72
+
73
+ test "one_to_many should return true if source is exactly one and destination is higher than one" do
74
+ assert_equal true, Domain::Relationship::Cardinality.new(1, 15).one_to_many?
75
+ end
76
+
77
+ test "one_to_many should return true if source is less than or equal to one and destination is higher than one" do
78
+ assert_equal true, Domain::Relationship::Cardinality.new(0..1, 0..15).one_to_many?
79
+ end
80
+
81
+ test "one_to_many should return false if source range upper limit is more than one" do
82
+ assert_equal false, Domain::Relationship::Cardinality.new(0..15, 0..15).one_to_many?
83
+ end
84
+
85
+ test "one_to_many should return false if destination range upper limit is one" do
86
+ assert_equal false, Domain::Relationship::Cardinality.new(0..1, 1).one_to_many?
87
+ end
88
+
89
+ test "many_to_many should return true if source and destination are higher than one" do
90
+ assert_equal true, Domain::Relationship::Cardinality.new(15, 15).many_to_many?
91
+ end
92
+
93
+ test "many_to_many should return true if source and destination upper limits are higher than one" do
94
+ assert_equal true, Domain::Relationship::Cardinality.new(0..15, 0..15).many_to_many?
95
+ end
96
+
97
+ test "many_to_many should return false if source range upper limit is is one" do
98
+ assert_equal false, Domain::Relationship::Cardinality.new(1, 0..15).many_to_many?
99
+ end
100
+
101
+ test "many_to_many should return false if destination range upper limit is one" do
102
+ assert_equal false, Domain::Relationship::Cardinality.new(0..1, 1).many_to_many?
103
+ end
104
+
105
+ test "inverse of one_to_many should be many_to_one" do
106
+ assert_equal true, Domain::Relationship::Cardinality.new(0..1, 0..@n).inverse.many_to_one?
107
+ end
108
+
109
+ # Cardinality order ========================================================
110
+ test "cardinalities should be sorted in order of maniness" do
111
+ card1 = Domain::Relationship::Cardinality.new(0..1, 1)
112
+ card2 = Domain::Relationship::Cardinality.new(1, 1)
113
+ card3 = Domain::Relationship::Cardinality.new(0..1, 1..3)
114
+ card4 = Domain::Relationship::Cardinality.new(1, 1..2)
115
+ card5 = Domain::Relationship::Cardinality.new(1, 1..@n)
116
+ card6 = Domain::Relationship::Cardinality.new(1..5, 1..3)
117
+ card7 = Domain::Relationship::Cardinality.new(1..2, 1..15)
118
+ card8 = Domain::Relationship::Cardinality.new(1..15, 1..@n)
119
+ card9 = Domain::Relationship::Cardinality.new(1..@n, 1..@n)
120
+ assert_equal [card1, card2, card3, card4, card5, card6, card7, card8, card9],
121
+ [card9, card5, card8, card2, card4, card7, card1, card6, card3].sort
122
+ end
123
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+ require File.expand_path("../test_helper", File.dirname(__FILE__))
3
+
4
+ class ConfigTest < ActiveSupport::TestCase
5
+
6
+ test "load_config_gile should return blank hash when neither CURRENT_CONFIG_FILE nor USER_WIDE_CONFIG_FILE exist." do
7
+ expected_hash = {}
8
+ assert_equal expected_hash, RailsERD::Config.load
9
+ end
10
+
11
+ test "load_config_gile should return a hash from USER_WIDE_CONFIG_FILE when only USER_WIDE_CONFIG_FILE exists." do
12
+ set_user_config_file_to("erdconfig.example")
13
+
14
+ expected_hash = {
15
+ attributes: [:content, :foreign_key, :inheritance, :false],
16
+ disconnected: true,
17
+ filename: "erd",
18
+ filetype: :pdf,
19
+ indirect: true,
20
+ inheritance: false,
21
+ markup: true,
22
+ notation: :simple,
23
+ orientation: :horizontal,
24
+ polymorphism: false,
25
+ warn: true,
26
+ title: "sample title",
27
+ exclude: [],
28
+ only: []
29
+ }
30
+ assert_equal expected_hash, RailsERD::Config.load
31
+ end
32
+
33
+ test "load_config_file should return a hash from USER_WIDE_CONFIG_FILE when only USER_WIDE_CONFIG_FILE exists." do
34
+ set_user_config_file_to("erdconfig.exclude.example")
35
+
36
+ expected_hash = {
37
+ attributes: [:content, :foreign_key, :inheritance, :false],
38
+ disconnected: true,
39
+ filename: "erd",
40
+ filetype: :pdf,
41
+ indirect: true,
42
+ inheritance: false,
43
+ markup: true,
44
+ notation: :simple,
45
+ orientation: :horizontal,
46
+ polymorphism: false,
47
+ warn: true,
48
+ title: "sample title",
49
+ exclude: ['Book', 'Author'],
50
+ only: []
51
+ }
52
+ assert_equal expected_hash, RailsERD::Config.load
53
+ end
54
+
55
+ test "load_config_gile should return a hash from CURRENT_CONFIG_FILE when only CURRENT_CONFIG_FILE exists." do
56
+ set_local_config_file_to("erdconfig.another_example")
57
+
58
+ expected_hash = {
59
+ :attributes => [:primary_key]
60
+ }
61
+ assert_equal expected_hash, RailsERD::Config.load
62
+ end
63
+
64
+ test "load_config_gile should return a hash from CURRENT_CONFIG_FILE overriding USER_WIDE_CONFIG_FILE when both of them exist." do
65
+ set_user_config_file_to("erdconfig.example")
66
+ set_local_config_file_to("erdconfig.another_example")
67
+
68
+ expected_hash = {
69
+ attributes: [:primary_key],
70
+ disconnected: true,
71
+ filename: "erd",
72
+ filetype: :pdf,
73
+ indirect: true,
74
+ inheritance: false,
75
+ markup: true,
76
+ notation: :simple,
77
+ orientation: :horizontal,
78
+ polymorphism: false,
79
+ warn: true,
80
+ title: "sample title",
81
+ exclude: [],
82
+ only: []
83
+ }
84
+ assert_equal expected_hash, RailsERD::Config.load
85
+ end
86
+
87
+ test "normalize_value should return symbols in an array when key is :attributes and value is a comma-joined string." do
88
+ assert_equal [:content, :foreign_keys], normalize_value(:attributes, "content,foreign_keys")
89
+ end
90
+
91
+ test "normalize_value should return symbols in an array when key is :attributes and value is strings in an array." do
92
+ assert_equal [:content, :primary_keys], normalize_value(:attributes, ["content", "primary_keys"])
93
+ end
94
+
95
+ def normalize_value(key, value)
96
+ RailsERD::Config.new.send(:normalize_value, key, value)
97
+ end
98
+
99
+ def set_user_config_file_to(config_file)
100
+ RailsERD::Config.send :remove_const, :USER_WIDE_CONFIG_FILE
101
+ RailsERD::Config.send :const_set, :USER_WIDE_CONFIG_FILE,
102
+ File.expand_path("test/support_files/#{config_file}")
103
+ end
104
+
105
+ def set_local_config_file_to(config_file)
106
+ RailsERD::Config.send :remove_const, :CURRENT_CONFIG_FILE
107
+ RailsERD::Config.send :const_set, :CURRENT_CONFIG_FILE,
108
+ File.expand_path("test/support_files/#{config_file}")
109
+ end
110
+ end
@@ -0,0 +1,352 @@
1
+ require File.expand_path("../test_helper", File.dirname(__FILE__))
2
+ require "rails_erd/diagram"
3
+
4
+ class DiagramTest < ActiveSupport::TestCase
5
+ def retrieve_entities(options = {})
6
+ klass = Class.new(Diagram)
7
+ [].tap do |entities|
8
+ klass.class_eval do
9
+ each_entity do |entity, attributes|
10
+ entities << entity
11
+ end
12
+ end
13
+ klass.create(options)
14
+ end
15
+ end
16
+
17
+ def retrieve_relationships(options = {})
18
+ klass = Class.new(Diagram)
19
+ [].tap do |relationships|
20
+ klass.class_eval do
21
+ each_relationship do |relationship|
22
+ relationships << relationship
23
+ end
24
+ end
25
+ klass.create(options)
26
+ end
27
+ end
28
+
29
+ def retrieve_specializations(options = {})
30
+ klass = Class.new(Diagram)
31
+ [].tap do |specializations|
32
+ klass.class_eval do
33
+ each_specialization do |specialization|
34
+ specializations << specialization
35
+ end
36
+ end
37
+ klass.create(options)
38
+ end
39
+ end
40
+
41
+ def retrieve_attribute_lists(options = {})
42
+ klass = Class.new(Diagram)
43
+ {}.tap do |attribute_lists|
44
+ klass.class_eval do
45
+ each_entity do |entity, attributes|
46
+ attribute_lists[entity.model] = attributes
47
+ end
48
+ end
49
+ klass.create(options)
50
+ end
51
+ end
52
+
53
+ # Diagram ==================================================================
54
+ test "domain sould return given domain" do
55
+ domain = Object.new
56
+ assert_same domain, Class.new(Diagram).new(domain).domain
57
+ end
58
+
59
+ # Diagram DSL ==============================================================
60
+ test "create should succeed silently if called on abstract class" do
61
+ create_simple_domain
62
+ assert_nothing_raised do
63
+ Diagram.create
64
+ end
65
+ end
66
+
67
+ test "create should succeed if called on subclass" do
68
+ create_simple_domain
69
+ assert_nothing_raised do
70
+ Class.new(Diagram).create
71
+ end
72
+ end
73
+
74
+ test "create should call callbacks in instance in specific order" do
75
+ create_simple_domain
76
+ executed_calls = Class.new(Diagram) do
77
+ setup do
78
+ calls << :setup
79
+ end
80
+
81
+ each_entity do
82
+ calls << :entity
83
+ end
84
+
85
+ each_relationship do
86
+ calls << :relationship
87
+ end
88
+
89
+ save do
90
+ calls << :save
91
+ end
92
+
93
+ def calls
94
+ @calls ||= []
95
+ end
96
+ end.create
97
+ assert_equal [:setup, :entity, :entity, :relationship, :save], executed_calls
98
+ end
99
+
100
+ test "create class method should return result of save" do
101
+ create_simple_domain
102
+ subclass = Class.new(Diagram) do
103
+ save do
104
+ "foobar"
105
+ end
106
+ end
107
+ assert_equal "foobar", subclass.create
108
+ end
109
+
110
+ test "create should return result of save" do
111
+ create_simple_domain
112
+ diagram = Class.new(Diagram) do
113
+ save do
114
+ "foobar"
115
+ end
116
+ end.new(Domain.generate)
117
+ assert_equal "foobar", diagram.create
118
+ end
119
+
120
+ # Entity filtering =========================================================
121
+ test "generate should yield entities" do
122
+ create_model "Foo"
123
+ assert_equal [Foo], retrieve_entities.map(&:model)
124
+ end
125
+
126
+ test "generate should filter excluded entity" do
127
+ create_model "Book"
128
+ create_model "Author"
129
+ assert_equal [Book], retrieve_entities(:exclude => [:Author]).map(&:model)
130
+ end
131
+
132
+ test "generate should filter excluded entities" do
133
+ create_model "Book"
134
+ create_model "Author"
135
+ create_model "Editor"
136
+ assert_equal [Book], retrieve_entities(:exclude => [:Author, :Editor]).map(&:model)
137
+ end
138
+
139
+ test "generate should include only specified entity" do
140
+ create_model "Book"
141
+ create_model "Author"
142
+ assert_equal [Book], retrieve_entities(:only => [:Book]).map(&:model)
143
+ end
144
+
145
+ test "generate should include only specified entities" do
146
+ create_model "Book"
147
+ create_model "Author"
148
+ create_model "Editor"
149
+ assert_equal [Author, Editor], retrieve_entities(:only => [:Author, :Editor]).map(&:model)
150
+ end
151
+
152
+ test "generate should include only specified entities (With the class names as strings)" do
153
+ create_model "Book"
154
+ create_model "Author"
155
+ create_model "Editor"
156
+ assert_equal [Author, Editor], retrieve_entities(:only => ['Author', 'Editor']).map(&:model)
157
+ end
158
+
159
+ test "generate should filter disconnected entities if disconnected is false" do
160
+ create_model "Book", :author => :references do
161
+ belongs_to :author
162
+ end
163
+ create_model "Author"
164
+ create_model "Table", :type => :string
165
+ assert_equal [Author, Book], retrieve_entities(:disconnected => false).map(&:model)
166
+ end
167
+
168
+ test "generate should yield disconnected entities if disconnected is true" do
169
+ create_model "Foo", :type => :string
170
+ assert_equal [Foo], retrieve_entities(:disconnected => true).map(&:model)
171
+ end
172
+
173
+ test "generate should filter specialized entities" do
174
+ create_model "Foo", :type => :string
175
+ Object.const_set :SpecialFoo, Class.new(Foo)
176
+ assert_equal [Foo], retrieve_entities.map(&:model)
177
+ end
178
+
179
+ test "generate should yield specialized entities if inheritance is true" do
180
+ create_model "Foo", :type => :string
181
+ Object.const_set :SpecialFoo, Class.new(Foo)
182
+ assert_equal [Foo, SpecialFoo], retrieve_entities(:inheritance => true).map(&:model)
183
+ end
184
+
185
+ test "generate should yield specialized entities with distinct tables" do
186
+ create_model "Foo"
187
+ Object.const_set :SpecialFoo, Class.new(Foo)
188
+ SpecialFoo.class_eval do
189
+ self.table_name = "special_foo"
190
+ end
191
+ create_table "special_foo", {}, true
192
+ assert_equal [Foo, SpecialFoo], retrieve_entities.map(&:model)
193
+ end
194
+
195
+ test "generate should filter generalized entities" do
196
+ create_model "Cannon"
197
+ create_model "Galleon" do
198
+ has_many :cannons, :as => :defensible
199
+ end
200
+ assert_equal ["Cannon", "Galleon"], retrieve_entities.map(&:name)
201
+ end
202
+
203
+ test "generate should yield generalized entities if polymorphism is true" do
204
+ create_model "Cannon"
205
+ create_model "Galleon" do
206
+ has_many :cannons, :as => :defensible
207
+ end
208
+ assert_equal ["Cannon", "Defensible", "Galleon"], retrieve_entities(:polymorphism => true).map(&:name)
209
+ end
210
+
211
+ # Relationship filtering ===================================================
212
+ test "generate should yield relationships" do
213
+ create_simple_domain
214
+ assert_equal 1, retrieve_relationships.length
215
+ end
216
+
217
+ test "generate should yield indirect relationships if indirect is true" do
218
+ create_model "Foo" do
219
+ has_many :bazs
220
+ has_many :bars
221
+ end
222
+ create_model "Bar", :foo => :references do
223
+ belongs_to :foo
224
+ has_many :bazs, :through => :foo
225
+ end
226
+ create_model "Baz", :foo => :references do
227
+ belongs_to :foo
228
+ end
229
+ assert_equal [false, false, true], retrieve_relationships(:indirect => true).map(&:indirect?)
230
+ end
231
+
232
+ test "generate should filter indirect relationships if indirect is false" do
233
+ create_model "Foo" do
234
+ has_many :bazs
235
+ has_many :bars
236
+ end
237
+ create_model "Bar", :foo => :references do
238
+ belongs_to :foo
239
+ has_many :bazs, :through => :foo
240
+ end
241
+ create_model "Baz", :foo => :references do
242
+ belongs_to :foo
243
+ end
244
+ assert_equal [false, false], retrieve_relationships(:indirect => false).map(&:indirect?)
245
+ end
246
+
247
+ test "generate should yield relationships from specialized entities" do
248
+ create_model "Foo", :bar => :references
249
+ create_model "Bar", :type => :string
250
+ Object.const_set :SpecialBar, Class.new(Bar)
251
+ SpecialBar.class_eval do
252
+ has_many :foos
253
+ end
254
+ assert_equal 1, retrieve_relationships.length
255
+ end
256
+
257
+ test "generate should yield relationships to specialized entities" do
258
+ create_model "Foo", :type => :string, :bar => :references
259
+ Object.const_set :SpecialFoo, Class.new(Foo)
260
+ create_model "Bar" do
261
+ has_many :special_foos
262
+ end
263
+ assert_equal 1, retrieve_relationships.length
264
+ end
265
+
266
+ # Specialization filtering =================================================
267
+ test "generate should not yield specializations" do
268
+ create_specialization
269
+ create_polymorphic_generalization
270
+ create_abstract_generalization
271
+ assert_equal [], retrieve_specializations
272
+ end
273
+
274
+ test "generate should yield specializations but not generalizations if inheritance is true" do
275
+ create_specialization
276
+ create_polymorphic_generalization
277
+ create_abstract_generalization
278
+ assert_equal ["Beer"], retrieve_specializations(:inheritance => true).map { |s| s.specialized.name }
279
+ end
280
+
281
+ test "generate should yield generalizations but not specializations if polymorphism is true" do
282
+ create_specialization
283
+ create_polymorphic_generalization
284
+ create_abstract_generalization
285
+ assert_equal ["Galleon", "Palace"], retrieve_specializations(:polymorphism => true).map { |s| s.specialized.name }
286
+ end
287
+
288
+ test "generate should yield specializations and generalizations if polymorphism and inheritance is true" do
289
+ create_specialization
290
+ create_polymorphic_generalization
291
+ create_abstract_generalization
292
+ assert_equal ["Beer", "Galleon", "Palace"], retrieve_specializations(:inheritance => true,
293
+ :polymorphism => true).map { |s| s.specialized.name }
294
+ end
295
+
296
+ # Attribute filtering ======================================================
297
+ test "generate should yield content attributes by default" do
298
+ create_model "Book", :title => :string, :created_at => :datetime, :author => :references do
299
+ belongs_to :author
300
+ end
301
+ create_model "Author"
302
+ assert_equal %w{title}, retrieve_attribute_lists[Book].map(&:name)
303
+ end
304
+
305
+ test "generate should yield primary key attributes if included" do
306
+ create_model "Book", :title => :string
307
+ create_model "Page", :book => :references do
308
+ belongs_to :book
309
+ end
310
+ assert_equal %w{id}, retrieve_attribute_lists(:attributes => [:primary_keys])[Book].map(&:name)
311
+ end
312
+
313
+ test "generate should yield [] if attributes = false" do
314
+ create_model "Book", :title => :string
315
+ create_model "Page", :book => :references do
316
+ belongs_to :book
317
+ end
318
+ assert_equal [], retrieve_attribute_lists(:attributes => [:false])[Book].map(&:name)
319
+ end
320
+
321
+ test "generate should yield foreign key attributes if included" do
322
+ create_model "Book", :author => :references do
323
+ belongs_to :author
324
+ end
325
+ create_model "Author"
326
+ assert_equal %w{author_id}, retrieve_attribute_lists(:attributes => [:foreign_keys])[Book].map(&:name)
327
+ end
328
+
329
+ test "generate should yield timestamp attributes if included" do
330
+ create_model "Book", :created_at => :datetime, :created_on => :date, :updated_at => :datetime, :updated_on => :date
331
+ create_model "Page", :book => :references do
332
+ belongs_to :book
333
+ end
334
+ assert_equal %w{created_at created_on updated_at updated_on},
335
+ retrieve_attribute_lists(:attributes => [:timestamps])[Book].map(&:name)
336
+ end
337
+
338
+ test "generate should yield combinations of attributes if included" do
339
+ create_model "Book", :created_at => :datetime, :title => :string, :author => :references do
340
+ belongs_to :author
341
+ end
342
+ create_model "Author"
343
+ assert_equal %w{created_at title},
344
+ retrieve_attribute_lists(:attributes => [:content, :timestamps])[Book].map(&:name)
345
+ end
346
+
347
+ test "generate should yield no attributes for specialized entities" do
348
+ create_model "Beverage", :type => :string, :name => :string, :distillery => :string, :age => :integer
349
+ Object.const_set :Whisky, Class.new(Beverage)
350
+ assert_equal [], retrieve_attribute_lists(:inheritance => true)[Whisky].map(&:name)
351
+ end
352
+ end