dm-core 0.9.11 → 0.10.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.
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
@@ -0,0 +1,105 @@
1
+ require "benchmark"
2
+
3
+ module DataMapper::Spec
4
+ module AdapterHelpers
5
+ def self.current_adapters
6
+ @current_adapters ||= []
7
+ end
8
+
9
+ def supported_by(*adapters, &block)
10
+ adapters = get_adapters(*adapters)
11
+
12
+ PRIMARY.only(*adapters).each do |adapter, connection_uri|
13
+ # keep track of the current adapters
14
+ AdapterHelpers.current_adapters << adapters
15
+
16
+ describe("with #{adapter}") do
17
+
18
+ before :all do
19
+ # store these in instance vars for the shared adapter specs
20
+ @adapter = DataMapper.setup(:default, connection_uri)
21
+ @repository = DataMapper.repository(@adapter.name)
22
+
23
+ # create all tables and constraints before each spec
24
+ if @repository.respond_to?(:auto_migrate!)
25
+ @repository.auto_migrate!
26
+ end
27
+ end
28
+
29
+ after :all do
30
+ # remove all tables and constraints after each spec
31
+ if DataMapper.respond_to?(:auto_migrate_down!, true)
32
+ DataMapper.send(:auto_migrate_down!, @repository.name)
33
+ end
34
+ end
35
+
36
+ # TODO: add destroy_model_storage and migrations code
37
+ # that removes the YAML file and remove this code
38
+ after :all do
39
+ if defined?(DataMapper::Adapters::YamlAdapter) && @adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
40
+ descendants = DataMapper::Model.descendants.to_a
41
+ while model = descendants.shift
42
+ descendants.concat(model.descendants.to_a - [ model ])
43
+
44
+ model.default_scope.clear
45
+ model.all(:repository => @repository).destroy!
46
+ end
47
+ end
48
+ end
49
+
50
+ self.instance_eval(&block)
51
+ end
52
+
53
+ AdapterHelpers.current_adapters.pop
54
+ end
55
+ end
56
+
57
+ def with_alternate_adapter(&block)
58
+ adapters = AdapterHelpers.current_adapters.last
59
+
60
+ ALTERNATE.only(*adapters).each do |adapter, connection_uri|
61
+ describe("and #{adapter}") do
62
+
63
+ before :all do
64
+ @alternate_adapter = DataMapper.setup(:alternate, connection_uri)
65
+ @alternate_repository = DataMapper.repository(@alternate_adapter.name)
66
+
67
+ # create all tables and constraints before each spec
68
+ if @alternate_repository.respond_to?(:auto_migrate!)
69
+ @alternate_repository.auto_migrate!
70
+ end
71
+ end
72
+
73
+ after :all do
74
+ # remove all tables and constraints after each spec
75
+ if DataMapper.respond_to?(:auto_migrate_down!, true)
76
+ DataMapper.send(:auto_migrate_down!, @alternate_repository.name)
77
+ end
78
+ end
79
+
80
+ # TODO: add destroy_model_storage and migrations code
81
+ # that removes the YAML file and remove this code
82
+ after :all do
83
+ if defined?(DataMapper::Adapters::YamlAdapter) && @alternate_adapter.kind_of?(DataMapper::Adapters::YamlAdapter)
84
+ descendants = DataMapper::Model.descendants.to_a
85
+ while model = descendants.shift
86
+ descendants.concat(model.descendants.to_a - [ model ])
87
+
88
+ model.default_scope.clear
89
+ model.all(:repository => @alternate_repository).destroy!
90
+ end
91
+ end
92
+ end
93
+
94
+ self.instance_eval(&block)
95
+ end
96
+ end
97
+ end
98
+
99
+ def get_adapters(*adapters)
100
+ adapters.map! { |adapter_name| adapter_name.to_s }
101
+ adapters = ADAPTERS if adapters.include?('all')
102
+ ADAPTERS & adapters
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,18 @@
1
+ module DataMapper::Spec
2
+ module CollectionHelpers
3
+ module GroupMethods
4
+ def self.extended(base)
5
+ base.class_inheritable_accessor :loaded
6
+ base.loaded = false
7
+ end
8
+
9
+ def should_not_be_a_kicker
10
+ unless loaded
11
+ it 'should not be a kicker' do
12
+ @articles.should_not be_loaded
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ class CounterAdapter < DataMapper::Adapters::AbstractAdapter
2
+ instance_methods.each { |method| undef_method method unless %w[ __id__ __send__ send class dup object_id kind_of? instance_of? respond_to? equal? assert_kind_of should should_not instance_variable_set instance_variable_get extend ].include?(method.to_s) }
3
+
4
+ attr_reader :counts
5
+
6
+ def kind_of?(klass)
7
+ super || @adapter.kind_of?(klass)
8
+ end
9
+
10
+ def instance_of?(klass)
11
+ super || @adapter.instance_of?(klass)
12
+ end
13
+
14
+ def respond_to?(method, include_private = false)
15
+ super || @adapter.respond_to?(method, include_private)
16
+ end
17
+
18
+ private
19
+
20
+ def initialize(adapter)
21
+ @counts = Hash.new { |hash, key| hash[key] = 0 }
22
+ @adapter = adapter
23
+ @count = 0
24
+ end
25
+
26
+ def increment_count_for(method)
27
+ @counts[method] += 1
28
+ end
29
+
30
+ def method_missing(method, *args, &block)
31
+ increment_count_for(method)
32
+ @adapter.send(method, *args, &block)
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module DataMapper::Spec
2
+ module PendingHelpers
3
+ def pending_if(message, boolean = true)
4
+ if boolean
5
+ pending(message) { yield }
6
+ else
7
+ yield
8
+ end
9
+ end
10
+
11
+ def rescue_if(message, boolean = true)
12
+ if boolean
13
+ raised = nil
14
+ begin
15
+ yield
16
+ raised = false
17
+ rescue Exception
18
+ raised = true
19
+ end
20
+
21
+ raise 'should have raised' if raised == false
22
+ else
23
+ yield
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec/runner/formatter/base_text_formatter'
2
+
3
+ # Code is based on standard SpecdocFormatter, but will print full error details as soon as they are found.
4
+ # Successful or pending examples are written only as a dot in the output. Header is only printed if errors occur.
5
+ #
6
+ # To use it, add the following to your spec/spec.opts:
7
+ # --require
8
+ # lib/rspec_immediate_feedback_formatter.rb
9
+ # --format
10
+ # Spec::Runner::Formatter::ImmediateFeedbackFormatter
11
+
12
+ module Spec
13
+ module Runner
14
+ module Formatter
15
+ class ImmediateFeedbackFormatter < BaseTextFormatter
16
+
17
+ def add_example_group(example_group)
18
+ super
19
+ @current_group = example_group.description
20
+ end
21
+
22
+ def example_failed(example, counter, failure)
23
+ if @current_group
24
+ output.puts
25
+ output.puts @current_group
26
+ @current_group = nil # only print the group name once
27
+ end
28
+
29
+ message = if failure.expectation_not_met?
30
+ "- #{example.description} (FAILED - #{counter})"
31
+ else
32
+ "- #{example.description} (ERROR - #{counter})"
33
+ end
34
+
35
+ output.puts(red(message))
36
+ dump_failure(counter, failure) # dump stacktrace immediately
37
+ output.flush
38
+ end
39
+
40
+ def example_passed(*)
41
+ output.print green('.')
42
+ output.flush
43
+ end
44
+
45
+ def example_pending(*)
46
+ super
47
+ output.print yellow('*')
48
+ output.flush
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,193 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ share_examples_for 'A Limited Many to Many Collection' do
4
+ describe '#destroy!' do
5
+ describe 'on a limited collection' do
6
+ before :all do
7
+ @other = @articles.create
8
+ @limited = @articles.all(:limit => 1)
9
+
10
+ @return = @limited.destroy!
11
+ end
12
+
13
+ it 'should only remove the join resource for the destroyed resource' do
14
+ @join_model.all.should_not be_empty
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # run the specs once with a loaded association and once not
21
+ [ false, true ].each do |loaded|
22
+ describe 'Many to Many Associations with :through => Resource' do
23
+ extend DataMapper::Spec::CollectionHelpers::GroupMethods
24
+
25
+ self.loaded = loaded
26
+
27
+ # define the model prior to supported_by
28
+ before :all do
29
+ module ::Blog
30
+ class Author
31
+ include DataMapper::Resource
32
+
33
+ property :id, Serial
34
+ property :name, String
35
+
36
+ has n, :articles, :through => Resource
37
+ end
38
+
39
+ class Article
40
+ include DataMapper::Resource
41
+
42
+ property :id, Serial
43
+ property :title, String, :nullable => false
44
+ property :content, Text
45
+ property :subtitle, String
46
+
47
+ has n, :authors, :through => Resource
48
+ belongs_to :original, self, :nullable => true
49
+ has n, :revisions, self, :child_key => [ :original_id ]
50
+ has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ]
51
+ has n, :publications, :through => Resource
52
+ end
53
+
54
+ class Publication
55
+ include DataMapper::Resource
56
+
57
+ property :id, Serial
58
+ property :name, String
59
+
60
+ has n, :articles, :through => Resource
61
+ end
62
+ end
63
+
64
+ @author_model = Blog::Author
65
+ @article_model = Blog::Article
66
+ @publication_model = Blog::Publication
67
+
68
+ # initialize the join model
69
+ Blog::Author.relationships(:default)[:articles].through
70
+
71
+ @join_model = Blog::ArticleAuthor
72
+ end
73
+
74
+ supported_by :all do
75
+ before :all do
76
+ @author = @author_model.create(:name => 'Dan Kubb')
77
+
78
+ @original = @author.articles.create(:title => 'Original Article')
79
+ @article = @author.articles.create(:title => 'Sample Article', :content => 'Sample', :original => @original)
80
+ @other = @author.articles.create(:title => 'Other Article', :content => 'Other')
81
+
82
+ # load the targets without references to a single source
83
+ load_collection = lambda do |query|
84
+ @author_model.get(*@author.key).articles(query)
85
+ end
86
+
87
+ @articles = load_collection.call(:title => 'Sample Article')
88
+ @other_articles = load_collection.call(:title => 'Other Article')
89
+
90
+ @articles.entries if loaded
91
+ end
92
+
93
+ it_should_behave_like 'A public Collection'
94
+ it_should_behave_like 'A public Association Collection'
95
+ it_should_behave_like 'A Collection supporting Strategic Eager Loading' unless loaded
96
+ it_should_behave_like 'Finder Interface'
97
+ it_should_behave_like 'A Limited Many to Many Collection'
98
+ end
99
+ end
100
+
101
+ describe 'Many to Many Associations :through => one_to_many' do
102
+ extend DataMapper::Spec::CollectionHelpers::GroupMethods
103
+
104
+ self.loaded = loaded
105
+
106
+ # define the model prior to supported_by
107
+ before :all do
108
+ module ::Blog
109
+ class Author
110
+ include DataMapper::Resource
111
+
112
+ property :id, Serial
113
+ property :name, String
114
+
115
+ has n, :sites
116
+ has n, :articles, :through => :sites
117
+ end
118
+
119
+ class Site
120
+ include DataMapper::Resource
121
+
122
+ property :name, String, :key => true, :default => 'default'
123
+
124
+ belongs_to :author
125
+ has n, :articles
126
+ end
127
+
128
+ class Article
129
+ include DataMapper::Resource
130
+
131
+ property :id, Serial
132
+ property :title, String, :nullable => false
133
+ property :content, Text
134
+ property :subtitle, String
135
+
136
+ property :site_name, String, :default => 'default'
137
+
138
+ belongs_to :site
139
+ has n, :authors, :through => :site
140
+ belongs_to :original, self, :nullable => true
141
+ has n, :revisions, self, :child_key => [ :original_id ]
142
+ has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ]
143
+ has n, :publications, :through => Resource
144
+ end
145
+
146
+ class Publication
147
+ include DataMapper::Resource
148
+
149
+ property :id, Serial
150
+ property :name, String
151
+
152
+ has n, :articles, :through => Resource
153
+ end
154
+ end
155
+
156
+ @author_model = Blog::Author
157
+ @article_model = Blog::Article
158
+ @publication_model = Blog::Publication
159
+
160
+ @join_model = Blog::Site
161
+ end
162
+
163
+ supported_by :all do
164
+ before :all do
165
+ @author = @author_model.create(:name => 'Dan Kubb')
166
+
167
+ @original_site = @author.sites.create(:name => 'original')
168
+ @article_site = @author.sites.create(:name => 'article')
169
+ @other_site = @author.sites.create(:name => 'other')
170
+
171
+ @original = @original_site.articles.create(:title => 'Original Article')
172
+ @article = @article_site.articles.create(:title => 'Sample Article', :content => 'Sample', :original => @original)
173
+ @other = @other_site.articles.create(:title => 'Other Article', :content => 'Other')
174
+
175
+ # load the targets without references to a single source
176
+ load_collection = lambda do |query|
177
+ @author_model.get(*@author.key).articles(query)
178
+ end
179
+
180
+ @articles = load_collection.call(:title => 'Sample Article')
181
+ @other_articles = load_collection.call(:title => 'Other Article')
182
+
183
+ @articles.entries if loaded
184
+ end
185
+
186
+ it_should_behave_like 'A public Collection'
187
+ it_should_behave_like 'A public Association Collection'
188
+ it_should_behave_like 'A Collection supporting Strategic Eager Loading' unless loaded
189
+ it_should_behave_like 'Finder Interface'
190
+ it_should_behave_like 'A Limited Many to Many Collection'
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe 'Many to One Associations' do
4
+ before :all do
5
+ module ::Blog
6
+ class User
7
+ include DataMapper::Resource
8
+
9
+ property :name, String, :key => true
10
+ property :age, Integer
11
+ property :summary, Text
12
+ property :description, Text
13
+ property :admin, Boolean, :accessor => :private
14
+
15
+ belongs_to :referrer, self, :nullable => true
16
+ has n, :comments
17
+
18
+ # FIXME: figure out a different approach than stubbing things out
19
+ def comment=(*)
20
+ # do nothing with comment
21
+ end
22
+ end
23
+
24
+ class Author < User; end
25
+
26
+ class Comment
27
+ include DataMapper::Resource
28
+
29
+ property :id, Serial
30
+ property :body, Text
31
+
32
+ belongs_to :user
33
+ end
34
+
35
+ class Article
36
+ include DataMapper::Resource
37
+
38
+ property :id, Serial
39
+ property :body, Text
40
+
41
+ has n, :paragraphs
42
+ end
43
+
44
+ class Paragraph
45
+ include DataMapper::Resource
46
+
47
+ property :id, Serial
48
+ property :text, String
49
+
50
+ belongs_to :article
51
+ end
52
+ end
53
+
54
+ @user_model = Blog::User
55
+ @author_model = Blog::Author
56
+ @comment_model = Blog::Comment
57
+ @article_model = Blog::Article
58
+ @paragraph_model = Blog::Paragraph
59
+ end
60
+
61
+ supported_by :all do
62
+ before :all do
63
+ user = @user_model.create(:name => 'dbussink', :age => 25, :description => 'Test')
64
+ comment = @comment_model.create(:body => 'Cool spec', :user => user)
65
+
66
+ @comment = @comment_model.get(*comment.key)
67
+ @user = @comment.user
68
+ end
69
+
70
+ it_should_behave_like 'A public Resource'
71
+ it_should_behave_like 'A Resource supporting Strategic Eager Loading'
72
+ end
73
+ end