sam-dm-core 0.9.6

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 (126) hide show
  1. data/.autotest +26 -0
  2. data/CONTRIBUTING +51 -0
  3. data/FAQ +92 -0
  4. data/History.txt +145 -0
  5. data/MIT-LICENSE +22 -0
  6. data/Manifest.txt +125 -0
  7. data/QUICKLINKS +12 -0
  8. data/README.txt +143 -0
  9. data/Rakefile +30 -0
  10. data/SPECS +63 -0
  11. data/TODO +1 -0
  12. data/lib/dm-core.rb +224 -0
  13. data/lib/dm-core/adapters.rb +4 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
  15. data/lib/dm-core/adapters/data_objects_adapter.rb +707 -0
  16. data/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +188 -0
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  19. data/lib/dm-core/associations.rb +199 -0
  20. data/lib/dm-core/associations/many_to_many.rb +147 -0
  21. data/lib/dm-core/associations/many_to_one.rb +107 -0
  22. data/lib/dm-core/associations/one_to_many.rb +309 -0
  23. data/lib/dm-core/associations/one_to_one.rb +61 -0
  24. data/lib/dm-core/associations/relationship.rb +218 -0
  25. data/lib/dm-core/associations/relationship_chain.rb +81 -0
  26. data/lib/dm-core/auto_migrations.rb +113 -0
  27. data/lib/dm-core/collection.rb +638 -0
  28. data/lib/dm-core/dependency_queue.rb +31 -0
  29. data/lib/dm-core/hook.rb +11 -0
  30. data/lib/dm-core/identity_map.rb +45 -0
  31. data/lib/dm-core/is.rb +16 -0
  32. data/lib/dm-core/logger.rb +232 -0
  33. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  34. data/lib/dm-core/migrator.rb +29 -0
  35. data/lib/dm-core/model.rb +471 -0
  36. data/lib/dm-core/naming_conventions.rb +84 -0
  37. data/lib/dm-core/property.rb +673 -0
  38. data/lib/dm-core/property_set.rb +162 -0
  39. data/lib/dm-core/query.rb +625 -0
  40. data/lib/dm-core/repository.rb +159 -0
  41. data/lib/dm-core/resource.rb +637 -0
  42. data/lib/dm-core/scope.rb +58 -0
  43. data/lib/dm-core/support.rb +7 -0
  44. data/lib/dm-core/support/array.rb +13 -0
  45. data/lib/dm-core/support/assertions.rb +8 -0
  46. data/lib/dm-core/support/errors.rb +23 -0
  47. data/lib/dm-core/support/kernel.rb +7 -0
  48. data/lib/dm-core/support/symbol.rb +41 -0
  49. data/lib/dm-core/transaction.rb +267 -0
  50. data/lib/dm-core/type.rb +160 -0
  51. data/lib/dm-core/type_map.rb +80 -0
  52. data/lib/dm-core/types.rb +19 -0
  53. data/lib/dm-core/types/boolean.rb +7 -0
  54. data/lib/dm-core/types/discriminator.rb +34 -0
  55. data/lib/dm-core/types/object.rb +24 -0
  56. data/lib/dm-core/types/paranoid_boolean.rb +34 -0
  57. data/lib/dm-core/types/paranoid_datetime.rb +33 -0
  58. data/lib/dm-core/types/serial.rb +9 -0
  59. data/lib/dm-core/types/text.rb +10 -0
  60. data/lib/dm-core/version.rb +3 -0
  61. data/script/all +5 -0
  62. data/script/performance.rb +203 -0
  63. data/script/profile.rb +87 -0
  64. data/spec/integration/association_spec.rb +1371 -0
  65. data/spec/integration/association_through_spec.rb +203 -0
  66. data/spec/integration/associations/many_to_many_spec.rb +449 -0
  67. data/spec/integration/associations/many_to_one_spec.rb +163 -0
  68. data/spec/integration/associations/one_to_many_spec.rb +151 -0
  69. data/spec/integration/auto_migrations_spec.rb +398 -0
  70. data/spec/integration/collection_spec.rb +1069 -0
  71. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  72. data/spec/integration/dependency_queue_spec.rb +58 -0
  73. data/spec/integration/model_spec.rb +127 -0
  74. data/spec/integration/mysql_adapter_spec.rb +85 -0
  75. data/spec/integration/postgres_adapter_spec.rb +731 -0
  76. data/spec/integration/property_spec.rb +233 -0
  77. data/spec/integration/query_spec.rb +506 -0
  78. data/spec/integration/repository_spec.rb +57 -0
  79. data/spec/integration/resource_spec.rb +475 -0
  80. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  81. data/spec/integration/sti_spec.rb +208 -0
  82. data/spec/integration/strategic_eager_loading_spec.rb +138 -0
  83. data/spec/integration/transaction_spec.rb +75 -0
  84. data/spec/integration/type_spec.rb +271 -0
  85. data/spec/lib/logging_helper.rb +18 -0
  86. data/spec/lib/mock_adapter.rb +27 -0
  87. data/spec/lib/model_loader.rb +91 -0
  88. data/spec/lib/publicize_methods.rb +28 -0
  89. data/spec/models/vehicles.rb +34 -0
  90. data/spec/models/zoo.rb +47 -0
  91. data/spec/spec.opts +3 -0
  92. data/spec/spec_helper.rb +86 -0
  93. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  94. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  95. data/spec/unit/adapters/data_objects_adapter_spec.rb +628 -0
  96. data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
  97. data/spec/unit/associations/many_to_many_spec.rb +17 -0
  98. data/spec/unit/associations/many_to_one_spec.rb +152 -0
  99. data/spec/unit/associations/one_to_many_spec.rb +393 -0
  100. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  101. data/spec/unit/associations/relationship_spec.rb +71 -0
  102. data/spec/unit/associations_spec.rb +242 -0
  103. data/spec/unit/auto_migrations_spec.rb +111 -0
  104. data/spec/unit/collection_spec.rb +182 -0
  105. data/spec/unit/data_mapper_spec.rb +35 -0
  106. data/spec/unit/identity_map_spec.rb +126 -0
  107. data/spec/unit/is_spec.rb +80 -0
  108. data/spec/unit/migrator_spec.rb +33 -0
  109. data/spec/unit/model_spec.rb +339 -0
  110. data/spec/unit/naming_conventions_spec.rb +36 -0
  111. data/spec/unit/property_set_spec.rb +83 -0
  112. data/spec/unit/property_spec.rb +753 -0
  113. data/spec/unit/query_spec.rb +530 -0
  114. data/spec/unit/repository_spec.rb +93 -0
  115. data/spec/unit/resource_spec.rb +626 -0
  116. data/spec/unit/scope_spec.rb +142 -0
  117. data/spec/unit/transaction_spec.rb +493 -0
  118. data/spec/unit/type_map_spec.rb +114 -0
  119. data/spec/unit/type_spec.rb +119 -0
  120. data/tasks/ci.rb +68 -0
  121. data/tasks/dm.rb +63 -0
  122. data/tasks/doc.rb +20 -0
  123. data/tasks/gemspec.rb +23 -0
  124. data/tasks/hoe.rb +46 -0
  125. data/tasks/install.rb +20 -0
  126. metadata +216 -0
@@ -0,0 +1,18 @@
1
+ module LoggingHelper
2
+ def logger(adapter = ADAPTER, &block)
3
+ current_adapter = DataObjects.const_get(repository(adapter).adapter.uri.scheme.capitalize)
4
+ old_logger = current_adapter.logger
5
+
6
+ log_path = File.join(SPEC_ROOT, "tmp.log")
7
+ handle = File.open(log_path, "a+")
8
+ current_adapter.logger = DataObjects::Logger.new(log_path, 0)
9
+ begin
10
+ yield(handle)
11
+ ensure
12
+ handle.truncate(0)
13
+ handle.close
14
+ current_adapter.logger = old_logger
15
+ File.delete(log_path)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
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
@@ -0,0 +1,91 @@
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
+ Object.module_eval { remove_const klass.to_s }
85
+ end
86
+ end
87
+ end
88
+
89
+ Spec::Runner.configure do |config|
90
+ config.include(ModelLoader)
91
+ end
@@ -0,0 +1,28 @@
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
@@ -0,0 +1,34 @@
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
@@ -0,0 +1,47 @@
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
+
11
+ has n, :animals
12
+
13
+ def to_s
14
+ name
15
+ end
16
+ end
17
+
18
+ class Species
19
+ include DataMapper::Resource
20
+
21
+ property :id, Serial
22
+ property :name, String
23
+ property :classification, String, :reader => :private
24
+
25
+ has n, :animals
26
+ end
27
+
28
+ class Animal
29
+ include DataMapper::Resource
30
+
31
+ property :id, Serial
32
+ property :name, String
33
+
34
+ belongs_to :zoo
35
+ belongs_to :species
36
+ belongs_to :keeper
37
+ end
38
+
39
+ class Employee
40
+ include DataMapper::Resource
41
+
42
+ property :name, String, :key => true
43
+ end
44
+
45
+ class Keeper < Employee
46
+ has n, :animals
47
+ end
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --loadby random
3
+ --format progress
@@ -0,0 +1,86 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>=1.1.3'
3
+ require 'spec'
4
+ require 'pathname'
5
+
6
+ SPEC_ROOT = Pathname(__FILE__).dirname.expand_path
7
+ require SPEC_ROOT.parent + 'lib/dm-core'
8
+
9
+ # Load the various helpers for the spec suite
10
+ Dir[DataMapper.root / 'spec' / 'lib' / '*.rb'].each do |file|
11
+ require file
12
+ end
13
+
14
+ # setup mock adapters
15
+ DataMapper.setup(:default, "sqlite3::memory:")
16
+ DataMapper.setup(:default2, "sqlite3::memory:")
17
+
18
+ [ :mock, :legacy, :west_coast, :east_coast ].each do |repository_name|
19
+ DataMapper.setup(repository_name, "mock://localhost/#{repository_name}")
20
+ end
21
+
22
+ # These environment variables will override the default connection string:
23
+ # MYSQL_SPEC_URI
24
+ # POSTGRES_SPEC_URI
25
+ # SQLITE3_SPEC_URI
26
+ #
27
+ # For example, in the bash shell, you might use:
28
+ # export MYSQL_SPEC_URI="mysql://localhost/dm_core_test?socket=/opt/local/var/run/mysql5/mysqld.sock"
29
+ #
30
+ def setup_adapter(name, default_uri)
31
+ begin
32
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
33
+ Object.const_set('ADAPTER', ENV['ADAPTER'].to_sym) if name.to_s == ENV['ADAPTER']
34
+ true
35
+ rescue Exception => e
36
+ if name.to_s == ENV['ADAPTER']
37
+ Object.const_set('ADAPTER', nil)
38
+ warn "Could not load #{name} adapter: #{e}"
39
+ end
40
+ false
41
+ end
42
+ end
43
+
44
+ ENV['ADAPTER'] ||= 'sqlite3'
45
+
46
+ HAS_SQLITE3 = setup_adapter(:sqlite3, 'sqlite3::memory:')
47
+ HAS_MYSQL = setup_adapter(:mysql, 'mysql://localhost/dm_core_test')
48
+ HAS_POSTGRES = setup_adapter(:postgres, 'postgres://postgres@localhost/dm_core_test')
49
+
50
+ DataMapper::Logger.new(nil, :debug)
51
+
52
+ # ----------------------------------------------------------------------
53
+ # --- Do not declare new models unless absolutely necessary. Instead ---
54
+ # --- pick a metaphor and use those models. If you do need new ---
55
+ # --- models, define them according to the metaphor being used. ---
56
+ # ----------------------------------------------------------------------
57
+
58
+ Spec::Runner.configure do |config|
59
+ config.before(:each) do
60
+ # load_models_for_metaphor :vehicles
61
+ end
62
+ end
63
+
64
+ # ----------------------------------------------------------------------
65
+ # --- All these models are going to be removed. Don't use them!!! ---
66
+ # ----------------------------------------------------------------------
67
+
68
+ class Article
69
+ include DataMapper::Resource
70
+
71
+ property :id, Serial
72
+ property :blog_id, Integer
73
+ property :created_at, DateTime
74
+ property :author, String
75
+ property :title, String
76
+ end
77
+
78
+ class Comment
79
+ include DataMapper::Resource
80
+
81
+ property :id, Serial # blah
82
+ end
83
+
84
+ class NormalClass
85
+ # should not include DataMapper::Resource
86
+ end
@@ -0,0 +1,133 @@
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