dm-core 0.9.11 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -50
  5. data/Manifest.txt +66 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +6 -7
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/deps.rip +2 -0
  12. data/dm-core.gemspec +11 -15
  13. data/lib/dm-core.rb +105 -110
  14. data/lib/dm-core/adapters.rb +135 -16
  15. data/lib/dm-core/adapters/abstract_adapter.rb +251 -181
  16. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  17. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  18. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  19. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  20. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  21. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  22. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  23. data/lib/dm-core/associations/many_to_many.rb +372 -90
  24. data/lib/dm-core/associations/many_to_one.rb +220 -73
  25. data/lib/dm-core/associations/one_to_many.rb +319 -255
  26. data/lib/dm-core/associations/one_to_one.rb +66 -53
  27. data/lib/dm-core/associations/relationship.rb +561 -156
  28. data/lib/dm-core/collection.rb +1101 -379
  29. data/lib/dm-core/core_ext/kernel.rb +12 -0
  30. data/lib/dm-core/core_ext/symbol.rb +10 -0
  31. data/lib/dm-core/identity_map.rb +4 -34
  32. data/lib/dm-core/migrations.rb +1283 -0
  33. data/lib/dm-core/model.rb +570 -369
  34. data/lib/dm-core/model/descendant_set.rb +81 -0
  35. data/lib/dm-core/model/hook.rb +45 -0
  36. data/lib/dm-core/model/is.rb +32 -0
  37. data/lib/dm-core/model/property.rb +247 -0
  38. data/lib/dm-core/model/relationship.rb +335 -0
  39. data/lib/dm-core/model/scope.rb +90 -0
  40. data/lib/dm-core/property.rb +808 -273
  41. data/lib/dm-core/property_set.rb +141 -98
  42. data/lib/dm-core/query.rb +1037 -483
  43. data/lib/dm-core/query/conditions/comparison.rb +872 -0
  44. data/lib/dm-core/query/conditions/operation.rb +221 -0
  45. data/lib/dm-core/query/direction.rb +43 -0
  46. data/lib/dm-core/query/operator.rb +84 -0
  47. data/lib/dm-core/query/path.rb +138 -0
  48. data/lib/dm-core/query/sort.rb +45 -0
  49. data/lib/dm-core/repository.rb +210 -94
  50. data/lib/dm-core/resource.rb +641 -421
  51. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  52. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  53. data/lib/dm-core/support/chainable.rb +22 -0
  54. data/lib/dm-core/support/deprecate.rb +12 -0
  55. data/lib/dm-core/support/logger.rb +13 -0
  56. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  57. data/lib/dm-core/transaction.rb +333 -92
  58. data/lib/dm-core/type.rb +98 -60
  59. data/lib/dm-core/types/boolean.rb +1 -1
  60. data/lib/dm-core/types/discriminator.rb +34 -20
  61. data/lib/dm-core/types/object.rb +7 -4
  62. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  63. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  64. data/lib/dm-core/types/serial.rb +3 -3
  65. data/lib/dm-core/types/text.rb +3 -4
  66. data/lib/dm-core/version.rb +1 -1
  67. data/script/performance.rb +102 -109
  68. data/script/profile.rb +169 -38
  69. data/spec/lib/adapter_helpers.rb +105 -0
  70. data/spec/lib/collection_helpers.rb +18 -0
  71. data/spec/lib/counter_adapter.rb +34 -0
  72. data/spec/lib/pending_helpers.rb +27 -0
  73. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  74. data/spec/public/associations/many_to_many_spec.rb +193 -0
  75. data/spec/public/associations/many_to_one_spec.rb +73 -0
  76. data/spec/public/associations/one_to_many_spec.rb +77 -0
  77. data/spec/public/associations/one_to_one_spec.rb +156 -0
  78. data/spec/public/collection_spec.rb +65 -0
  79. data/spec/public/migrations_spec.rb +359 -0
  80. data/spec/public/model/relationship_spec.rb +924 -0
  81. data/spec/public/model_spec.rb +159 -0
  82. data/spec/public/property_spec.rb +829 -0
  83. data/spec/public/resource_spec.rb +71 -0
  84. data/spec/public/sel_spec.rb +44 -0
  85. data/spec/public/setup_spec.rb +145 -0
  86. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  87. data/spec/public/shared/collection_shared_spec.rb +1670 -0
  88. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  89. data/spec/public/shared/resource_shared_spec.rb +924 -0
  90. data/spec/public/shared/sel_shared_spec.rb +112 -0
  91. data/spec/public/transaction_spec.rb +129 -0
  92. data/spec/public/types/discriminator_spec.rb +130 -0
  93. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  94. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  95. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  96. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  97. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  99. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  100. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  101. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  102. data/spec/semipublic/associations_spec.rb +177 -0
  103. data/spec/semipublic/collection_spec.rb +142 -0
  104. data/spec/semipublic/property_spec.rb +61 -0
  105. data/spec/semipublic/query/conditions_spec.rb +528 -0
  106. data/spec/semipublic/query/path_spec.rb +443 -0
  107. data/spec/semipublic/query_spec.rb +2626 -0
  108. data/spec/semipublic/resource_spec.rb +47 -0
  109. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  110. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  111. data/spec/spec.opts +3 -1
  112. data/spec/spec_helper.rb +80 -57
  113. data/tasks/ci.rb +19 -31
  114. data/tasks/dm.rb +43 -48
  115. data/tasks/doc.rb +8 -11
  116. data/tasks/gemspec.rb +5 -5
  117. data/tasks/hoe.rb +15 -16
  118. data/tasks/install.rb +8 -10
  119. metadata +74 -111
  120. data/lib/dm-core/associations.rb +0 -207
  121. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  122. data/lib/dm-core/auto_migrations.rb +0 -105
  123. data/lib/dm-core/dependency_queue.rb +0 -32
  124. data/lib/dm-core/hook.rb +0 -11
  125. data/lib/dm-core/is.rb +0 -16
  126. data/lib/dm-core/logger.rb +0 -232
  127. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  128. data/lib/dm-core/migrator.rb +0 -29
  129. data/lib/dm-core/scope.rb +0 -58
  130. data/lib/dm-core/support.rb +0 -7
  131. data/lib/dm-core/support/array.rb +0 -13
  132. data/lib/dm-core/support/assertions.rb +0 -8
  133. data/lib/dm-core/support/errors.rb +0 -23
  134. data/lib/dm-core/support/kernel.rb +0 -11
  135. data/lib/dm-core/support/symbol.rb +0 -41
  136. data/lib/dm-core/type_map.rb +0 -80
  137. data/lib/dm-core/types.rb +0 -19
  138. data/script/all +0 -4
  139. data/spec/integration/association_spec.rb +0 -1382
  140. data/spec/integration/association_through_spec.rb +0 -203
  141. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  142. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  143. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  144. data/spec/integration/auto_migrations_spec.rb +0 -413
  145. data/spec/integration/collection_spec.rb +0 -1073
  146. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  147. data/spec/integration/dependency_queue_spec.rb +0 -46
  148. data/spec/integration/model_spec.rb +0 -197
  149. data/spec/integration/mysql_adapter_spec.rb +0 -85
  150. data/spec/integration/postgres_adapter_spec.rb +0 -731
  151. data/spec/integration/property_spec.rb +0 -253
  152. data/spec/integration/query_spec.rb +0 -514
  153. data/spec/integration/repository_spec.rb +0 -61
  154. data/spec/integration/resource_spec.rb +0 -513
  155. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  156. data/spec/integration/sti_spec.rb +0 -273
  157. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  158. data/spec/integration/transaction_spec.rb +0 -75
  159. data/spec/integration/type_spec.rb +0 -275
  160. data/spec/lib/logging_helper.rb +0 -18
  161. data/spec/lib/mock_adapter.rb +0 -27
  162. data/spec/lib/model_loader.rb +0 -100
  163. data/spec/lib/publicize_methods.rb +0 -28
  164. data/spec/models/content.rb +0 -16
  165. data/spec/models/vehicles.rb +0 -34
  166. data/spec/models/zoo.rb +0 -48
  167. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  168. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  169. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  170. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  171. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  172. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  173. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  174. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  175. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  176. data/spec/unit/associations/relationship_spec.rb +0 -71
  177. data/spec/unit/associations_spec.rb +0 -242
  178. data/spec/unit/auto_migrations_spec.rb +0 -111
  179. data/spec/unit/collection_spec.rb +0 -182
  180. data/spec/unit/data_mapper_spec.rb +0 -35
  181. data/spec/unit/identity_map_spec.rb +0 -126
  182. data/spec/unit/is_spec.rb +0 -80
  183. data/spec/unit/migrator_spec.rb +0 -33
  184. data/spec/unit/model_spec.rb +0 -321
  185. data/spec/unit/naming_conventions_spec.rb +0 -36
  186. data/spec/unit/property_set_spec.rb +0 -90
  187. data/spec/unit/property_spec.rb +0 -753
  188. data/spec/unit/query_spec.rb +0 -571
  189. data/spec/unit/repository_spec.rb +0 -93
  190. data/spec/unit/resource_spec.rb +0 -649
  191. data/spec/unit/scope_spec.rb +0 -142
  192. data/spec/unit/transaction_spec.rb +0 -493
  193. data/spec/unit/type_map_spec.rb +0 -114
  194. data/spec/unit/type_spec.rb +0 -119
@@ -1,17 +0,0 @@
1
- # TODO: move to dm-more/dm-migrations
2
-
3
- module DataMapper
4
- module DestructiveMigrations
5
- def self.included(model)
6
- DestructiveMigrator.models << model
7
- end
8
- end # module DestructiveMigrations
9
-
10
- class DestructiveMigrator < Migrator
11
- def self.migrate(repository_name)
12
- models.each do |model|
13
- model.auto_migrate!
14
- end
15
- end
16
- end # class DestructiveMigrator
17
- end # module DataMapper
@@ -1,29 +0,0 @@
1
- # TODO: move to dm-more/dm-migrations
2
-
3
- module DataMapper
4
- class Migrator
5
- def self.subclasses
6
- @@subclasses ||= []
7
- end
8
-
9
- def self.subclasses=(obj)
10
- @@subclasses = obj
11
- end
12
-
13
- def self.inherited(klass)
14
- subclasses << klass
15
-
16
- class << klass
17
- def models
18
- @models ||= []
19
- end
20
- end
21
- end
22
-
23
- def self.migrate(repository_name)
24
- subclasses.collect do |migrator|
25
- migrator.migrate(repository_name)
26
- end.flatten
27
- end
28
- end # class Migrator
29
- end # module DataMapper
@@ -1,58 +0,0 @@
1
- module DataMapper
2
- module Scope
3
- Model.append_extensions self
4
-
5
- # @api private
6
- def default_scope(repository_name = nil)
7
- repository_name = self.default_repository_name if repository_name == :default || repository_name.nil?
8
- @default_scope ||= {}
9
- @default_scope[repository_name] ||= {}
10
- end
11
-
12
- # @api private
13
- def query
14
- scope_stack.last
15
- end
16
-
17
- protected
18
-
19
- # @api semipublic
20
- def with_scope(query)
21
- # merge the current scope with the passed in query
22
- with_exclusive_scope(self.query ? self.query.merge(query) : query) {|*block_args| yield(*block_args) }
23
- end
24
-
25
- # @api semipublic
26
- def with_exclusive_scope(query)
27
- query = DataMapper::Query.new(repository, self, query) if query.kind_of?(Hash)
28
-
29
- scope_stack << query
30
-
31
- begin
32
- return yield(query)
33
- ensure
34
- scope_stack.pop
35
- end
36
- end
37
-
38
- private
39
-
40
- # @api private
41
- def merge_with_default_scope(query)
42
- DataMapper::Query.new(query.repository, query.model, default_scope_for_query(query)).update(query)
43
- end
44
-
45
- # @api private
46
- def scope_stack
47
- scope_stack_for = Thread.current[:dm_scope_stack] ||= {}
48
- scope_stack_for[self] ||= []
49
- end
50
-
51
- # @api private
52
- def default_scope_for_query(query)
53
- repository_name = query.repository.name
54
- default_repository_name = query.model.default_repository_name
55
- self.default_scope(default_repository_name).merge(self.default_scope(repository_name))
56
- end
57
- end # module Scope
58
- end # module DataMapper
@@ -1,7 +0,0 @@
1
- dir = Pathname(__FILE__).dirname.expand_path / 'support'
2
-
3
- require dir / 'array'
4
- require dir / 'assertions'
5
- require dir / 'errors'
6
- require dir / 'kernel'
7
- require dir / 'symbol'
@@ -1,13 +0,0 @@
1
- class Array
2
-
3
- ##
4
- # atm it assumes self is an array of [key,value]-arrays
5
- # this is just a better way to make hashes than Hash[*array.flatten]
6
- # since you cannot flatten only one level in ruby 1.8.6
7
- #
8
- def to_hash
9
- h = {}
10
- self.each{ |k,v| h[k] = v }
11
- h
12
- end
13
- end # class Symbol
@@ -1,8 +0,0 @@
1
- module DataMapper
2
- module Assertions
3
- def assert_kind_of(name, value, *klasses)
4
- klasses.each { |k| return if value.kind_of?(k) }
5
- raise ArgumentError, "+#{name}+ should be #{klasses.map { |k| k.name } * ' or '}, but was #{value.class.name}", caller(2)
6
- end
7
- end
8
- end # module DataMapper
@@ -1,23 +0,0 @@
1
- #Some useful errors types
2
- module DataMapper
3
- class ValidationError < StandardError; end
4
-
5
- class ObjectNotFoundError < StandardError; end
6
-
7
- class MaterializationError < StandardError; end
8
-
9
- class RepositoryNotSetupError < StandardError; end
10
-
11
- class IncompleteResourceError < StandardError; end
12
-
13
- class PersistenceError < StandardError; end
14
-
15
- class PluginNotFoundError < StandardError; end
16
- end # module DataMapper
17
-
18
- class StandardError
19
- # Displays the specific error message and the backtrace associated with it.
20
- def display
21
- "#{message}\n\t#{backtrace.join("\n\t")}"
22
- end
23
- end # class StandardError
@@ -1,11 +0,0 @@
1
- module Kernel
2
- # Delegates to DataMapper::repository.
3
- # Will not overwrite if a method of the same name is pre-defined.
4
- def repository(*args)
5
- if block_given?
6
- DataMapper.repository(*args) { |*block_args| yield(*block_args) }
7
- else
8
- DataMapper.repository(*args)
9
- end
10
- end
11
- end # module Kernel
@@ -1,41 +0,0 @@
1
- class Symbol
2
- def gt
3
- DataMapper::Query::Operator.new(self, :gt)
4
- end
5
-
6
- def gte
7
- DataMapper::Query::Operator.new(self, :gte)
8
- end
9
-
10
- def lt
11
- DataMapper::Query::Operator.new(self, :lt)
12
- end
13
-
14
- def lte
15
- DataMapper::Query::Operator.new(self, :lte)
16
- end
17
-
18
- def not
19
- DataMapper::Query::Operator.new(self, :not)
20
- end
21
-
22
- def eql
23
- DataMapper::Query::Operator.new(self, :eql)
24
- end
25
-
26
- def like
27
- DataMapper::Query::Operator.new(self, :like)
28
- end
29
-
30
- def in
31
- DataMapper::Query::Operator.new(self, :in)
32
- end
33
-
34
- def asc
35
- DataMapper::Query::Operator.new(self, :asc)
36
- end
37
-
38
- def desc
39
- DataMapper::Query::Operator.new(self, :desc)
40
- end
41
- end # class Symbol
@@ -1,80 +0,0 @@
1
- # TODO: move to dm-more/dm-migrations
2
-
3
- module DataMapper
4
- class TypeMap
5
-
6
- attr_accessor :parent, :chains
7
-
8
- def initialize(parent = nil, &blk)
9
- @parent, @chains = parent, {}
10
-
11
- blk.call(self) unless blk.nil?
12
- end
13
-
14
- def map(type)
15
- @chains[type] ||= TypeChain.new
16
- end
17
-
18
- def lookup(type)
19
- if type_mapped?(type)
20
- lookup_from_map(type)
21
- else
22
- lookup_by_type(type)
23
- end
24
- end
25
-
26
- def lookup_from_map(type)
27
- lookup_from_parent(type).merge(map(type).translate)
28
- end
29
-
30
- def lookup_from_parent(type)
31
- if !@parent.nil? && @parent.type_mapped?(type)
32
- @parent[type]
33
- else
34
- {}
35
- end
36
- end
37
-
38
- # @raise <DataMapper::TypeMap::Error> if the type is not a default primitive or has a type map entry.
39
- def lookup_by_type(type)
40
- raise DataMapper::TypeMap::Error.new(type) unless type.respond_to?(:primitive) && !type.primitive.nil?
41
-
42
- lookup(type.primitive).merge(Type::PROPERTY_OPTIONS.inject({}) {|h, k| h[k] = type.send(k); h})
43
- end
44
-
45
- alias [] lookup
46
-
47
- def type_mapped?(type)
48
- @chains.has_key?(type) || (@parent.nil? ? false : @parent.type_mapped?(type))
49
- end
50
-
51
- class TypeChain
52
- attr_accessor :primitive, :attributes
53
-
54
- def initialize
55
- @attributes = {}
56
- end
57
-
58
- def to(primitive)
59
- @primitive = primitive
60
- self
61
- end
62
-
63
- def with(attributes)
64
- raise "method 'with' expects a hash" unless attributes.kind_of?(Hash)
65
- @attributes.merge!(attributes)
66
- self
67
- end
68
-
69
- def translate
70
- @attributes.merge((@primitive.nil? ? {} : {:primitive => @primitive}))
71
- end
72
- end # class TypeChain
73
-
74
- class Error < StandardError
75
- def initialize(type)
76
- super("Type #{type} must have a default primitive or type map entry")
77
- end
78
- end
79
- end # class TypeMap
80
- end # module DataMapper
@@ -1,19 +0,0 @@
1
- dir = Pathname(__FILE__).dirname.expand_path / 'types'
2
-
3
- require dir / 'boolean'
4
- require dir / 'discriminator'
5
- require dir / 'text'
6
- require dir / 'paranoid_datetime'
7
- require dir / 'paranoid_boolean'
8
- require dir / 'object'
9
- require dir / 'serial'
10
-
11
- unless defined?(DM)
12
- DM = DataMapper::Types
13
- end
14
-
15
- module DataMapper
16
- module Resource
17
- include Types
18
- end # module Resource
19
- end # module DataMapper
data/script/all DELETED
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env sh
2
- ADAPTER=sqlite3 rake spec
3
- ADAPTER=mysql rake spec
4
- ADAPTER=postgres rake spec
@@ -1,1382 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
-
3
- if HAS_SQLITE3
4
- describe DataMapper::Associations do
5
- before :all do
6
- db1 = File.expand_path(File.join(File.dirname(__FILE__), "custom_db1_sqlite3.db"))
7
- db2 = File.expand_path(File.join(File.dirname(__FILE__), "custom_db2_sqlite3.db"))
8
- FileUtils.touch(db1)
9
- FileUtils.touch(db2)
10
- DataMapper.setup(:custom_db1, "sqlite3://#{db1}")
11
- DataMapper.setup(:custom_db2, "sqlite3://#{db2}")
12
- class ::CustomParent
13
- include DataMapper::Resource
14
- def self.default_repository_name
15
- :custom_db1
16
- end
17
- property :id, Serial
18
- property :name, String
19
- repository(:custom_db2) do
20
- has n, :custom_childs
21
- end
22
- end
23
- class ::CustomChild
24
- include DataMapper::Resource
25
- def self.default_repository_name
26
- :custom_db2
27
- end
28
- property :id, Serial
29
- property :name, String
30
- repository(:custom_db1) do
31
- belongs_to :custom_parent
32
- end
33
- end
34
-
35
- end
36
- before :each do
37
- [ CustomChild, CustomParent ].each { |m| m.auto_migrate! }
38
-
39
- parent = CustomParent.create(:name => "mother")
40
- child1 = parent.custom_childs.create(:name => "son")
41
- child2 = parent.custom_childs.create(:name => "daughter")
42
-
43
- @parent = CustomParent.first(:name => "mother")
44
- @child1 = CustomChild.first(:name => "son")
45
- @child2 = CustomChild.first(:name => "daughter")
46
- end
47
- it "should be able to handle has_many relationships to other repositories" do
48
- @parent.custom_childs.size.should == 2
49
- @parent.custom_childs.include?(@child1).should == true
50
- @parent.custom_childs.include?(@child2).should == true
51
- @parent.custom_childs.delete(@child1)
52
- @parent.custom_childs.save
53
- @parent.reload
54
- @parent.custom_childs.size.should == 1
55
- @parent.custom_childs.include?(@child2).should == true
56
- end
57
- it "should be able to handle belongs_to relationships to other repositories" do
58
- @child1.custom_parent.should == @parent
59
- @child2.custom_parent.should == @parent
60
- @child1.custom_parent = nil
61
- @child1.save
62
- @child1.reload
63
- @child1.custom_parent.should == nil
64
- @parent.reload
65
- @parent.custom_childs.size.should == 1
66
- @parent.custom_childs.include?(@child2).should == true
67
- end
68
- end
69
- end
70
-
71
- if ADAPTER
72
- repository(ADAPTER) do
73
- class ::Machine
74
- include DataMapper::Resource
75
-
76
- def self.default_repository_name
77
- ADAPTER
78
- end
79
-
80
- property :id, Serial
81
- property :name, String
82
-
83
- has n, :areas
84
- has n, :fussy_areas, :class_name => 'Area', :rating.gte => 3, :type => 'particular'
85
- end
86
-
87
- class ::Area
88
- include DataMapper::Resource
89
-
90
- def self.default_repository_name
91
- ADAPTER
92
- end
93
-
94
- property :id, Serial
95
- property :name, String
96
- property :rating, Integer
97
- property :type, String
98
-
99
- belongs_to :machine
100
- end
101
-
102
- class ::Pie
103
- include DataMapper::Resource
104
-
105
- def self.default_repository_name
106
- ADAPTER
107
- end
108
-
109
- property :id, Serial
110
- property :name, String
111
-
112
- belongs_to :sky
113
- end
114
-
115
- class ::Sky
116
- include DataMapper::Resource
117
-
118
- def self.default_repository_name
119
- ADAPTER
120
- end
121
-
122
- property :id, Serial
123
- property :name, String
124
-
125
- has 1, :pie
126
- end
127
-
128
- class ::Ultrahost
129
- include DataMapper::Resource
130
-
131
- def self.default_repository_name
132
- ADAPTER
133
- end
134
-
135
- property :id, Serial
136
- property :name, String
137
-
138
- has n, :ultraslices, :order => [:id.desc]
139
- end
140
-
141
- class ::Ultraslice
142
- include DataMapper::Resource
143
-
144
- def self.default_repository_name
145
- ADAPTER
146
- end
147
-
148
- property :id, Serial
149
- property :name, String
150
-
151
- belongs_to :ultrahost
152
- end
153
-
154
- class ::Node
155
- include DataMapper::Resource
156
-
157
- def self.default_repository_name
158
- ADAPTER
159
- end
160
-
161
- property :id, Serial
162
- property :name, String
163
-
164
- has n, :children, :class_name => 'Node', :child_key => [ :parent_id ]
165
- belongs_to :parent, :class_name => 'Node', :child_key => [ :parent_id ]
166
- end
167
-
168
- class ::MadeUpThing
169
- include DataMapper::Resource
170
-
171
- def self.default_repository_name
172
- ADAPTER
173
- end
174
-
175
- property :id, Serial
176
- property :name, String
177
- belongs_to :area
178
- belongs_to :machine
179
- end
180
-
181
- module ::Models
182
- class Project
183
- include DataMapper::Resource
184
-
185
- def self.default_repository_name
186
- ADAPTER
187
- end
188
-
189
- property :title, String, :length => 255, :key => true
190
- property :summary, DataMapper::Types::Text
191
-
192
- has n, :tasks
193
- has 1, :goal
194
- end
195
-
196
- class Goal
197
- include DataMapper::Resource
198
-
199
- def self.default_repository_name
200
- ADAPTER
201
- end
202
-
203
- property :title, String, :length => 255, :key => true
204
- property :summary, DataMapper::Types::Text
205
-
206
- belongs_to :project
207
- end
208
-
209
- class Task
210
- include DataMapper::Resource
211
-
212
- def self.default_repository_name
213
- ADAPTER
214
- end
215
-
216
- property :title, String, :length => 255, :key => true
217
- property :description, DataMapper::Types::Text
218
-
219
- belongs_to :project
220
- end
221
- end
222
-
223
- class ::Galaxy
224
- include DataMapper::Resource
225
-
226
- def self.default_repository_name
227
- ADAPTER
228
- end
229
-
230
- property :name, String, :key => true, :length => 255
231
- property :size, Float, :key => true, :precision => 15, :scale => 6
232
- end
233
-
234
- class ::Star
235
- include DataMapper::Resource
236
-
237
- def self.default_repository_name
238
- ADAPTER
239
- end
240
-
241
- belongs_to :galaxy
242
- end
243
-
244
- end
245
-
246
- describe DataMapper::Associations do
247
- describe 'namespaced associations' do
248
- before do
249
- Models::Project.auto_migrate!(ADAPTER)
250
- Models::Task.auto_migrate!(ADAPTER)
251
- Models::Goal.auto_migrate!(ADAPTER)
252
- end
253
-
254
- it 'should allow namespaced classes in parent and child for many <=> one' do
255
- m = Models::Project.new(:title => 'p1', :summary => 'sum1')
256
- m.tasks << Models::Task.new(:title => 't1', :description => 'desc 1')
257
- m.save
258
-
259
- t = Models::Task.first(:title => 't1')
260
-
261
- t.project.should_not be_nil
262
- t.project.title.should == 'p1'
263
- t.project.tasks.size.should == 1
264
-
265
- p = Models::Project.first(:title => 'p1')
266
-
267
- p.tasks.size.should == 1
268
- p.tasks[0].title.should == 't1'
269
- end
270
-
271
- it 'should allow namespaced classes in parent and child for one <=> one' do
272
- g = Models::Goal.new(:title => "g2", :summary => "desc 2")
273
- p = Models::Project.create(:title => "p2", :summary => "sum 2", :goal => g)
274
-
275
- pp = Models::Project.first(:title => 'p2')
276
- pp.goal.title.should == "g2"
277
-
278
- g = Models::Goal.first(:title => "g2")
279
-
280
- g.project.should_not be_nil
281
- g.project.title.should == 'p2'
282
-
283
- g.project.goal.should_not be_nil
284
- end
285
- end
286
-
287
- describe 'many to one associations' do
288
- before do
289
- Machine.auto_migrate!(ADAPTER)
290
- Area.auto_migrate!(ADAPTER)
291
- MadeUpThing.auto_migrate!(ADAPTER)
292
-
293
- machine1 = Machine.create(:name => 'machine1')
294
- machine2 = Machine.create(:name => 'machine2')
295
- area1 = Area.create(:name => 'area1', :machine => machine1)
296
- area2 = Area.create(:name => 'area2')
297
- end
298
-
299
- it '#belongs_to' do
300
- area = Area.new
301
- area.should respond_to(:machine)
302
- area.should respond_to(:machine=)
303
- end
304
-
305
- it 'should create the foreign key property immediately' do
306
- class ::Duck
307
- include DataMapper::Resource
308
- property :id, Serial
309
- belongs_to :sky
310
- end
311
- Duck.properties.slice(:sky_id).compact.should_not be_empty
312
- duck = Duck.new
313
- duck.should respond_to(:sky_id)
314
- duck.should respond_to(:sky_id=)
315
- end
316
-
317
- it 'should load without the parent'
318
-
319
- it 'should allow substituting the parent' do
320
- area1 = Area.first(:name => 'area1')
321
- machine2 = Machine.first(:name => 'machine2')
322
-
323
- area1.machine = machine2
324
- area1.save
325
- Area.first(:name => 'area1').machine.should == machine2
326
- end
327
-
328
- it 'should save both the object and parent if both are new' do
329
- area1 = Area.new(:name => 'area1')
330
- area1.machine = Machine.new(:name => 'machine1')
331
- area1.save
332
- area1.machine_id.should == area1.machine.id
333
- end
334
-
335
- it '#belongs_to with namespaced models' do
336
- repository(ADAPTER) do
337
- module ::FlightlessBirds
338
- class Ostrich
339
- include DataMapper::Resource
340
- property :id, Serial
341
- property :name, String
342
- belongs_to :sky # there's something sad about this :'(
343
- end
344
- end
345
-
346
- FlightlessBirds::Ostrich.properties(ADAPTER).slice(:sky_id).compact.should_not be_empty
347
- end
348
- end
349
-
350
- it 'should load the associated instance' do
351
- machine1 = Machine.first(:name => 'machine1')
352
- Area.first(:name => 'area1').machine.should == machine1
353
- end
354
-
355
- it 'should save the association key in the child' do
356
- machine2 = Machine.first(:name => 'machine2')
357
-
358
- Area.create(:name => 'area3', :machine => machine2)
359
- Area.first(:name => 'area3').machine.should == machine2
360
- end
361
-
362
- it 'should set the association key immediately' do
363
- machine = Machine.first(:name => 'machine1')
364
- Area.new(:machine => machine).machine_id.should == machine.id
365
- end
366
-
367
- it "should be able to set an association obtained from another association" do
368
- machine1 = Machine.first(:name => 'machine1')
369
- area1 = Area.first(:name => 'area1')
370
- area1.machine = machine1
371
-
372
- m = MadeUpThing.create(:machine => area1.machine, :name => "Weird")
373
-
374
- m.machine_id.should == machine1.id
375
- end
376
-
377
- it 'should save the parent upon saving of child' do
378
- e = Machine.new(:name => 'machine10')
379
- y = Area.create(:name => 'area10', :machine => e)
380
-
381
- y.machine.name.should == 'machine10'
382
- Machine.first(:name => 'machine10').should_not be_nil
383
- end
384
-
385
- it 'should set and retrieve associations on not yet saved objects' do
386
- e = Machine.create(:name => 'machine10')
387
- y = e.areas.build(:name => 'area10')
388
-
389
- y.machine.name.should == 'machine10'
390
- end
391
-
392
- it 'should convert NULL parent ids into nils' do
393
- Area.first(:name => 'area2').machine.should be_nil
394
- end
395
-
396
- it 'should save nil parents as NULL ids' do
397
- y1 = Area.create(:id => 20, :name => 'area20')
398
- y2 = Area.create(:id => 30, :name => 'area30', :machine => nil)
399
-
400
- y1.id.should == 20
401
- y1.machine.should be_nil
402
- y2.id.should == 30
403
- y2.machine.should be_nil
404
- end
405
-
406
- it 'should respect length on foreign keys' do
407
- property = Star.relationships[:galaxy].child_key[:galaxy_name]
408
- property.length.should == 255
409
- end
410
-
411
- it 'should respect precision and scale on foreign keys' do
412
- property = Star.relationships[:galaxy].child_key[:galaxy_size]
413
- property.precision.should == 15
414
- property.scale.should == 6
415
- end
416
-
417
- it 'should be reloaded when calling Resource#reload' do
418
- e = Machine.new(:name => 'machine40')
419
- y = Area.create(:name => 'area40', :machine => e)
420
-
421
- y.send(:machine_association).should_receive(:reload).once
422
-
423
- lambda { y.reload }.should_not raise_error
424
- end
425
-
426
- it "should have machine when created using machine_id" do
427
- m = Machine.create(:name => 'machineX')
428
- a = Area.new(:machine_id => m.id)
429
- a.machine.should == m
430
- end
431
-
432
- it "should not have a machine when orphaned" do
433
- a = Area.new(:machine_id => 42)
434
- a.machine.should be_nil
435
- end
436
- end
437
-
438
- describe 'one to one associations' do
439
- before do
440
- Sky.auto_migrate!(ADAPTER)
441
- Pie.auto_migrate!(ADAPTER)
442
-
443
- pie1 = Pie.create(:name => 'pie1')
444
- pie2 = Pie.create(:name => 'pie2')
445
- sky1 = Sky.create(:name => 'sky1', :pie => pie1)
446
- end
447
-
448
- it '#has 1' do
449
- s = Sky.new
450
- s.should respond_to(:pie)
451
- s.should respond_to(:pie=)
452
- end
453
-
454
- it 'should allow substituting the child' do
455
- sky1 = Sky.first(:name => 'sky1')
456
- pie1 = Pie.first(:name => 'pie1')
457
- pie2 = Pie.first(:name => 'pie2')
458
-
459
- sky1.pie.should == pie1
460
- pie2.sky.should be_nil
461
-
462
- sky1.pie = pie2
463
- sky1.save
464
-
465
- pie2.sky.should == sky1
466
- pie1.reload.sky.should be_nil
467
- end
468
-
469
- it 'should load the associated instance' do
470
- sky1 = Sky.first(:name => 'sky1')
471
- pie1 = Pie.first(:name => 'pie1')
472
-
473
- sky1.pie.should == pie1
474
- end
475
-
476
- it 'should save the association key in the child' do
477
- pie2 = Pie.first(:name => 'pie2')
478
-
479
- sky2 = Sky.create(:id => 2, :name => 'sky2', :pie => pie2)
480
- pie2.sky.should == sky2
481
- end
482
-
483
- it 'should save the children upon saving of parent' do
484
- p = Pie.new(:id => 10, :name => 'pie10')
485
- s = Sky.create(:id => 10, :name => 'sky10', :pie => p)
486
-
487
- p.sky.should == s
488
-
489
- Pie.first(:name => 'pie10').should_not be_nil
490
- end
491
-
492
- it 'should save nil parents as NULL ids' do
493
- p1 = Pie.create(:id => 20, :name => 'pie20')
494
- p2 = Pie.create(:id => 30, :name => 'pie30', :sky => nil)
495
-
496
- p1.id.should == 20
497
- p1.sky.should be_nil
498
- p2.id.should == 30
499
- p2.sky.should be_nil
500
- end
501
-
502
- it 'should be reloaded when calling Resource#reload' do
503
- pie = Pie.first(:name => 'pie1')
504
- pie.send(:sky_association).should_receive(:reload).once
505
- lambda { pie.reload }.should_not raise_error
506
- end
507
- end
508
-
509
- describe 'one to many associations' do
510
- before do
511
- Ultrahost.auto_migrate!(ADAPTER)
512
- Ultraslice.auto_migrate!(ADAPTER)
513
- Machine.auto_migrate!(ADAPTER)
514
- Area.auto_migrate!(ADAPTER)
515
-
516
- ultrahost1 = Ultrahost.create(:name => 'ultrahost1')
517
- ultrahost2 = Ultrahost.create(:name => 'ultrahost2')
518
- ultraslice1 = Ultraslice.create(:name => 'ultraslice1', :ultrahost => ultrahost1)
519
- ultraslice2 = Ultraslice.create(:name => 'ultraslice2', :ultrahost => ultrahost1)
520
- ultraslice3 = Ultraslice.create(:name => 'ultraslice3')
521
- end
522
-
523
- it '#has n' do
524
- h = Ultrahost.new
525
- h.should respond_to(:ultraslices)
526
- end
527
-
528
- it 'should allow removal of a child through a loaded association' do
529
- ultrahost1 = Ultrahost.first(:name => 'ultrahost1')
530
- ultraslice2 = ultrahost1.ultraslices.first
531
-
532
- ultrahost1.ultraslices.size.should == 2
533
- ultrahost1.ultraslices.delete(ultraslice2)
534
- ultrahost1.ultraslices.size.should == 1
535
-
536
- ultraslice2 = Ultraslice.first(:name => 'ultraslice2')
537
- ultraslice2.ultrahost.should_not be_nil
538
-
539
- ultrahost1.save
540
-
541
- ultraslice2.reload.ultrahost.should be_nil
542
- end
543
-
544
- it 'should use the IdentityMap correctly' do
545
- repository(ADAPTER) do
546
- ultrahost1 = Ultrahost.first(:name => 'ultrahost1')
547
-
548
- ultraslice = ultrahost1.ultraslices.first
549
- ultraslice2 = ultrahost1.ultraslices(:order => [:id]).last # should be the same as 1
550
- ultraslice3 = Ultraslice.get(2) # should be the same as 1
551
-
552
- ultraslice.object_id.should == ultraslice2.object_id
553
- ultraslice.object_id.should == ultraslice3.object_id
554
- end
555
- end
556
-
557
- it '#<< should add exactly the parameters' do
558
- machine = Machine.new(:name => 'my machine')
559
- 4.times do |i|
560
- machine.areas << Area.new(:name => "area nr #{i}")
561
- end
562
- machine.save
563
- machine.areas.size.should == 4
564
- 4.times do |i|
565
- machine.areas.any? do |area|
566
- area.name == "area nr #{i}"
567
- end.should == true
568
- end
569
- machine = Machine.get!(machine.id)
570
- machine.areas.size.should == 4
571
- 4.times do |i|
572
- machine.areas.any? do |area|
573
- area.name == "area nr #{i}"
574
- end.should == true
575
- end
576
- end
577
-
578
- it "#<< should add the correct number of elements if they are created" do
579
- machine = Machine.create(:name => 'my machine')
580
- 4.times do |i|
581
- machine.areas << Area.create(:name => "area nr #{i}", :machine => machine)
582
- end
583
- machine.areas.size.should == 4
584
- end
585
-
586
- it "#build should add exactly one instance of the built record" do
587
- machine = Machine.create(:name => 'my machine')
588
-
589
- original_size = machine.areas.size
590
- machine.areas.build(:name => "an area", :machine => machine)
591
-
592
- machine.areas.size.should == original_size + 1
593
- end
594
-
595
- it '#<< should add default values for relationships that have conditions' do
596
- # it should add default values
597
- machine = Machine.new(:name => 'my machine')
598
- machine.fussy_areas << Area.new(:name => 'area 1', :rating => 4 )
599
- machine.save
600
- Area.first(:name => 'area 1').type.should == 'particular'
601
- # it should not add default values if the condition's property already has a value
602
- machine.fussy_areas << Area.new(:name => 'area 2', :rating => 4, :type => 'not particular')
603
- machine.save
604
- Area.first(:name => 'area 2').type.should == 'not particular'
605
- # it should ignore non :eql conditions
606
- machine.fussy_areas << Area.new(:name => 'area 3')
607
- machine.save
608
- Area.first(:name => 'area 3').rating.should == nil
609
- end
610
-
611
- it 'should load the associated instances, in the correct order' do
612
- ultrahost1 = Ultrahost.first(:name => 'ultrahost1')
613
-
614
- ultrahost1.ultraslices.should_not be_nil
615
- ultrahost1.ultraslices.size.should == 2
616
- ultrahost1.ultraslices.first.name.should == 'ultraslice2' # ordered by [:id.desc]
617
- ultrahost1.ultraslices.last.name.should == 'ultraslice1'
618
-
619
- ultraslice3 = Ultraslice.first(:name => 'ultraslice3')
620
-
621
- ultraslice3.ultrahost.should be_nil
622
- end
623
-
624
- it 'should add and save the associated instance' do
625
- ultrahost1 = Ultrahost.first(:name => 'ultrahost1')
626
- ultrahost1.ultraslices << Ultraslice.new(:id => 4, :name => 'ultraslice4')
627
- ultrahost1.save
628
-
629
- Ultraslice.first(:name => 'ultraslice4').ultrahost.should == ultrahost1
630
- end
631
-
632
- it 'should not save the associated instance if the parent is not saved' do
633
- h = Ultrahost.new(:id => 10, :name => 'ultrahost10')
634
- h.ultraslices << Ultraslice.new(:id => 10, :name => 'ultraslice10')
635
-
636
- Ultraslice.first(:name => 'ultraslice10').should be_nil
637
- end
638
-
639
- it 'should save the associated instance upon saving of parent' do
640
- h = Ultrahost.new(:id => 10, :name => 'ultrahost10')
641
- h.ultraslices << Ultraslice.new(:id => 10, :name => 'ultraslice10')
642
- h.save
643
-
644
- s = Ultraslice.first(:name => 'ultraslice10')
645
-
646
- s.should_not be_nil
647
- s.ultrahost.should == h
648
- end
649
-
650
- it 'should save the associated instances upon saving of parent when mass-assigned' do
651
- h = Ultrahost.create(:id => 10, :name => 'ultrahost10', :ultraslices => [ Ultraslice.new(:id => 10, :name => 'ultraslice10') ])
652
-
653
- s = Ultraslice.first(:name => 'ultraslice10')
654
-
655
- s.should_not be_nil
656
- s.ultrahost.should == h
657
- end
658
-
659
- it 'should have finder-functionality' do
660
- h = Ultrahost.first(:name => 'ultrahost1')
661
-
662
- h.ultraslices.should have(2).entries
663
-
664
- s = h.ultraslices.all(:name => 'ultraslice2')
665
-
666
- s.should have(1).entries
667
- s.first.id.should == 2
668
-
669
- h.ultraslices.first(:name => 'ultraslice2').should == s.first
670
- end
671
-
672
- it 'should be reloaded when calling Resource#reload' do
673
- ultrahost = Ultrahost.first(:name => 'ultrahost1')
674
- ultrahost.send(:ultraslices_association).should_receive(:reload).once
675
- lambda { ultrahost.reload }.should_not raise_error
676
- end
677
- end
678
-
679
- describe 'many-to-one and one-to-many associations combined' do
680
- before do
681
- Node.auto_migrate!(ADAPTER)
682
-
683
- Node.create(:name => 'r1')
684
- Node.create(:name => 'r2')
685
- Node.create(:name => 'r1c1', :parent_id => 1)
686
- Node.create(:name => 'r1c2', :parent_id => 1)
687
- Node.create(:name => 'r1c3', :parent_id => 1)
688
- Node.create(:name => 'r1c1c1', :parent_id => 3)
689
- end
690
-
691
- it 'should properly set #parent' do
692
- r1 = Node.get 1
693
- r1.parent.should be_nil
694
-
695
- n3 = Node.get 3
696
- n3.parent.should == r1
697
-
698
- n6 = Node.get 6
699
- n6.parent.should == n3
700
- end
701
-
702
- it 'should properly set #children' do
703
- r1 = Node.get(1)
704
- off = r1.children
705
- off.size.should == 3
706
- off.include?(Node.get(3)).should be_true
707
- off.include?(Node.get(4)).should be_true
708
- off.include?(Node.get(5)).should be_true
709
- end
710
-
711
- it 'should allow to create root nodes' do
712
- r = Node.create(:name => 'newroot')
713
- r.parent.should be_nil
714
- r.children.size.should == 0
715
- end
716
-
717
- it 'should properly delete nodes' do
718
- r1 = Node.get 1
719
-
720
- r1.children.size.should == 3
721
- r1.children.delete(Node.get(4))
722
- r1.save
723
- Node.get(4).parent.should be_nil
724
- r1.children.size.should == 2
725
- end
726
- end
727
-
728
- describe 'through-associations' do
729
- before :all do
730
- repository(ADAPTER) do
731
- module ::Sweets
732
- class Shop
733
- include DataMapper::Resource
734
- def self.default_repository_name
735
- ADAPTER
736
- end
737
- property :id, Serial
738
- property :name, String
739
- has n, :cakes # has n
740
- has n, :recipes, :through => :cakes # has n => has 1
741
- has n, :ingredients, :through => :cakes # has n => has 1 => has n
742
- has n, :creators, :through => :cakes # has n => has 1 => has 1
743
- has n, :ultraslices, :through => :cakes # has n => has n
744
- has n, :bites, :through => :cakes # has n => has n => has n
745
- has n, :shapes, :through => :cakes # has n => has n => has 1
746
- has n, :customers, :through => :cakes # has n => belongs_to (pending)
747
- has 1, :shop_owner # has 1
748
- has 1, :wife, :through => :shop_owner # has 1 => has 1
749
- has 1, :ring, :through => :shop_owner # has 1 => has 1 => has 1
750
- has n, :coats, :through => :shop_owner # has 1 => has 1 => has n
751
- has n, :children, :through => :shop_owner # has 1 => has n
752
- has n, :toys, :through => :shop_owner # has 1 => has n => has n
753
- has n, :boogers, :through => :shop_owner # has 1 => has n => has 1
754
- end
755
-
756
- class ShopOwner
757
- include DataMapper::Resource
758
- def self.default_repository_name
759
- ADAPTER
760
- end
761
- property :id, Serial
762
- property :name, String
763
- belongs_to :shop
764
- has 1, :wife
765
- has n, :children
766
- has n, :toys, :through => :children
767
- has n, :boogers, :through => :children
768
- has n, :coats, :through => :wife
769
- has 1, :ring, :through => :wife
770
- has n, :schools, :through => :children
771
- end
772
-
773
- class Wife
774
- include DataMapper::Resource
775
- def self.default_repository_name
776
- ADAPTER
777
- end
778
- property :id, Serial
779
- property :name, String
780
- belongs_to :shop_owner
781
- has 1, :ring
782
- has n, :coats
783
- end
784
-
785
- class Coat
786
- include DataMapper::Resource
787
- def self.default_repository_name
788
- ADAPTER
789
- end
790
- property :id, Serial
791
- property :name, String
792
- belongs_to :wife
793
- end
794
-
795
- class Ring
796
- include DataMapper::Resource
797
- def self.default_repository_name
798
- ADAPTER
799
- end
800
- property :id, Serial
801
- property :name, String
802
- belongs_to :wife
803
- end
804
-
805
- class Child
806
- include DataMapper::Resource
807
- def self.default_repository_name
808
- ADAPTER
809
- end
810
- property :id, Serial
811
- property :name, String
812
- belongs_to :shop_owner
813
- has n, :toys
814
- has 1, :booger
815
- end
816
-
817
- class Booger
818
- include DataMapper::Resource
819
- def self.default_repository_name
820
- ADAPTER
821
- end
822
- property :id, Serial
823
- property :name, String
824
- belongs_to :child
825
- end
826
-
827
- class Toy
828
- include DataMapper::Resource
829
- def self.default_repository_name
830
- ADAPTER
831
- end
832
- property :id, Serial
833
- property :name, String
834
- belongs_to :child
835
- end
836
-
837
- class Cake
838
- include DataMapper::Resource
839
- def self.default_repository_name
840
- ADAPTER
841
- end
842
- property :id, Serial
843
- property :name, String
844
- belongs_to :shop
845
- belongs_to :customer
846
- has n, :ultraslices
847
- has n, :bites, :through => :ultraslices
848
- has 1, :recipe
849
- has n, :ingredients, :through => :recipe
850
- has 1, :creator, :through => :recipe
851
- has n, :shapes, :through => :ultraslices
852
- end
853
-
854
- class Recipe
855
- include DataMapper::Resource
856
- def self.default_repository_name
857
- ADAPTER
858
- end
859
- property :id, Serial
860
- property :name, String
861
- belongs_to :cake
862
- has n, :ingredients
863
- has 1, :creator
864
- end
865
-
866
- class Customer
867
- include DataMapper::Resource
868
- def self.default_repository_name
869
- ADAPTER
870
- end
871
- property :id, Serial
872
- property :name, String
873
- has n, :cakes
874
- end
875
-
876
- class Creator
877
- include DataMapper::Resource
878
- def self.default_repository_name
879
- ADAPTER
880
- end
881
- property :id, Serial
882
- property :name, String
883
- belongs_to :recipe
884
- end
885
-
886
- class Ingredient
887
- include DataMapper::Resource
888
- def self.default_repository_name
889
- ADAPTER
890
- end
891
- property :id, Serial
892
- property :name, String
893
- belongs_to :recipe
894
- end
895
-
896
- class Ultraslice
897
- include DataMapper::Resource
898
- def self.default_repository_name
899
- ADAPTER
900
- end
901
- property :id, Serial
902
- property :size, Integer
903
- belongs_to :cake
904
- has n, :bites
905
- has 1, :shape
906
- end
907
-
908
- class Shape
909
- include DataMapper::Resource
910
- def self.default_repository_name
911
- ADAPTER
912
- end
913
- property :id, Serial
914
- property :name, String
915
- belongs_to :ultraslice
916
- end
917
-
918
- class Bite
919
- include DataMapper::Resource
920
- def self.default_repository_name
921
- ADAPTER
922
- end
923
- property :id, Serial
924
- property :name, String
925
- belongs_to :ultraslice
926
- end
927
-
928
- DataMapper::Resource.descendants.each do |descendant|
929
- descendant.auto_migrate!(ADAPTER) if descendant.name =~ /^Sweets::/
930
- end
931
-
932
- betsys = Shop.new(:name => "Betsy's")
933
- betsys.save
934
-
935
- #
936
- # has n
937
- #
938
-
939
- german_chocolate = Cake.new(:name => 'German Chocolate')
940
- betsys.cakes << german_chocolate
941
- german_chocolate.save
942
- short_cake = Cake.new(:name => 'Short Cake')
943
- betsys.cakes << short_cake
944
- short_cake.save
945
-
946
- # has n => belongs_to
947
-
948
- old_customer = Customer.new(:name => 'John Johnsen')
949
- old_customer.cakes << german_chocolate
950
- old_customer.cakes << short_cake
951
- german_chocolate.save
952
- short_cake.save
953
- old_customer.save
954
-
955
- # has n => has 1
956
-
957
- schwarzwald = Recipe.new(:name => 'Schwarzwald Cake')
958
- schwarzwald.save
959
- german_chocolate.recipe = schwarzwald
960
- german_chocolate.save
961
- shortys_special = Recipe.new(:name => "Shorty's Special")
962
- shortys_special.save
963
- short_cake.recipe = shortys_special
964
- short_cake.save
965
-
966
- # has n => has 1 => has 1
967
-
968
- runar = Creator.new(:name => 'Runar')
969
- schwarzwald.creator = runar
970
- runar.save
971
- berit = Creator.new(:name => 'Berit')
972
- shortys_special.creator = berit
973
- berit.save
974
-
975
- # has n => has 1 => has n
976
-
977
- 4.times do |i| schwarzwald.ingredients << Ingredient.new(:name => "Secret ingredient nr #{i}") end
978
- 6.times do |i| shortys_special.ingredients << Ingredient.new(:name => "Well known ingredient nr #{i}") end
979
-
980
- # has n => has n
981
-
982
- 10.times do |i| german_chocolate.ultraslices << Ultraslice.new(:size => i) end
983
- 5.times do |i| short_cake.ultraslices << Ultraslice.new(:size => i) end
984
- german_chocolate.ultraslices.size.should == 10
985
- # has n => has n => has 1
986
-
987
- german_chocolate.ultraslices.each do |ultraslice|
988
- shape = Shape.new(:name => 'square')
989
- ultraslice.shape = shape
990
- shape.save
991
- end
992
- short_cake.ultraslices.each do |ultraslice|
993
- shape = Shape.new(:name => 'round')
994
- ultraslice.shape = shape
995
- shape.save
996
- end
997
-
998
- # has n => has n => has n
999
- german_chocolate.ultraslices.each do |ultraslice|
1000
- 6.times do |i|
1001
- ultraslice.bites << Bite.new(:name => "Big bite nr #{i}")
1002
- end
1003
- end
1004
- short_cake.ultraslices.each do |ultraslice|
1005
- 3.times do |i|
1006
- ultraslice.bites << Bite.new(:name => "Small bite nr #{i}")
1007
- end
1008
- end
1009
-
1010
- #
1011
- # has 1
1012
- #
1013
-
1014
- betsy = ShopOwner.new(:name => 'Betsy')
1015
- betsys.shop_owner = betsy
1016
- betsys.save
1017
-
1018
- # has 1 => has 1
1019
-
1020
- barry = Wife.new(:name => 'Barry')
1021
- betsy.wife = barry
1022
- barry.save
1023
-
1024
- # has 1 => has 1 => has 1
1025
-
1026
- golden = Ring.new(:name => 'golden')
1027
- barry.ring = golden
1028
- golden.save
1029
-
1030
- # has 1 => has 1 => has n
1031
-
1032
- 3.times do |i|
1033
- barry.coats << Coat.new(:name => "Fancy coat nr #{i}")
1034
- end
1035
- barry.save
1036
-
1037
- # has 1 => has n
1038
-
1039
- 5.times do |i|
1040
- betsy.children << Child.new(:name => "Snotling nr #{i}")
1041
- end
1042
- betsy.save
1043
-
1044
- # has 1 => has n => has n
1045
-
1046
- betsy.children.each do |child|
1047
- 4.times do |i|
1048
- child.toys << Toy.new(:name => "Cheap toy nr #{i}")
1049
- end
1050
- child.save
1051
- end
1052
-
1053
- # has 1 => has n => has 1
1054
-
1055
- betsy.children.each do |child|
1056
- booger = Booger.new(:name => 'Nasty booger')
1057
- child.booger = booger
1058
- child.save
1059
- end
1060
- end
1061
- end
1062
- end
1063
-
1064
- #
1065
- # has n
1066
- #
1067
-
1068
- it 'should return the right children for has n => has n relationships' do
1069
- Sweets::Shop.first.ultraslices.size.should == 15
1070
- 10.times do |i|
1071
- Sweets::Shop.first.ultraslices.select do |ultraslice|
1072
- ultraslice.cake == Sweets::Cake.first(:name => 'German Chocolate') && ultraslice.size == i
1073
- end
1074
- end
1075
- end
1076
-
1077
- it 'should return the right children for has n => has n => has 1' do
1078
- Sweets::Shop.first.shapes.size.should == 15
1079
- Sweets::Shop.first.shapes.select do |shape|
1080
- shape.name == 'square'
1081
- end.size.should == 10
1082
- Sweets::Shop.first.shapes.select do |shape|
1083
- shape.name == 'round'
1084
- end.size.should == 5
1085
- end
1086
-
1087
- it 'should return the right children for has n => has n => has n' do
1088
- Sweets::Shop.first.bites.size.should == 75
1089
- Sweets::Shop.first.bites.select do |bite|
1090
- bite.ultraslice.cake == Sweets::Cake.first(:name => 'German Chocolate')
1091
- end.size.should == 60
1092
- Sweets::Shop.first.bites.select do |bite|
1093
- bite.ultraslice.cake == Sweets::Cake.first(:name => 'Short Cake')
1094
- end.size.should == 15
1095
- end
1096
-
1097
- it 'should return the right children for has n => belongs_to relationships' do
1098
- Sweets::Customer.first.cakes.size.should == 2
1099
- customers = Sweets::Shop.first.customers.select do |customer|
1100
- customer.name == 'John Johnsen'
1101
- end
1102
- customers.size.should == 1
1103
- end
1104
-
1105
- it 'should return the right children for has n => has 1 relationships' do
1106
- Sweets::Shop.first.recipes.size.should == 2
1107
- Sweets::Shop.first.recipes.select do |recipe|
1108
- recipe.name == 'Schwarzwald Cake'
1109
- end.size.should == 1
1110
- Sweets::Shop.first.recipes.select do |recipe|
1111
- recipe.name == "Shorty's Special"
1112
- end.size.should == 1
1113
- end
1114
-
1115
- it 'should return the right children for has n => has 1 => has 1 relationships' do
1116
- Sweets::Shop.first.creators.size.should == 2
1117
- Sweets::Shop.first.creators.any? do |creator|
1118
- creator.name == 'Runar'
1119
- end.should == true
1120
- Sweets::Shop.first.creators.any? do |creator|
1121
- creator.name == 'Berit'
1122
- end.should == true
1123
- end
1124
-
1125
- it 'should return the right children for has n => has 1 => has n relationships' do
1126
- Sweets::Shop.first.ingredients.size.should == 10
1127
- 4.times do |i|
1128
- Sweets::Shop.first.ingredients.any? do |ingredient|
1129
- ingredient.name == "Secret ingredient nr #{i}" && ingredient.recipe.cake == Sweets::Cake.first(:name => 'German Chocolate')
1130
- end.should == true
1131
- end
1132
- 6.times do |i|
1133
- Sweets::Shop.first.ingredients.any? do |ingredient|
1134
- ingredient.name == "Well known ingredient nr #{i}" && ingredient.recipe.cake == Sweets::Cake.first(:name => 'Short Cake')
1135
- end.should == true
1136
- end
1137
- end
1138
-
1139
- #
1140
- # has 1
1141
- #
1142
-
1143
- it 'should return the right children for has 1 => has 1 relationships' do
1144
- Sweets::Shop.first.wife.should == Sweets::Wife.first
1145
- end
1146
-
1147
- it 'should return the right children for has 1 => has 1 => has 1 relationships' do
1148
- Sweets::Shop.first.ring.should == Sweets::Ring.first
1149
- end
1150
-
1151
- it 'should return the right children for has 1 => has 1 => has n relationships' do
1152
- Sweets::Shop.first.coats.size.should == 3
1153
- 3.times do |i|
1154
- Sweets::Shop.first.coats.any? do |coat|
1155
- coat.name == "Fancy coat nr #{i}"
1156
- end.should == true
1157
- end
1158
- end
1159
-
1160
- it 'should return the right children for has 1 => has n relationships' do
1161
- Sweets::Shop.first.children.size.should == 5
1162
- 5.times do |i|
1163
- Sweets::Shop.first.children.any? do |child|
1164
- child.name == "Snotling nr #{i}"
1165
- end.should == true
1166
- end
1167
- end
1168
-
1169
- it 'should return the right children for has 1 => has n => has 1 relationships' do
1170
- Sweets::Shop.first.boogers.size.should == 5
1171
- Sweets::Shop.first.boogers.inject(Set.new) do |sum, booger|
1172
- sum << booger.child_id
1173
- end.size.should == 5
1174
- end
1175
-
1176
- it 'should return the right children for has 1 => has n => has n relationships' do
1177
- Sweets::Shop.first.toys.size.should == 20
1178
- 5.times do |child_nr|
1179
- 4.times do |toy_nr|
1180
- Sweets::Shop.first.toys.any? do |toy|
1181
- toy.name == "Cheap toy nr #{toy_nr}" && toy.child = Sweets::Child.first(:name => "Snotling nr #{child_nr}")
1182
- end.should == true
1183
- end
1184
- end
1185
- end
1186
-
1187
- #
1188
- # misc
1189
- #
1190
-
1191
- it 'should join tables in the right order during has 1 => has n => has 1 queries' do
1192
- child = Sweets::Shop.first.children(:name => 'Snotling nr 3').booger(:name.like => 'Nasty booger')
1193
- child.should_not be_nil
1194
- child.size.should eql(1)
1195
- child.first.name.should eql("Nasty booger")
1196
- end
1197
-
1198
- it 'should join tables in the right order for belongs_to relations' do
1199
- wife = Sweets::Wife.first(Sweets::Wife.shop_owner.name => "Betsy", Sweets::Wife.shop_owner.shop.name => "Betsy's")
1200
- wife.should_not be_nil
1201
- wife.name.should eql("Barry")
1202
- end
1203
-
1204
- it 'should raise exception if you try to change it' do
1205
- lambda do
1206
- Sweets::Shop.first.wife = Sweets::Wife.new(:name => 'Larry')
1207
- end.should raise_error(DataMapper::Associations::ImmutableAssociationError)
1208
- end
1209
-
1210
- it 'should be reloaded when calling Resource#reload' do
1211
- betsys = Sweets::Shop.first(:name => "Betsy's")
1212
- betsys.send(:customers_association).should_receive(:reload).once
1213
- lambda { betsys.reload }.should_not raise_error
1214
- end
1215
-
1216
- end
1217
-
1218
- if false # Many to many not yet implemented
1219
- describe "many to many associations" do
1220
- before(:all) do
1221
- class ::RightItem
1222
- include DataMapper::Resource
1223
-
1224
- def self.default_repository_name
1225
- ADAPTER
1226
- end
1227
-
1228
- property :id, Serial
1229
- property :name, String
1230
-
1231
- has n..n, :left_items
1232
- end
1233
-
1234
- class ::LeftItem
1235
- include DataMapper::Resource
1236
-
1237
- def self.default_repository_name
1238
- ADAPTER
1239
- end
1240
-
1241
- property :id, Serial
1242
- property :name, String
1243
-
1244
- has n..n, :right_items
1245
- end
1246
-
1247
- RightItem.auto_migrate!
1248
- LeftItem.auto_migrate!
1249
- end
1250
-
1251
- def create_item_pair(number)
1252
- @ri = RightItem.new(:name => "ri#{number}")
1253
- @li = LeftItem.new(:name => "li#{number}")
1254
- end
1255
-
1256
- it "should add to the association from the left" do
1257
- pending "Waiting on Many To Many to be implemented" do
1258
- create_item_pair "0000"
1259
- @ri.save; @li.save
1260
- @ri.should_not be_new_record
1261
- @li.should_not be_new_record
1262
-
1263
- @li.right_items << @ri
1264
- @li.right_items.should include(@ri)
1265
- @li.reload
1266
- @ri.reload
1267
- @li.right_items.should include(@ri)
1268
- end
1269
- end
1270
-
1271
- it "should add to the association from the right" do
1272
- create_item_pair "0010"
1273
- @ri.save; @li.save
1274
- @ri.should_not be_new_record
1275
- @li.should_not be_new_record
1276
-
1277
- @ri.left_items << @li
1278
- @ri.left_items.should include(@li)
1279
- @li.reload
1280
- @ri.reload
1281
- @ri.left_items.should include(@li)
1282
- end
1283
-
1284
- it "should load the associated collection from the either side" do
1285
- pending "Waiting on Many To Many to be implemented" do
1286
- create_item_pair "0020"
1287
- @ri.save; @li.save
1288
- @ri.left_items << @li
1289
- @ri.reload; @li.reload
1290
-
1291
- @ri.left_items.should include(@li)
1292
- @li.right_items.should include(@ri)
1293
- end
1294
- end
1295
-
1296
- it "should load the associated collection from the right" do
1297
- pending "Waiting on Many To Many to be implemented" do
1298
- create_item_pair "0030"
1299
- @ri.save; @li.save
1300
- @li.right_items << @li
1301
- @ri.reload; @li.reload
1302
-
1303
- @ri.left_items.should include(@li)
1304
- @li.right_items.should include(@ri)
1305
- end
1306
- end
1307
-
1308
- it "should save the left side of the association if new record" do
1309
- pending "Waiting on Many To Many to be implemented" do
1310
- create_item_pair "0040"
1311
- @ri.save
1312
- @li.should be_new_record
1313
- @ri.left_items << @li
1314
- @li.should_not be_new_record
1315
- end
1316
- end
1317
-
1318
- it "should save the right side of the association if new record" do
1319
- pending "Waiting on Many To Many to be implemented" do
1320
- create_item_pair "0050"
1321
- @li.save
1322
- @ri.should be_new_record
1323
- @li.right_items << @ri
1324
- @ri.should_not be_new_record
1325
- end
1326
- end
1327
-
1328
- it "should save both side of the association if new record" do
1329
- pending "Waiting on Many To Many to be implemented" do
1330
- create_item_pair "0060"
1331
- @li.should be_new_record
1332
- @ri.should be_new_record
1333
- @ri.left_items << @li
1334
- @ri.should_not be_new_record
1335
- @li.should_not be_new_record
1336
- end
1337
- end
1338
-
1339
- it "should remove an item from the left collection without destroying the item" do
1340
- pending "Waiting on Many To Many to be implemented" do
1341
- create_item_pair "0070"
1342
- @li.save; @ri.save
1343
- @ri.left_items << @li
1344
- @ri.reload; @li.reload
1345
- @ri.left_items.should include(@li)
1346
- @ri.left_items.delete(@li)
1347
- @ri.left_items.should_not include(@li)
1348
- @li.reload
1349
- LeftItem.get(@li.id).should_not be_nil
1350
- end
1351
- end
1352
-
1353
- it "should remove an item from the right collection without destroying the item" do
1354
- pending "Waiting on Many To Many to be implemented" do
1355
- create_item_pair "0080"
1356
- @li.save; @ri.save
1357
- @li.right_items << @ri
1358
- @li.reload; @ri.reload
1359
- @li.right_items.should include(@ri)
1360
- @li.right_items.delete(@ri)
1361
- @li.right_items.should_not include(@ri)
1362
- @ri.reload
1363
- RightItem.get(@ri.id).should_not be_nil
1364
- end
1365
- end
1366
-
1367
- it "should remove the item from the collection when an item is deleted" do
1368
- pending "Waiting on Many To Many to be implemented" do
1369
- create_item_pair "0090"
1370
- @li.save; @ri.save
1371
- @ri.left_items << @li
1372
- @ri.reload; @li.reload
1373
- @ri.left_items.should include(@li)
1374
- @li.destroy
1375
- @ri.reload
1376
- @ri.left_items.should_not include(@li)
1377
- end
1378
- end
1379
- end
1380
- end
1381
- end
1382
- end