dm-core 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +27 -0
  4. data/LICENSE +20 -0
  5. data/{README.txt → README.rdoc} +14 -3
  6. data/Rakefile +23 -22
  7. data/VERSION +1 -0
  8. data/dm-core.gemspec +201 -10
  9. data/lib/dm-core.rb +32 -23
  10. data/lib/dm-core/adapters.rb +0 -1
  11. data/lib/dm-core/adapters/data_objects_adapter.rb +230 -151
  12. data/lib/dm-core/adapters/mysql_adapter.rb +7 -8
  13. data/lib/dm-core/adapters/oracle_adapter.rb +39 -59
  14. data/lib/dm-core/adapters/postgres_adapter.rb +0 -1
  15. data/lib/dm-core/adapters/sqlite3_adapter.rb +5 -0
  16. data/lib/dm-core/adapters/sqlserver_adapter.rb +114 -0
  17. data/lib/dm-core/adapters/yaml_adapter.rb +0 -5
  18. data/lib/dm-core/associations/many_to_many.rb +118 -56
  19. data/lib/dm-core/associations/many_to_one.rb +48 -21
  20. data/lib/dm-core/associations/one_to_many.rb +8 -30
  21. data/lib/dm-core/associations/one_to_one.rb +1 -5
  22. data/lib/dm-core/associations/relationship.rb +89 -97
  23. data/lib/dm-core/collection.rb +299 -184
  24. data/lib/dm-core/core_ext/enumerable.rb +28 -0
  25. data/lib/dm-core/core_ext/kernel.rb +0 -2
  26. data/lib/dm-core/migrations.rb +314 -170
  27. data/lib/dm-core/model.rb +97 -66
  28. data/lib/dm-core/model/descendant_set.rb +1 -1
  29. data/lib/dm-core/model/hook.rb +0 -3
  30. data/lib/dm-core/model/property.rb +7 -10
  31. data/lib/dm-core/model/relationship.rb +79 -26
  32. data/lib/dm-core/model/scope.rb +3 -4
  33. data/lib/dm-core/property.rb +152 -90
  34. data/lib/dm-core/property_set.rb +18 -37
  35. data/lib/dm-core/query.rb +452 -153
  36. data/lib/dm-core/query/conditions/comparison.rb +266 -173
  37. data/lib/dm-core/query/conditions/operation.rb +499 -57
  38. data/lib/dm-core/query/direction.rb +0 -3
  39. data/lib/dm-core/query/operator.rb +0 -4
  40. data/lib/dm-core/query/path.rb +10 -12
  41. data/lib/dm-core/query/sort.rb +4 -10
  42. data/lib/dm-core/repository.rb +10 -6
  43. data/lib/dm-core/resource.rb +343 -148
  44. data/lib/dm-core/spec/adapter_shared_spec.rb +17 -1
  45. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +277 -17
  46. data/lib/dm-core/support/chainable.rb +0 -2
  47. data/lib/dm-core/support/equalizer.rb +27 -3
  48. data/lib/dm-core/transaction.rb +75 -75
  49. data/lib/dm-core/type.rb +19 -5
  50. data/lib/dm-core/types/discriminator.rb +4 -4
  51. data/lib/dm-core/types/object.rb +2 -7
  52. data/lib/dm-core/types/paranoid_boolean.rb +8 -2
  53. data/lib/dm-core/types/paranoid_datetime.rb +8 -2
  54. data/lib/dm-core/version.rb +1 -1
  55. data/script/performance.rb +7 -7
  56. data/script/profile.rb +6 -6
  57. data/spec/lib/collection_helpers.rb +2 -2
  58. data/spec/lib/pending_helpers.rb +22 -3
  59. data/spec/lib/rspec_immediate_feedback_formatter.rb +1 -0
  60. data/spec/public/associations/many_to_many_spec.rb +6 -4
  61. data/spec/public/associations/many_to_one_spec.rb +10 -1
  62. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +39 -0
  63. data/spec/public/associations/one_to_many_spec.rb +4 -3
  64. data/spec/public/associations/one_to_one_spec.rb +19 -1
  65. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +45 -0
  66. data/spec/public/collection_spec.rb +4 -3
  67. data/spec/public/migrations_spec.rb +144 -0
  68. data/spec/public/model/relationship_spec.rb +115 -55
  69. data/spec/public/model_spec.rb +13 -13
  70. data/spec/public/property/object_spec.rb +106 -0
  71. data/spec/public/property_spec.rb +18 -14
  72. data/spec/public/resource_spec.rb +10 -1
  73. data/spec/public/sel_spec.rb +16 -49
  74. data/spec/public/setup_spec.rb +1 -1
  75. data/spec/public/shared/association_collection_shared_spec.rb +6 -14
  76. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  77. data/spec/public/shared/collection_shared_spec.rb +214 -217
  78. data/spec/public/shared/finder_shared_spec.rb +259 -365
  79. data/spec/public/shared/resource_shared_spec.rb +524 -248
  80. data/spec/public/transaction_spec.rb +27 -3
  81. data/spec/public/types/discriminator_spec.rb +1 -1
  82. data/spec/rcov.opts +6 -0
  83. data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +17 -0
  84. data/spec/semipublic/associations/many_to_one_spec.rb +3 -20
  85. data/spec/semipublic/associations_spec.rb +2 -2
  86. data/spec/semipublic/collection_spec.rb +0 -32
  87. data/spec/semipublic/model_spec.rb +96 -0
  88. data/spec/semipublic/property_spec.rb +3 -3
  89. data/spec/semipublic/query/conditions/comparison_spec.rb +1719 -0
  90. data/spec/semipublic/query/conditions/operation_spec.rb +1292 -0
  91. data/spec/semipublic/query_spec.rb +1285 -144
  92. data/spec/semipublic/resource_spec.rb +0 -24
  93. data/spec/semipublic/shared/resource_shared_spec.rb +103 -38
  94. data/spec/spec.opts +1 -1
  95. data/spec/spec_helper.rb +15 -6
  96. data/tasks/ci.rake +1 -0
  97. data/tasks/metrics.rake +37 -0
  98. data/tasks/spec.rake +41 -0
  99. data/tasks/yard.rake +9 -0
  100. data/tasks/yardstick.rake +19 -0
  101. metadata +99 -29
  102. data/CONTRIBUTING +0 -51
  103. data/FAQ +0 -93
  104. data/History.txt +0 -27
  105. data/MIT-LICENSE +0 -22
  106. data/Manifest.txt +0 -121
  107. data/QUICKLINKS +0 -11
  108. data/SPECS +0 -35
  109. data/TODO +0 -1
  110. data/spec/semipublic/query/conditions_spec.rb +0 -528
  111. data/tasks/ci.rb +0 -24
  112. data/tasks/dm.rb +0 -58
  113. data/tasks/doc.rb +0 -17
  114. data/tasks/gemspec.rb +0 -23
  115. data/tasks/hoe.rb +0 -45
  116. data/tasks/install.rb +0 -18
data/lib/dm-core/type.rb CHANGED
@@ -84,20 +84,18 @@ module DataMapper
84
84
  # end # module Types
85
85
  # end # module DataMapper
86
86
  class Type
87
-
88
87
  # Until cooperation of Property and Type does not change, each must
89
88
  # have a separate list of options, because plugins (ex.: dm-validations)
90
89
  # may want to extend one or the other, and expects no side effects
91
90
  PROPERTY_OPTIONS = [
92
91
  :accessor, :reader, :writer,
93
- :lazy, :default, :nullable, :key, :serial, :field, :size, :length,
92
+ :lazy, :default, :key, :serial, :field, :size, :length,
94
93
  :format, :index, :unique_index, :auto_validation,
95
- :validates, :unique, :precision, :scale, :min, :max
94
+ :validates, :unique, :precision, :scale, :min, :max,
95
+ :allow_nil, :allow_blank, :required
96
96
  ]
97
97
 
98
98
  class << self
99
-
100
- # TODO: document
101
99
  # @api private
102
100
  def configure(primitive_type, options)
103
101
  warn "DataMapper.Type.configure is deprecated, specify the primitive and options explicitly (#{caller[0]})"
@@ -126,6 +124,16 @@ module DataMapper
126
124
  def primitive(primitive = nil)
127
125
  return @primitive if primitive.nil?
128
126
  @primitive = primitive
127
+
128
+ return unless @primitive.respond_to?(:options)
129
+ options = @primitive.options
130
+
131
+ return unless options.respond_to?(:each)
132
+
133
+ # inherit the options from the primitive if any
134
+ options.each do |key, value|
135
+ send(key, value) unless send(key)
136
+ end
129
137
  end
130
138
 
131
139
  # Load Property options
@@ -141,6 +149,12 @@ module DataMapper
141
149
  RUBY
142
150
  end
143
151
 
152
+ def nullable(value)
153
+ # :required is preferable to :allow_nil, but :nullable maps precisely to :allow_nil
154
+ warn "#nullable is deprecated, use #required instead (#{caller[0]})"
155
+ allow_nil(value)
156
+ end
157
+
144
158
  # Gives all the options set on this type
145
159
  #
146
160
  # @return [Hash] with all options and their values set on this type
@@ -3,15 +3,15 @@ module DataMapper
3
3
  class Discriminator < Type
4
4
  primitive Class
5
5
  default lambda { |resource, property| resource.model }
6
- nullable false
6
+ required true
7
7
 
8
- # TODO: document
9
8
  # @api private
10
9
  def self.bind(property)
11
10
  repository_name = property.repository_name
12
11
  model = property.model
12
+ property_name = property.name
13
13
 
14
- model.default_scope(repository_name).update(property.name => model.descendants)
14
+ model.default_scope(repository_name).update(property_name => model.descendants)
15
15
 
16
16
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
17
17
  extend Chainable
@@ -38,7 +38,7 @@ module DataMapper
38
38
  private
39
39
 
40
40
  def set_discriminator_scope_for(model)
41
- model.default_scope(#{repository_name.inspect}).update(#{property.name.inspect} => model.descendants)
41
+ model.default_scope(#{repository_name.inspect}).update(#{property_name.inspect} => model.descendants)
42
42
  end
43
43
  end
44
44
  RUBY
@@ -1,23 +1,18 @@
1
1
  module DataMapper
2
2
  module Types
3
3
  class Object < Type
4
- primitive String
5
- length 65535
6
- lazy true
4
+ primitive Text
7
5
 
8
- # TODO: document
9
6
  # @api private
10
7
  def self.typecast(value, property)
11
8
  value
12
9
  end
13
10
 
14
- # TODO: document
15
11
  # @api private
16
12
  def self.dump(value, property)
17
- [ Marshal.dump(value) ].pack('m')
13
+ [ Marshal.dump(value) ].pack('m') unless value.nil?
18
14
  end
19
15
 
20
- # TODO: document
21
16
  # @api private
22
17
  def self.load(value, property)
23
18
  Marshal.load(value.unpack('m').first) unless value.nil?
@@ -5,7 +5,6 @@ module DataMapper
5
5
  default false
6
6
  lazy true
7
7
 
8
- # TODO: document
9
8
  # @api private
10
9
  def self.bind(property)
11
10
  repository_name = property.repository_name
@@ -22,10 +21,17 @@ module DataMapper
22
21
  end
23
22
 
24
23
  def destroy
24
+ paranoid_destroy
25
+ end
26
+
27
+ def paranoid_destroy
25
28
  self.class.paranoid_properties.each do |name, blk|
26
29
  attribute_set(name, blk.call(self))
27
30
  end
28
- save
31
+ save_self
32
+ @_destroyed = true
33
+ @_readonly = true
34
+ reset
29
35
  end
30
36
  RUBY
31
37
 
@@ -4,7 +4,6 @@ module DataMapper
4
4
  primitive DateTime
5
5
  lazy true
6
6
 
7
- # TODO: document
8
7
  # @api private
9
8
  def self.bind(property)
10
9
  repository_name = property.repository_name
@@ -21,10 +20,17 @@ module DataMapper
21
20
  end
22
21
 
23
22
  def destroy
23
+ paranoid_destroy
24
+ end
25
+
26
+ def paranoid_destroy
24
27
  self.class.paranoid_properties.each do |name, blk|
25
28
  attribute_set(name, blk.call(self))
26
29
  end
27
- save
30
+ save_self
31
+ @_destroyed = true
32
+ @_readonly = true
33
+ reset
28
34
  end
29
35
  RUBY
30
36
 
@@ -1,3 +1,3 @@
1
1
  module DataMapper
2
- VERSION = '0.10.1'.freeze
2
+ VERSION = '0.10.2'.freeze
3
3
  end
@@ -1,12 +1,12 @@
1
- #!/usr/bin/env ruby -KU
1
+ #!/usr/bin/env ruby -Ku
2
2
 
3
3
  require 'ftools'
4
4
  require 'rubygems'
5
5
 
6
- gem 'activerecord', '~>2.3.2'
7
- gem 'addressable', '~>2.0'
8
- gem 'faker', '~>0.3.1'
9
- gem 'rbench', '~>0.2.3'
6
+ gem 'activerecord', '~> 2.3.4'
7
+ gem 'addressable', '~> 2.1'
8
+ gem 'faker', '~> 0.3.1'
9
+ gem 'rbench', '~> 0.2.3'
10
10
 
11
11
  require 'active_record'
12
12
  require 'addressable/uri'
@@ -144,13 +144,13 @@ else
144
144
 
145
145
  if sqlfile
146
146
  answer = nil
147
- until answer && answer[/^$|y|yes|n|no/]
147
+ until answer && answer[/\A(?:y(?:es)?|no?)\b/i]
148
148
  print('Would you like to dump data into tmp/performance.sql (for faster setup)? [Yn]');
149
149
  STDOUT.flush
150
150
  answer = gets
151
151
  end
152
152
 
153
- if answer[/^$|y|yes/]
153
+ if %w[ y yes ].include?(answer.downcase)
154
154
  File.makedirs(File.dirname(sqlfile))
155
155
  #adapter.execute("SELECT * INTO OUTFILE '#{sqlfile}' FROM exhibits;")
156
156
  `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{sqlfile}`
data/script/profile.rb CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env ruby -KU
1
+ #!/usr/bin/env ruby -Ku
2
2
 
3
3
  require 'ftools'
4
4
  require 'rubygems'
5
5
 
6
- gem 'addressable', '~>2.0'
7
- gem 'faker', '~>0.3.1'
8
- gem 'ruby-prof', '~>0.7.3'
6
+ gem 'addressable', '~> 2.1'
7
+ gem 'faker', '~> 0.3.1'
8
+ gem 'ruby-prof', '~> 0.7.3'
9
9
 
10
10
  require 'addressable/uri'
11
11
  require 'faker'
@@ -139,13 +139,13 @@ else
139
139
 
140
140
  if sqlfile
141
141
  answer = nil
142
- until answer && answer[/^$|y|yes|n|no/]
142
+ until answer && answer[/\A(?:y(?:es)?|no?)\b/i]
143
143
  print('Would you like to dump data into tmp/performance.sql (for faster setup)? [Yn]');
144
144
  STDOUT.flush
145
145
  answer = gets
146
146
  end
147
147
 
148
- if answer[/^$|y|yes/]
148
+ if %w[ y yes ].include?(answer.downcase)
149
149
  File.makedirs(File.dirname(sqlfile))
150
150
  #adapter.execute("SELECT * INTO OUTFILE '#{sqlfile}' FROM exhibits;")
151
151
  `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{sqlfile}`
@@ -6,10 +6,10 @@ module DataMapper::Spec
6
6
  base.loaded = false
7
7
  end
8
8
 
9
- def should_not_be_a_kicker
9
+ def should_not_be_a_kicker(ivar = :@articles)
10
10
  unless loaded
11
11
  it 'should not be a kicker' do
12
- @articles.should_not be_loaded
12
+ instance_variable_get(ivar).should_not be_loaded
13
13
  end
14
14
  end
15
15
  end
@@ -1,6 +1,8 @@
1
1
  module DataMapper::Spec
2
2
  module PendingHelpers
3
- def pending_if(message, boolean = true)
3
+ def pending_if(*args)
4
+ message, boolean = parse_args(*args)
5
+
4
6
  if boolean
5
7
  pending(message) { yield }
6
8
  else
@@ -8,7 +10,9 @@ module DataMapper::Spec
8
10
  end
9
11
  end
10
12
 
11
- def rescue_if(message, boolean = true)
13
+ def rescue_if(*args)
14
+ message, boolean = parse_args(*args)
15
+
12
16
  if boolean
13
17
  raised = nil
14
18
  begin
@@ -18,10 +22,25 @@ module DataMapper::Spec
18
22
  raised = true
19
23
  end
20
24
 
21
- raise 'should have raised' if raised == false
25
+ raise "should have raised: #{message || 'TODO'}" if raised == false
22
26
  else
23
27
  yield
24
28
  end
25
29
  end
30
+
31
+ private
32
+
33
+ def parse_args(*args)
34
+ case args.map { |arg| arg.class }
35
+ when [ String, TrueClass ], [ String, FalseClass ] then args
36
+ when [ String, NilClass ] then [ args.first, false ]
37
+ when [ String ] then [ args.first, true ]
38
+ when [ TrueClass ], [ FalseClass ] then [ '', args.first ]
39
+ when [ NilClass ] then [ '', false ]
40
+ when [] then [ '', true ] # defaults
41
+ else
42
+ raise ArgumentError, "Invalid arguments: #{args.inspect}"
43
+ end
44
+ end
26
45
  end
27
46
  end
@@ -33,6 +33,7 @@ module Spec
33
33
  end
34
34
 
35
35
  output.puts(red(message))
36
+ # output.puts(failure.expectation_not_met? ? red(message) : message)
36
37
  dump_failure(counter, failure) # dump stacktrace immediately
37
38
  output.flush
38
39
  end
@@ -40,12 +40,12 @@ end
40
40
  include DataMapper::Resource
41
41
 
42
42
  property :id, Serial
43
- property :title, String, :nullable => false
43
+ property :title, String, :required => true
44
44
  property :content, Text
45
45
  property :subtitle, String
46
46
 
47
47
  has n, :authors, :through => Resource
48
- belongs_to :original, self, :nullable => true
48
+ belongs_to :original, self, :required => false
49
49
  has n, :revisions, self, :child_key => [ :original_id ]
50
50
  has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ]
51
51
  has n, :publications, :through => Resource
@@ -94,6 +94,7 @@ end
94
94
  it_should_behave_like 'A public Association Collection'
95
95
  it_should_behave_like 'A Collection supporting Strategic Eager Loading' unless loaded
96
96
  it_should_behave_like 'Finder Interface'
97
+ it_should_behave_like 'Collection Finder Interface'
97
98
  it_should_behave_like 'A Limited Many to Many Collection'
98
99
  end
99
100
  end
@@ -129,7 +130,7 @@ end
129
130
  include DataMapper::Resource
130
131
 
131
132
  property :id, Serial
132
- property :title, String, :nullable => false
133
+ property :title, String, :required => true
133
134
  property :content, Text
134
135
  property :subtitle, String
135
136
 
@@ -137,7 +138,7 @@ end
137
138
 
138
139
  belongs_to :site
139
140
  has n, :authors, :through => :site
140
- belongs_to :original, self, :nullable => true
141
+ belongs_to :original, self, :required => false
141
142
  has n, :revisions, self, :child_key => [ :original_id ]
142
143
  has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ]
143
144
  has n, :publications, :through => Resource
@@ -187,6 +188,7 @@ end
187
188
  it_should_behave_like 'A public Association Collection'
188
189
  it_should_behave_like 'A Collection supporting Strategic Eager Loading' unless loaded
189
190
  it_should_behave_like 'Finder Interface'
191
+ it_should_behave_like 'Collection Finder Interface'
190
192
  it_should_behave_like 'A Limited Many to Many Collection'
191
193
  end
192
194
  end
@@ -12,7 +12,10 @@ describe 'Many to One Associations' do
12
12
  property :description, Text
13
13
  property :admin, Boolean, :accessor => :private
14
14
 
15
- belongs_to :referrer, self, :nullable => true
15
+ belongs_to :parent, self, :required => false
16
+ has n, :children, self, :inverse => :parent
17
+
18
+ belongs_to :referrer, self, :required => false
16
19
  has n, :comments
17
20
 
18
21
  # FIXME: figure out a different approach than stubbing things out
@@ -51,6 +54,12 @@ describe 'Many to One Associations' do
51
54
  end
52
55
  end
53
56
 
57
+ class ::Default
58
+ include DataMapper::Resource
59
+
60
+ property :name, String, :key => true, :default => 'a default value'
61
+ end
62
+
54
63
  @user_model = Blog::User
55
64
  @author_model = Blog::Author
56
65
  @comment_model = Blog::Comment
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ # TODO: combine this into many_to_one_spec.rb
4
+
5
+ describe 'Many to One Associations when foreign key is part of a composite key, with an integer and a boolean making up the composite key' do
6
+ before :all do
7
+ class ::ManyModel
8
+ include DataMapper::Resource
9
+
10
+ property :integer_key, Integer, :key => true
11
+ property :boolean_key, Boolean, :key => true
12
+
13
+ belongs_to :one_model, :child_key => [ :integer_key ]
14
+ end
15
+
16
+ class ::OneModel
17
+ include DataMapper::Resource
18
+
19
+ property :integer_key, Integer, :key => true
20
+
21
+ has n, :many_models, :child_key => [ :integer_key ]
22
+ end
23
+ end
24
+
25
+ supported_by :all do
26
+ before :all do
27
+ @one = OneModel.create(:integer_key => 1)
28
+ @many = ManyModel.create(:integer_key => 1, :boolean_key => false)
29
+ end
30
+
31
+ it 'should be able to access parent' do
32
+ @many.one_model.should == @one
33
+ end
34
+
35
+ it 'should be able to access the child' do
36
+ @one.many_models.should == [ @many ]
37
+ end
38
+ end
39
+ end
@@ -23,12 +23,12 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_hel
23
23
  include DataMapper::Resource
24
24
 
25
25
  property :id, Serial
26
- property :title, String, :nullable => false
26
+ property :title, String, :required => true
27
27
  property :content, Text
28
28
  property :subtitle, String
29
29
 
30
- belongs_to :author, :nullable => true
31
- belongs_to :original, self, :nullable => true
30
+ belongs_to :author, :required => false
31
+ belongs_to :original, self, :required => false
32
32
  has n, :revisions, self, :child_key => [ :original_id ]
33
33
  has 1, :previous, self, :child_key => [ :original_id ], :order => [ :id.desc ]
34
34
  has n, :publications, :through => Resource
@@ -72,6 +72,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_hel
72
72
  it_should_behave_like 'A public Association Collection'
73
73
  it_should_behave_like 'A Collection supporting Strategic Eager Loading'
74
74
  it_should_behave_like 'Finder Interface'
75
+ it_should_behave_like 'Collection Finder Interface'
75
76
  end
76
77
  end
77
78
  end