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
@@ -1,18 +0,0 @@
1
- module LoggingHelper
2
- def logger
3
- class << DataMapper.logger
4
- attr_writer :log
5
- end
6
-
7
- old_log = DataMapper.logger.log
8
-
9
- begin
10
- StringIO.new('') do |io|
11
- DataMapper.logger.log = io
12
- yield io
13
- end
14
- ensure
15
- DataMapper.logger.log = old_log
16
- end
17
- end
18
- end
@@ -1,27 +0,0 @@
1
- module DataMapper
2
- module Adapters
3
- class MockAdapter < DataMapper::Adapters::DataObjectsAdapter
4
-
5
- def create(resources)
6
- 1
7
- end
8
-
9
- def exists?(storage_name)
10
- true
11
- end
12
-
13
- end
14
- end
15
- end
16
-
17
- module DataObjects
18
- module Mock
19
-
20
- def self.logger
21
- end
22
-
23
- def self.logger=(value)
24
- end
25
-
26
- end
27
- end
@@ -1,100 +0,0 @@
1
- # ---
2
- # Overview
3
- # ========
4
- # ModelLoader is a method for loading methods models for specs in a way
5
- # that will ensure that each spec will be an a pristine state when run.
6
- #
7
- # The problem is that if a spec needs to modify a model, the modifications
8
- # should not carry over to the next spec. As such, all models are
9
- # destroyed at the end of the spec and reloaded at the start.
10
- #
11
- # The second problem is that DataMapper::Resource keeps track
12
- # of every class that it is included in. This is used for automigration.
13
- # A number of specs run automigrate, and we don't want all the classes
14
- # that were defined in other specs to be migrated as well.
15
- #
16
- # Usage
17
- # =====
18
- #
19
- # Sets the specified model metaphors to be loaded before each spec and
20
- # destroyed after each spec in the current example group. This method
21
- # can be used in a describe block or in a before block.
22
- #
23
- # ==== Parameters
24
- # *metaphor<Symbol>:: The name of the metaphor to load (this is just the filename of
25
- # file in specs/models)
26
- #
27
- # ==== Example
28
- #
29
- # describe "DataMapper::Associations" do
30
- #
31
- # load_models_for_metaphor :zoo, :blog
32
- #
33
- # it "should be awesome" do
34
- # Zoo.new.should be_awesome
35
- # end
36
- # end
37
- module ModelLoader
38
-
39
- def self.included(base)
40
- base.extend(ClassMethods)
41
- base.class_eval { include InstanceMethods }
42
- # base.before(:each) { load_models(:global) }
43
- base.after(:each) { unload_models }
44
- end
45
-
46
- module ClassMethods
47
-
48
- def load_models_for_metaphor(*metaphors)
49
- before(:each) { load_models_for_metaphor(*metaphors) }
50
- end
51
-
52
- end
53
-
54
- module InstanceMethods
55
-
56
- def load_models_for_metaphor(*metaphors)
57
- files = metaphors.map { |m| DataMapper.root / "spec" / "models" / "#{m}.rb" }
58
-
59
- klasses = object_space_classes.dup
60
- files.each { |file| load file }
61
- loaded_models.concat(object_space_classes - klasses)
62
- end
63
-
64
- def unload_models
65
- while model = loaded_models.pop
66
- remove_model(model)
67
- end
68
- end
69
-
70
- def loaded_models
71
- @loaded_models ||= []
72
- end
73
-
74
- private
75
-
76
- def object_space_classes
77
- klasses = []
78
- ObjectSpace.each_object(Class) {|o| klasses << o}
79
- klasses
80
- end
81
-
82
- def remove_model(klass)
83
- DataMapper::Resource.descendants.delete(klass)
84
- # Check to see if the model is living inside a module
85
- klass_name = klass.to_s
86
- if klass_name.index("::")
87
- mod = klass_name.match(/(\S+)::/)[1]
88
- child_class = klass_name.match(/\S+::(\S+)/)[1]
89
-
90
- Object.const_get(mod).module_eval { remove_const child_class }
91
- else
92
- Object.module_eval { remove_const klass.to_s }
93
- end
94
- end
95
- end
96
- end
97
-
98
- Spec::Runner.configure do |config|
99
- config.include(ModelLoader)
100
- end
@@ -1,28 +0,0 @@
1
- class Class
2
- def publicize_methods
3
- klass = class << self; self; end
4
-
5
- saved_private_class_methods = klass.private_instance_methods
6
- saved_protected_class_methods = klass.protected_instance_methods
7
- saved_private_instance_methods = self.private_instance_methods
8
- saved_protected_instance_methods = self.protected_instance_methods
9
-
10
- self.class_eval do
11
- klass.send(:public, *saved_private_class_methods)
12
- klass.send(:public, *saved_protected_class_methods)
13
- public(*saved_private_instance_methods)
14
- public(*saved_protected_instance_methods)
15
- end
16
-
17
- begin
18
- yield
19
- ensure
20
- self.class_eval do
21
- klass.send(:private, *saved_private_class_methods)
22
- klass.send(:protected, *saved_protected_class_methods)
23
- private(*saved_private_instance_methods)
24
- protected(*saved_protected_instance_methods)
25
- end
26
- end
27
- end
28
- end
@@ -1,16 +0,0 @@
1
- module Content
2
- class Dialect
3
- include DataMapper::Resource
4
-
5
- property :id, Serial
6
- property :name, String
7
- property :code, String
8
- end
9
-
10
- class Locale
11
- include DataMapper::Resource
12
-
13
- property :id, Serial
14
- property :name, String
15
- end
16
- end
@@ -1,34 +0,0 @@
1
- # ==========================
2
- # Used for Association specs
3
- # ---
4
- # These models will probably
5
- # end up removed. So, I wouldn't
6
- # use this metaphor
7
- class Vehicle
8
- include DataMapper::Resource
9
-
10
- property :id, Serial
11
- property :name, String
12
-
13
- class << self
14
- attr_accessor :mock_relationship
15
- end
16
- end
17
-
18
- class Manufacturer
19
- include DataMapper::Resource
20
-
21
- property :id, Serial
22
- property :name, String
23
-
24
- class << self
25
- attr_accessor :mock_relationship
26
- end
27
- end
28
-
29
- class Supplier
30
- include DataMapper::Resource
31
-
32
- property :id, Serial
33
- property :name, String
34
- end
@@ -1,48 +0,0 @@
1
- class Zoo
2
- include DataMapper::Resource
3
-
4
- property :id, Serial
5
- property :name, String
6
- property :description, Text
7
- property :inception, DateTime
8
- property :open, Boolean, :default => false
9
- property :size, Integer
10
- property :mission, Text, :writer => :protected
11
-
12
- has n, :animals
13
-
14
- def to_s
15
- name
16
- end
17
- end
18
-
19
- class Species
20
- include DataMapper::Resource
21
-
22
- property :id, Serial
23
- property :name, String
24
- property :classification, String, :reader => :private
25
-
26
- has n, :animals
27
- end
28
-
29
- class Animal
30
- include DataMapper::Resource
31
-
32
- property :id, Serial
33
- property :name, String
34
-
35
- belongs_to :zoo
36
- belongs_to :species
37
- belongs_to :keeper
38
- end
39
-
40
- class Employee
41
- include DataMapper::Resource
42
-
43
- property :name, String, :key => true
44
- end
45
-
46
- class Keeper < Employee
47
- has n, :animals
48
- end
@@ -1,133 +0,0 @@
1
- require 'monitor'
2
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
3
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'adapters', 'adapter_shared_spec'))
4
-
5
- describe DataMapper::Adapters::AbstractAdapter do
6
-
7
- before do
8
- @adapter = DataMapper::Adapters::AbstractAdapter.new(:default, 'mock_uri_string')
9
- end
10
-
11
- it_should_behave_like 'a DataMapper Adapter'
12
-
13
- describe "when handling transactions" do
14
- before :each do
15
- @transaction = DataMapper::Transaction.new(@adapter)
16
- end
17
- it "should be able to push and pop transactions on the current stack" do
18
- @adapter.current_transaction.should == nil
19
- @adapter.within_transaction?.should == false
20
- @adapter.push_transaction(@transaction)
21
- @adapter.current_transaction.should == @transaction
22
- @adapter.within_transaction?.should == true
23
- @adapter.push_transaction(@transaction)
24
- @adapter.current_transaction.should == @transaction
25
- @adapter.within_transaction?.should == true
26
- @adapter.pop_transaction
27
- @adapter.current_transaction.should == @transaction
28
- @adapter.within_transaction?.should == true
29
- @adapter.pop_transaction
30
- @adapter.current_transaction.should == nil
31
- @adapter.within_transaction?.should == false
32
- end
33
- it "should let each Thread have its own transaction stack" do
34
- lock = Monitor.new
35
- transaction2 = DataMapper::Transaction.new(@adapter)
36
- @adapter.within_transaction?.should == false
37
- @adapter.current_transaction.should == nil
38
- @adapter.push_transaction(transaction2)
39
- @adapter.within_transaction?.should == true
40
- @adapter.current_transaction.should == transaction2
41
- lock.synchronize do
42
- Thread.new do
43
- @adapter.within_transaction?.should == false
44
- @adapter.current_transaction.should == nil
45
- @adapter.push_transaction(@transaction)
46
- @adapter.within_transaction?.should == true
47
- @adapter.current_transaction.should == @transaction
48
- lock.synchronize do
49
- @adapter.within_transaction?.should == true
50
- @adapter.current_transaction.should == @transaction
51
- @adapter.pop_transaction
52
- @adapter.within_transaction?.should == false
53
- @adapter.current_transaction.should == nil
54
- end
55
- end
56
- @adapter.within_transaction?.should == true
57
- @adapter.current_transaction.should == transaction2
58
- @adapter.pop_transaction
59
- @adapter.within_transaction?.should == false
60
- @adapter.current_transaction.should == nil
61
- end
62
- end
63
- end
64
-
65
- it "should raise NotImplementedError when #create is called" do
66
- lambda { @adapter.create([ :resource ]) }.should raise_error(NotImplementedError)
67
- end
68
-
69
- it "should raise NotImplementedError when #read_many is called" do
70
- lambda { @adapter.read_many(:query) }.should raise_error(NotImplementedError)
71
- end
72
-
73
- it "should raise NotImplementedError when #read_one is called" do
74
- lambda { @adapter.read_one(:query) }.should raise_error(NotImplementedError)
75
- end
76
-
77
- it "should raise NotImplementedError when #update is called" do
78
- lambda { @adapter.update(:attributes, :query) }.should raise_error(NotImplementedError)
79
- end
80
-
81
- it "should raise NotImplementedError when #delete is called" do
82
- lambda { @adapter.delete(:query) }.should raise_error(NotImplementedError)
83
- end
84
-
85
- it "should raise NotImplementedError when #upgrade_model_storage is called" do
86
- lambda { @adapter.upgrade_model_storage(:repository, :resource) }.should raise_error(NotImplementedError)
87
- end
88
-
89
- it "should raise NotImplementedError when #storage_exists? is called" do
90
- lambda { @adapter.storage_exists?("hehu") }.should raise_error(NotImplementedError)
91
- end
92
-
93
- it "should raise NotImplementedError when #create_model_storage is called" do
94
- lambda { @adapter.create_model_storage(:repository, :resource) }.should raise_error(NotImplementedError)
95
- end
96
-
97
- it "should raise NotImplementedError when #destroy_model_storage is called" do
98
- lambda { @adapter.destroy_model_storage(:repository, :resource) }.should raise_error(NotImplementedError)
99
- end
100
-
101
- it "should raise NotImplementedError when #alter_model_storage is called" do
102
- lambda { @adapter.alter_model_storage(:repository, :resource) }.should raise_error(NotImplementedError)
103
- end
104
-
105
- it "should raise NotImplementedError when #create_property_storage is called" do
106
- lambda { @adapter.create_property_storage(:repository, :property) }
107
- end
108
-
109
- it "should raise NotImplementedError when #destroy_property_storage is called" do
110
- lambda { @adapter.destroy_property_storage(:repository, :property) }
111
- end
112
-
113
- it "should raise NotImplementedError when #alter_property_storage is called" do
114
- lambda { @adapter.alter_property_storage(:repository, :property) }
115
- end
116
-
117
- it "should raise NotImplementedError when #transaction_primitive is called" do
118
- lambda { @adapter.transaction_primitive }.should raise_error(NotImplementedError)
119
- end
120
-
121
- it "should clean out dead threads from @transactions" do
122
- @adapter.instance_eval do @transactions end.size.should == 0
123
- t = Thread.new do
124
- @adapter.push_transaction("plur")
125
- end
126
- while t.alive?
127
- sleep 0.1
128
- end
129
- @adapter.instance_eval do @transactions end.size.should == 1
130
- @adapter.push_transaction("ploj")
131
- @adapter.instance_eval do @transactions end.size.should == 1
132
- end
133
- end
@@ -1,15 +0,0 @@
1
-
2
- describe "a DataMapper Adapter", :shared => true do
3
-
4
- it "should initialize the connection uri" do
5
- new_adapter = @adapter.class.new(:default, Addressable::URI.parse('some://uri/string'))
6
- new_adapter.instance_variable_get('@uri').to_s.should == Addressable::URI.parse('some://uri/string').to_s
7
- end
8
-
9
- %w{create read_many read_one update delete create_model_storage alter_model_storage destroy_model_storage create_property_storage alter_property_storage destroy_property_storage} .each do |meth|
10
- it "should have a #{meth} method" do
11
- @adapter.should respond_to(meth.intern)
12
- end
13
- end
14
-
15
- end
@@ -1,632 +0,0 @@
1
- require 'monitor'
2
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
3
-
4
- require DataMapper.root / 'spec' / 'unit' / 'adapters' / 'adapter_shared_spec'
5
-
6
- # TODO: make a shared adapter spec for all the DAO objects to adhere to
7
-
8
- describe DataMapper::Adapters::DataObjectsAdapter do
9
- before :all do
10
- class ::Cheese
11
- include DataMapper::Resource
12
- property :id, Serial
13
- property :name, String, :nullable => false
14
- property :color, String, :default => 'yellow'
15
- property :notes, String, :length => 100, :lazy => true
16
- end
17
- end
18
-
19
- before do
20
- @uri = Addressable::URI.parse('mock://localhost')
21
- @adapter = DataMapper::Adapters::DataObjectsAdapter.new(:default, @uri)
22
- end
23
-
24
- it_should_behave_like 'a DataMapper Adapter'
25
-
26
- describe "#find_by_sql" do
27
-
28
- before do
29
- class ::Plupp
30
- include DataMapper::Resource
31
- property :id, Integer, :key => true
32
- property :name, String
33
- end
34
- end
35
-
36
- it "should be added to DataMapper::Model" do
37
- DataMapper::Model.instance_methods.map { |m| m.to_s }.include?("find_by_sql").should == true
38
- Plupp.should respond_to(:find_by_sql)
39
- end
40
-
41
- describe "when called" do
42
-
43
- before do
44
- @reader = mock("reader")
45
- @reader.stub!(:next!).and_return(false)
46
- @reader.stub!(:close)
47
- @connection = mock("connection")
48
- @connection.stub!(:close)
49
- @command = mock("command")
50
- @adapter = Plupp.repository.adapter
51
- @repository = Plupp.repository
52
- @repository.stub!(:adapter).and_return(@adapter)
53
- @adapter.stub!(:create_connection).and_return(@connection)
54
- @adapter.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::DataObjectsAdapter).and_return(true)
55
- end
56
-
57
- it "should accept a single String argument with or without options hash" do
58
- @connection.should_receive(:create_command).twice.with("SELECT * FROM plupps").and_return(@command)
59
- @command.should_receive(:execute_reader).twice.and_return(@reader)
60
- Plupp.should_receive(:repository).any_number_of_times.and_return(@repository)
61
- Plupp.should_receive(:repository).any_number_of_times.with(:plupp_repo).and_return(@repository)
62
- Plupp.find_by_sql("SELECT * FROM plupps").to_a
63
- Plupp.find_by_sql("SELECT * FROM plupps", :repository => :plupp_repo).to_a
64
- end
65
-
66
- it "should accept an Array argument with or without options hash" do
67
- @connection.should_receive(:create_command).twice.with("SELECT * FROM plupps WHERE plur = ?").and_return(@command)
68
- @command.should_receive(:execute_reader).twice.with("my pretty plur").and_return(@reader)
69
- Plupp.should_receive(:repository).any_number_of_times.and_return(@repository)
70
- Plupp.should_receive(:repository).any_number_of_times.with(:plupp_repo).and_return(@repository)
71
- Plupp.find_by_sql(["SELECT * FROM plupps WHERE plur = ?", "my pretty plur"]).to_a
72
- Plupp.find_by_sql(["SELECT * FROM plupps WHERE plur = ?", "my pretty plur"], :repository => :plupp_repo).to_a
73
- end
74
-
75
- it "should accept a Query argument with or without options hash" do
76
- if ADAPTER == :mysql
77
- @connection.should_receive(:create_command).twice.with('SELECT `name` FROM `plupps` WHERE (`name` = ?) ORDER BY `id`').and_return(@command)
78
- else
79
- @connection.should_receive(:create_command).twice.with('SELECT "name" FROM "plupps" WHERE ("name" = ?) ORDER BY "id"').and_return(@command)
80
- end
81
- @command.should_receive(:execute_reader).twice.with('my pretty plur').and_return(@reader)
82
- Plupp.should_receive(:repository).any_number_of_times.and_return(@repository)
83
- Plupp.should_receive(:repository).any_number_of_times.with(:plupp_repo).and_return(@repository)
84
- Plupp.find_by_sql(DataMapper::Query.new(@repository, Plupp, "name" => "my pretty plur", :fields => ["name"])).to_a
85
- Plupp.find_by_sql(DataMapper::Query.new(@repository, Plupp, "name" => "my pretty plur", :fields => ["name"]), :repository => :plupp_repo).to_a
86
- end
87
-
88
- it "requires a Repository that is a DataObjectsRepository to work" do
89
- non_do_adapter = mock("non do adapter")
90
- non_do_repo = mock("non do repo")
91
- non_do_repo.stub!(:adapter).and_return(non_do_adapter)
92
- Plupp.should_receive(:repository).any_number_of_times.with(:plupp_repo).and_return(non_do_repo)
93
- Proc.new do
94
- Plupp.find_by_sql(:repository => :plupp_repo)
95
- end.should raise_error(Exception, /DataObjectsAdapter/)
96
- end
97
-
98
- it "requires some kind of query to work at all" do
99
- Plupp.should_receive(:repository).any_number_of_times.with(:plupp_repo).and_return(@repository)
100
- Proc.new do
101
- Plupp.find_by_sql(:repository => :plupp_repo)
102
- end.should raise_error(Exception, /requires a query/)
103
- end
104
-
105
- end
106
-
107
- end
108
-
109
- describe '#uri options' do
110
- it 'should transform a fully specified option hash into a URI' do
111
- options = {
112
- :adapter => 'mysql',
113
- :host => 'davidleal.com',
114
- :username => 'me',
115
- :password => 'mypass',
116
- :port => 5000,
117
- :database => 'you_can_call_me_al',
118
- :socket => 'nosock'
119
- }
120
-
121
- adapter = DataMapper::Adapters::DataObjectsAdapter.new(:spec, options)
122
- adapter.uri.should ==
123
- DataObjects::URI.parse("mysql://me:mypass@davidleal.com:5000/you_can_call_me_al?socket=nosock")
124
- end
125
-
126
- it 'should transform a minimal options hash into a URI' do
127
- options = {
128
- :adapter => 'mysql',
129
- :database => 'you_can_call_me_al'
130
- }
131
-
132
- adapter = DataMapper::Adapters::DataObjectsAdapter.new(:spec, options)
133
- adapter.uri.should == DataObjects::URI.parse("mysql:you_can_call_me_al")
134
- end
135
-
136
- it 'should accept the uri when no overrides exist' do
137
- uri = Addressable::URI.parse("protocol:///")
138
- DataMapper::Adapters::DataObjectsAdapter.new(:spec, uri).uri.should == DataObjects::URI.parse(uri)
139
- end
140
- end
141
-
142
- describe '#create' do
143
- before do
144
- @result = mock('result', :to_i => 1, :insert_id => 1)
145
-
146
- @adapter.stub!(:execute).and_return(@result)
147
- @adapter.stub!(:supports_returning?).and_return(false)
148
-
149
- @property = mock('property', :kind_of? => true, :serial? => true, :name => :property, :field => 'property', :custom? => false, :typecast => 'bind value')
150
- @properties = [ @property ]
151
- @bind_values = [ 'bind value' ]
152
- @attributes = mock('attributes', :keys => @properties, :values => @bind_values)
153
- @model = mock('model', :kind_of? => true, :key => [ @property ], :storage_name => 'models')
154
- @resource = mock('resource', :model => @model, :dirty_attributes => @attributes)
155
-
156
- @property.stub!(:set!).and_return(@resource)
157
-
158
- @statement = 'INSERT INTO "models" ("property") VALUES (?)'
159
- end
160
-
161
- def do_create
162
- @adapter.create([ @resource ])
163
- end
164
-
165
- it 'should use only dirty properties' do
166
- @resource.should_receive(:dirty_attributes).with(no_args).and_return(@attributes)
167
- do_create.should == 1
168
- end
169
-
170
- it 'should use the bind values' do
171
- @attributes.should_receive(:values).with(no_args).and_return(@bind_values)
172
-
173
- @adapter.should_receive(:execute).with(@statement, *@bind_values).and_return(@result)
174
-
175
- do_create.should == 1
176
- end
177
-
178
- it 'should generate an SQL statement when supports_returning? is true' do
179
- @property.should_receive(:serial?).with(no_args).and_return(true)
180
- @adapter.should_receive(:supports_returning?).with(no_args).and_return(true)
181
-
182
- @statement = 'INSERT INTO "models" ("property") VALUES (?) RETURNING "property"'
183
- @adapter.should_receive(:execute).with(@statement, 'bind value').and_return(@result)
184
-
185
- do_create.should == 1
186
- end
187
-
188
- it 'should generate an SQL statement when supports_default_values? is true' do
189
- @bind_values.clear
190
- @properties.clear
191
- @adapter.should_receive(:supports_default_values?).with(no_args).and_return(true)
192
-
193
- @statement = 'INSERT INTO "models" DEFAULT VALUES'
194
- @adapter.should_receive(:execute).with(@statement).and_return(@result)
195
-
196
- do_create.should == 1
197
- end
198
-
199
- it 'should generate an SQL statement when supports_default_values? is false' do
200
- @bind_values.clear
201
- @properties.clear
202
- @adapter.should_receive(:supports_default_values?).with(no_args).and_return(false)
203
-
204
- @statement = 'INSERT INTO "models" () VALUES ()'
205
- @adapter.should_receive(:execute).with(@statement).and_return(@result)
206
-
207
- do_create.should == 1
208
- end
209
-
210
- it 'should return 0 if no rows created' do
211
- @result.should_receive(:to_i).with(no_args).and_return(0)
212
- do_create.should == 0
213
- end
214
-
215
- it 'should return 1 if number of rows created is 1' do
216
- @result.should_receive(:to_i).with(no_args).and_return(1)
217
- do_create.should == 1
218
- end
219
-
220
- it 'should set the resource primary key if the model key size is 1 and the key is serial' do
221
- @model.key.size.should == 1
222
- @property.should_receive(:serial?).and_return(true)
223
- @result.should_receive(:insert_id).and_return(777)
224
- @property.should_receive(:set!).with(@resource, 777)
225
- do_create.should == 1
226
- end
227
- end
228
-
229
- [ :read_many, :read_one ].each do |method|
230
- describe "##{method}" do
231
- before do
232
- @key = mock('key')
233
- @model = mock('model', :key => @key, :storage_name => 'models', :relationships => {})
234
- @primitive = mock('primitive')
235
- @property = mock('property', :kind_of? => true, :model => @model, :field => 'property', :primitive => @primitive)
236
-
237
- @child_model = @model
238
- @parent_model = mock('parent model', :storage_name => 'parents')
239
- @parent_property = mock('parent id', :kind_of? => true, :model => @parent_model, :field => 'id')
240
-
241
- @child_key = [ @property ]
242
- @parent_key = [ @parent_property ]
243
- @relationship = mock('relationship', :child_model => @child_model, :parent_model => @parent_model, :child_key => @child_key, :parent_key => @parent_key)
244
- @links = [ @relationship ]
245
-
246
- @fields = [ @property ]
247
- @bind_values = [ 'bind value' ]
248
- @conditions = [ [ :eql, @property, @bind_values[0] ] ]
249
-
250
- @direction = mock('direction', :property => @property, :direction => :desc)
251
- @order = [ @direction ]
252
-
253
- @query = mock('query', :model => @model, :kind_of? => true, :links => @links, :fields => @fields, :conditions => @conditions, :order => @order, :limit => 111, :offset => 222, :bind_values => @bind_values)
254
- @query.should_receive(:unique?).with(no_args).and_return(false)
255
-
256
- @reader = mock('reader', :close => true, :next! => false)
257
- @command = mock('command', :set_types => nil, :execute_reader => @reader)
258
- @connection = mock('connection', :close => true, :create_command => @command)
259
-
260
- DataObjects::Connection.stub!(:new).and_return(@connection)
261
- DataMapper::Query::Direction.stub!(:===).and_return(true)
262
- end
263
-
264
- if method == :read_one
265
- before do
266
- @query.should_receive(:limit).with(no_args).twice.and_return(1)
267
-
268
- @values = @bind_values.dup
269
-
270
- @reader.should_receive(:next!).with(no_args).and_return(true)
271
- @reader.should_receive(:values).with(no_args).and_return(@values)
272
-
273
- @resource = mock('resource')
274
- @resource.should_receive(:kind_of?).with(DataMapper::Resource).any_number_of_times.and_return(true)
275
-
276
- @model.should_receive(:load).with(@values, @query).and_return(@resource)
277
-
278
- @statement = 'SELECT "models"."property" FROM "models" INNER JOIN "parents" ON ("parents"."id" = "models"."property") WHERE ("models"."property" = ?) ORDER BY "models"."property" DESC LIMIT 1 OFFSET 222'
279
- end
280
-
281
- define_method(:do_read) do
282
- resource = @adapter.read_one(@query)
283
- resource.should == @resource
284
- resource
285
- end
286
- elsif method == :read_many
287
- before do
288
- @statement = 'SELECT "models"."property" FROM "models" INNER JOIN "parents" ON ("parents"."id" = "models"."property") WHERE ("models"."property" = ?) ORDER BY "models"."property" DESC LIMIT 111 OFFSET 222'
289
- end
290
-
291
- define_method(:do_read) do
292
- collection = @adapter.read_many(@query)
293
- collection.to_a
294
- collection
295
- end
296
- end
297
-
298
- it 'should use the bind values' do
299
- @command.should_receive(:execute_reader).with(*@bind_values).and_return(@reader)
300
- do_read
301
- end
302
-
303
- it 'should generate an SQL statement' do
304
- @connection.should_receive(:create_command).with(@statement).and_return(@command)
305
- do_read
306
- end
307
-
308
- it 'should generate an SQL statement with composite keys' do
309
- other_property = mock('other property', :kind_of? => true)
310
- other_property.should_receive(:field).with(:default).and_return('other')
311
- other_property.should_receive(:model).with(no_args).and_return(@model)
312
-
313
- other_value = 'other value'
314
- @bind_values << other_value
315
- @conditions << [ :eql, other_property, other_value ]
316
-
317
- @statement = %[SELECT "models"."property" FROM "models" INNER JOIN "parents" ON ("parents"."id" = "models"."property") WHERE ("models"."property" = ?) AND ("models"."other" = ?) ORDER BY "models"."property" DESC LIMIT #{method == :read_one ? '1' : '111'} OFFSET 222]
318
- @query.should_receive(:conditions).with(no_args).twice.and_return(@conditions)
319
-
320
- @connection.should_receive(:create_command).with(@statement).and_return(@command)
321
-
322
- do_read
323
- end
324
-
325
- it 'should set the return types to the property primitives' do
326
- @command.should_receive(:set_types).with([ @primitive ])
327
- do_read
328
- end
329
-
330
- it 'should close the reader' do
331
- @reader.should_receive(:close).with(no_args)
332
- do_read
333
- end
334
-
335
- it 'should close the connection' do
336
- @connection.should_receive(:close).with(no_args)
337
- do_read
338
- end
339
-
340
- if method == :read_one
341
- it 'should return a DataMapper::Resource' do
342
- do_read.should be_kind_of(DataMapper::Resource)
343
- end
344
- else
345
- it 'should return a DataMapper::Collection' do
346
- do_read.should be_kind_of(DataMapper::Collection)
347
- end
348
- end
349
- end
350
- end
351
-
352
- describe '#update' do
353
- before do
354
- @result = mock('result', :to_i => 1)
355
-
356
- @adapter.stub!(:execute).and_return(@result)
357
-
358
- @values = %w[ new ]
359
- @model = mock('model', :storage_name => 'models')
360
- @property = mock('property', :kind_of? => true, :field => 'property')
361
- @bind_values = [ 'bind value' ]
362
- @conditions = [ [ :eql, @property, @bind_values[0] ] ]
363
- @attributes = mock('attributes', :kind_of? => true, :empty? => false, :keys => [ @property ], :values => @values)
364
- @query = mock('query', :kind_of? => true, :model => @model, :links => [], :conditions => @conditions, :bind_values => @bind_values)
365
- @statement = 'UPDATE "models" SET "property" = ? WHERE ("property" = ?)'
366
- end
367
-
368
- def do_update
369
- @adapter.update(@attributes, @query)
370
- end
371
-
372
- it 'should use the bind values' do
373
- @attributes.should_receive(:values).with(no_args).and_return(@values)
374
- @query.should_receive(:bind_values).with(no_args).and_return(@bind_values)
375
-
376
- @adapter.should_receive(:execute).with(@statement, *@values + @bind_values).and_return(@result)
377
-
378
- do_update.should == 1
379
- end
380
-
381
- it 'should generate an SQL statement' do
382
- other_property = mock('other property', :kind_of? => true)
383
- other_property.should_receive(:field).with(:default).and_return('other')
384
- other_property.should_receive(:model).with(no_args).and_return(@model)
385
-
386
- other_value = 'other value'
387
- @bind_values << other_value
388
- @conditions << [ :eql, other_property, other_value ]
389
-
390
- @query.should_receive(:conditions).with(no_args).twice.and_return(@conditions)
391
-
392
- @statement = 'UPDATE "models" SET "property" = ? WHERE ("property" = ?) AND ("other" = ?)'
393
- @adapter.should_receive(:execute).with(@statement, *%w[ new ] + @bind_values).and_return(@result)
394
-
395
- do_update.should == 1
396
- end
397
-
398
- it 'should return 0 if no rows updated' do
399
- @result.should_receive(:to_i).with(no_args).and_return(0)
400
- do_update.should == 0
401
- end
402
-
403
- it 'should return 1 if number of rows updated is 1' do
404
- @result.should_receive(:to_i).with(no_args).and_return(1)
405
- do_update.should == 1
406
- end
407
- end
408
-
409
- describe '#delete' do
410
- before do
411
- @result = mock('result', :to_i => 1)
412
-
413
- @adapter.stub!(:execute).and_return(@result)
414
-
415
- @model = mock('model', :storage_name => 'models')
416
- @property = mock('property', :kind_of? => true, :field => 'property')
417
- @bind_values = [ 'bind value' ]
418
- @conditions = [ [ :eql, @property, @bind_values[0] ] ]
419
- @query = mock('query', :kind_of? => true, :model => @model, :links => [], :conditions => @conditions, :bind_values => @bind_values)
420
- @resource = mock('resource', :to_query => @query)
421
- @statement = 'DELETE FROM "models" WHERE ("property" = ?)'
422
- end
423
-
424
- def do_delete
425
- @adapter.delete(@resource.to_query(@repository))
426
- end
427
-
428
- it 'should use the bind values' do
429
- @query.should_receive(:bind_values).with(no_args).and_return(@bind_values)
430
-
431
- @adapter.should_receive(:execute).with(@statement, *@bind_values).and_return(@result)
432
-
433
- do_delete.should == 1
434
- end
435
-
436
- it 'should generate an SQL statement' do
437
- other_property = mock('other property', :kind_of? => true)
438
- other_property.should_receive(:field).with(:default).and_return('other')
439
- other_property.should_receive(:model).with(no_args).and_return(@model)
440
-
441
- other_value = 'other value'
442
- @bind_values << other_value
443
- @conditions << [ :eql, other_property, other_value ]
444
-
445
- @query.should_receive(:conditions).with(no_args).twice.and_return(@conditions)
446
-
447
- @statement = 'DELETE FROM "models" WHERE ("property" = ?) AND ("other" = ?)'
448
- @adapter.should_receive(:execute).with(@statement, *@bind_values).and_return(@result)
449
-
450
- do_delete.should == 1
451
- end
452
-
453
- it 'should return 0 if no rows deleted' do
454
- @result.should_receive(:to_i).with(no_args).and_return(0)
455
- do_delete.should == 0
456
- end
457
-
458
- it 'should return 1 if number of rows deleted is 1' do
459
- @result.should_receive(:to_i).with(no_args).and_return(1)
460
- do_delete.should == 1
461
- end
462
- end
463
-
464
- describe "when upgrading tables" do
465
- it "should raise NotImplementedError when #storage_exists? is called" do
466
- lambda { @adapter.storage_exists?("cheeses") }.should raise_error(NotImplementedError)
467
- end
468
-
469
- describe "#upgrade_model_storage" do
470
- it "should call #create_model_storage" do
471
- @adapter.should_receive(:create_model_storage).with(repository, Cheese).and_return(true)
472
- @adapter.upgrade_model_storage(repository, Cheese).should == Cheese.properties
473
- end
474
-
475
- it "should check if all properties of the model have columns if the table exists" do
476
- @adapter.should_receive(:field_exists?).with("cheeses", "id").and_return(true)
477
- @adapter.should_receive(:field_exists?).with("cheeses", "name").and_return(true)
478
- @adapter.should_receive(:field_exists?).with("cheeses", "color").and_return(true)
479
- @adapter.should_receive(:field_exists?).with("cheeses", "notes").and_return(true)
480
- @adapter.should_receive(:storage_exists?).with("cheeses").and_return(true)
481
- @adapter.upgrade_model_storage(repository, Cheese).should == []
482
- end
483
-
484
- it "should create and execute add column statements for columns that dont exist" do
485
- @adapter.should_receive(:field_exists?).with("cheeses", "id").and_return(true)
486
- @adapter.should_receive(:field_exists?).with("cheeses", "name").and_return(true)
487
- @adapter.should_receive(:field_exists?).with("cheeses", "color").and_return(true)
488
- @adapter.should_receive(:field_exists?).with("cheeses", "notes").and_return(false)
489
- @adapter.should_receive(:storage_exists?).with("cheeses").and_return(true)
490
- connection = mock("connection")
491
- connection.should_receive(:close)
492
- @adapter.should_receive(:create_connection).and_return(connection)
493
- statement = mock("statement")
494
- command = mock("command")
495
- result = mock("result")
496
- command.should_receive(:execute_non_query).and_return(result)
497
- connection.should_receive(:create_command).with(statement).and_return(command)
498
- @adapter.should_receive(:alter_table_add_column_statement).with("cheeses",
499
- {
500
- :nullable? => true,
501
- :name => "notes",
502
- :serial? => false,
503
- :primitive => "VARCHAR",
504
- :size => 100
505
- }).and_return(statement)
506
- @adapter.upgrade_model_storage(repository, Cheese).should == [Cheese.notes]
507
- end
508
- end
509
- end
510
-
511
- describe '#execute' do
512
- before do
513
- @mock_command = mock('Command', :execute_non_query => nil)
514
- @mock_db = mock('DB Connection', :create_command => @mock_command, :close => true)
515
-
516
- @adapter.stub!(:create_connection).and_return(@mock_db)
517
- end
518
-
519
- it 'should #create_command from the sql passed' do
520
- @mock_db.should_receive(:create_command).with('SQL STRING').and_return(@mock_command)
521
- @adapter.execute('SQL STRING')
522
- end
523
-
524
- it 'should pass any additional args to #execute_non_query' do
525
- @mock_command.should_receive(:execute_non_query).with(:args)
526
- @adapter.execute('SQL STRING', :args)
527
- end
528
-
529
- it 'should return the result of #execute_non_query' do
530
- @mock_command.should_receive(:execute_non_query).and_return(:result_set)
531
-
532
- @adapter.execute('SQL STRING').should == :result_set
533
- end
534
-
535
- it 'should log any errors, then re-raise them' do
536
- @mock_command.should_receive(:execute_non_query).and_raise("Oh Noes!")
537
- DataMapper.logger.should_receive(:error)
538
-
539
- lambda { @adapter.execute('SQL STRING') }.should raise_error("Oh Noes!")
540
- end
541
-
542
- it 'should always close the db connection' do
543
- @mock_command.should_receive(:execute_non_query).and_raise("Oh Noes!")
544
- @mock_db.should_receive(:close)
545
-
546
- lambda { @adapter.execute('SQL STRING') }.should raise_error("Oh Noes!")
547
- end
548
- end
549
-
550
- describe '#query' do
551
- before do
552
- @mock_reader = mock('Reader', :fields => ['id', 'UserName', 'AGE'],
553
- :values => [1, 'rando', 27],
554
- :close => true)
555
- @mock_command = mock('Command', :execute_reader => @mock_reader)
556
- @mock_db = mock('DB Connection', :create_command => @mock_command, :close => true)
557
-
558
- #make the while loop run exactly once
559
- @mock_reader.stub!(:next!).and_return(true, nil)
560
- @adapter.stub!(:create_connection).and_return(@mock_db)
561
- end
562
-
563
- it 'should #create_command from the sql passed' do
564
- @mock_db.should_receive(:create_command).with('SQL STRING').and_return(@mock_command)
565
- @adapter.query('SQL STRING')
566
- end
567
-
568
- it 'should pass any additional args to #execute_reader' do
569
- @mock_command.should_receive(:execute_reader).with(:args).and_return(@mock_reader)
570
- @adapter.query('SQL STRING', :args)
571
- end
572
-
573
- describe 'returning multiple fields' do
574
-
575
- it 'should underscore the field names as members of the result struct' do
576
- @mock_reader.should_receive(:fields).and_return(['id', 'UserName', 'AGE'])
577
-
578
- result = @adapter.query('SQL STRING')
579
-
580
- result.first.members.map { |m| m.to_s }.should == %w[ id user_name age ]
581
- end
582
-
583
- it 'should convert each row into the struct' do
584
- @mock_reader.should_receive(:values).and_return([1, 'rando', 27])
585
-
586
- @adapter.query('SQL STRING')
587
- end
588
-
589
- it 'should add the row structs into the results array' do
590
- results = @adapter.query('SQL STRING')
591
-
592
- results.should be_kind_of(Array)
593
-
594
- row = results.first
595
- row.should be_kind_of(Struct)
596
-
597
- row.id.should == 1
598
- row.user_name.should == 'rando'
599
- row.age.should == 27
600
- end
601
-
602
- end
603
-
604
- describe 'returning a single field' do
605
-
606
- it 'should add the value to the results array' do
607
- @mock_reader.should_receive(:fields).and_return(['username'])
608
- @mock_reader.should_receive(:values).and_return(['rando'])
609
-
610
- results = @adapter.query('SQL STRING')
611
-
612
- results.should be_kind_of(Array)
613
- results.first.should == 'rando'
614
- end
615
-
616
- end
617
-
618
- it 'should log any errors, then re-raise them' do
619
- @mock_command.should_receive(:execute_non_query).and_raise("Oh Noes!")
620
- DataMapper.logger.should_receive(:error)
621
-
622
- lambda { @adapter.execute('SQL STRING') }.should raise_error("Oh Noes!")
623
- end
624
-
625
- it 'should always close the db connection' do
626
- @mock_command.should_receive(:execute_non_query).and_raise("Oh Noes!")
627
- @mock_db.should_receive(:close)
628
-
629
- lambda { @adapter.execute('SQL STRING') }.should raise_error("Oh Noes!")
630
- end
631
- end
632
- end