dm-core 1.1.0.rc2 → 1.1.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/Gemfile +1 -8
  2. data/VERSION +1 -1
  3. data/dm-core.gemspec +24 -15
  4. data/lib/dm-core.rb +9 -48
  5. data/lib/dm-core/adapters.rb +1 -1
  6. data/lib/dm-core/adapters/abstract_adapter.rb +2 -1
  7. data/lib/dm-core/associations/many_to_many.rb +3 -3
  8. data/lib/dm-core/associations/many_to_one.rb +15 -4
  9. data/lib/dm-core/associations/one_to_many.rb +1 -1
  10. data/lib/dm-core/associations/relationship.rb +7 -6
  11. data/lib/dm-core/collection.rb +7 -2
  12. data/lib/dm-core/core_ext/pathname.rb +0 -14
  13. data/lib/dm-core/ext/array.rb +41 -0
  14. data/lib/dm-core/ext/blank.rb +24 -0
  15. data/lib/dm-core/ext/hash.rb +67 -0
  16. data/lib/dm-core/ext/module.rb +49 -0
  17. data/lib/dm-core/ext/object.rb +57 -0
  18. data/lib/dm-core/ext/singleton_class.rb +8 -0
  19. data/lib/dm-core/ext/string.rb +24 -0
  20. data/lib/dm-core/ext/try_dup.rb +12 -0
  21. data/lib/dm-core/model.rb +2 -1
  22. data/lib/dm-core/model/property.rb +3 -2
  23. data/lib/dm-core/property.rb +1 -1
  24. data/lib/dm-core/property/class.rb +1 -1
  25. data/lib/dm-core/property/date.rb +3 -3
  26. data/lib/dm-core/property/date_time.rb +3 -3
  27. data/lib/dm-core/property/time.rb +3 -3
  28. data/lib/dm-core/property/typecast/time.rb +2 -2
  29. data/lib/dm-core/property_set.rb +1 -1
  30. data/lib/dm-core/query.rb +9 -12
  31. data/lib/dm-core/resource.rb +2 -2
  32. data/lib/dm-core/spec/lib/counter_adapter.rb +1 -1
  33. data/lib/dm-core/spec/lib/spec_helper.rb +1 -1
  34. data/lib/dm-core/spec/shared/resource_spec.rb +2 -1
  35. data/lib/dm-core/support/equalizer.rb +1 -1
  36. data/lib/dm-core/support/hook.rb +0 -15
  37. data/lib/dm-core/support/inflections.rb +60 -0
  38. data/lib/dm-core/support/inflector.rb +3 -0
  39. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  40. data/lib/dm-core/support/inflector/methods.rb +151 -0
  41. data/lib/dm-core/support/lazy_array.rb +4 -4
  42. data/lib/dm-core/support/mash.rb +176 -0
  43. data/lib/dm-core/support/subject.rb +1 -1
  44. data/lib/dm-core/version.rb +1 -1
  45. data/spec/public/model/relationship_spec.rb +2 -1
  46. data/spec/public/shared/collection_shared_spec.rb +5 -2
  47. data/spec/public/shared/finder_shared_spec.rb +10 -6
  48. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +1 -1
  49. data/spec/semipublic/query_spec.rb +2 -2
  50. data/spec/semipublic/resource/state/immutable_spec.rb +2 -1
  51. data/spec/semipublic/resource/state_spec.rb +1 -3
  52. data/spec/semipublic/shared/resource_state_shared_spec.rb +2 -1
  53. data/spec/semipublic/shared/subject_shared_spec.rb +1 -1
  54. data/spec/support/core_ext/hash.rb +10 -0
  55. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  56. data/spec/unit/array_spec.rb +8 -20
  57. data/spec/unit/blank_spec.rb +62 -0
  58. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +1 -1
  59. data/spec/unit/hash_spec.rb +8 -16
  60. data/spec/unit/lazy_array_spec.rb +3 -14
  61. data/spec/unit/mash_spec.rb +312 -0
  62. data/spec/unit/module_spec.rb +15 -15
  63. data/spec/unit/object_spec.rb +9 -9
  64. data/spec/unit/try_dup_spec.rb +8 -8
  65. metadata +32 -39
  66. data/lib/dm-core/core_ext/array.rb +0 -36
  67. data/lib/dm-core/core_ext/hash.rb +0 -30
  68. data/lib/dm-core/core_ext/module.rb +0 -46
  69. data/lib/dm-core/core_ext/object.rb +0 -31
  70. data/lib/dm-core/core_ext/string.rb +0 -22
  71. data/lib/dm-core/core_ext/try_dup.rb +0 -44
@@ -1,4 +1,4 @@
1
- require 'dm-core/core_ext/try_dup'
1
+ require 'dm-core/ext/try_dup'
2
2
 
3
3
  class LazyArray # borrowed partially from StrokeDB
4
4
  include Enumerable
@@ -358,9 +358,9 @@ class LazyArray # borrowed partially from StrokeDB
358
358
  end
359
359
 
360
360
  def initialize_copy(original)
361
- @head = @head.try_dup
362
- @tail = @tail.try_dup
363
- @array = @array.try_dup
361
+ @head = DataMapper::Ext.try_dup(@head)
362
+ @tail = DataMapper::Ext.try_dup(@tail)
363
+ @array = DataMapper::Ext.try_dup(@array)
364
364
  end
365
365
 
366
366
  def lazy_load
@@ -0,0 +1,176 @@
1
+ module DataMapper
2
+ # This class has dubious semantics and we only have it so that people can write
3
+ # params[:key] instead of params['key'].
4
+ class Mash < Hash
5
+
6
+ # Initializes a new mash.
7
+ #
8
+ # @param [Hash, Object] constructor
9
+ # The default value for the mash. If +constructor+ is a Hash, a new mash
10
+ # will be created based on the keys of the hash and no default value will
11
+ # be set.
12
+ def initialize(constructor = {})
13
+ if constructor.is_a?(Hash)
14
+ super()
15
+ update(constructor)
16
+ else
17
+ super(constructor)
18
+ end
19
+ end
20
+
21
+ # Gets the default value for the mash.
22
+ #
23
+ # @param [Object] key
24
+ # The default value for the mash. If +key+ is a Symbol and it is a key in
25
+ # the mash, then the default value will be set to the value matching the
26
+ # key.
27
+ def default(key = nil)
28
+ if key.is_a?(Symbol) && include?(key = key.to_s)
29
+ self[key]
30
+ else
31
+ super
32
+ end
33
+ end
34
+
35
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
36
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
37
+
38
+ # Sets the +value+ associated with the specified +key+.
39
+ #
40
+ # @param [Object] key The key to set.
41
+ # @param [Object] value The value to set the key to.
42
+ def []=(key, value)
43
+ regular_writer(convert_key(key), convert_value(value))
44
+ end
45
+
46
+ # Updates the mash with the key/value pairs from the specified hash.
47
+ #
48
+ # @param [Hash] other_hash
49
+ # A hash to update values in the mash with. The keys and the values will be
50
+ # converted to Mash format.
51
+ #
52
+ # @return [Mash] The updated mash.
53
+ def update(other_hash)
54
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
55
+ self
56
+ end
57
+
58
+ alias_method :merge!, :update
59
+
60
+ # Determines whether the mash contains the specified +key+.
61
+ #
62
+ # @param [Object] key The key to check for.
63
+ # @return [Boolean] True if the key exists in the mash.
64
+ def key?(key)
65
+ super(convert_key(key))
66
+ end
67
+
68
+ alias_method :include?, :key?
69
+ alias_method :has_key?, :key?
70
+ alias_method :member?, :key?
71
+
72
+ # @param [Object] key The key to fetch.
73
+ # @param [Array] *extras Default value.
74
+ #
75
+ # @return [Object] The value at key or the default value.
76
+ def fetch(key, *extras)
77
+ super(convert_key(key), *extras)
78
+ end
79
+
80
+ # @param [Array] *indices
81
+ # The keys to retrieve values for.
82
+ #
83
+ # @return [Array] The values at each of the provided keys.
84
+ def values_at(*indices)
85
+ indices.collect {|key| self[convert_key(key)]}
86
+ end
87
+
88
+ # @param [Hash] hash The hash to merge with the mash.
89
+ #
90
+ # @return [Mash] A new mash with the hash values merged in.
91
+ def merge(hash)
92
+ self.dup.update(hash)
93
+ end
94
+
95
+ # @param [Object] key The key to delete from the mash.
96
+ def delete(key)
97
+ super(convert_key(key))
98
+ end
99
+
100
+ # Returns a mash that includes everything but the given +keys+.
101
+ #
102
+ # @param [Array<String, Symbol>] *keys The mash keys to exclude.
103
+ #
104
+ # @return [Mash] A new mash without the selected keys.
105
+ #
106
+ # @example
107
+ # { :one => 1, :two => 2, :three => 3 }.except(:one)
108
+ # #=> { "two" => 2, "three" => 3 }
109
+ def except(*keys)
110
+ self.dup.except!(*keys.map {|k| convert_key(k)})
111
+ end
112
+
113
+ # Removes the specified +keys+ from the mash.
114
+ #
115
+ # @param [Array] *keys The mash keys to exclude.
116
+ #
117
+ # @return [Hash] +hash+
118
+ #
119
+ # @example
120
+ # mash = { :one => 1, :two => 2, :three => 3 }
121
+ # mash.except!(:one, :two)
122
+ # mash # => { :three => 3 }
123
+ def except!(*keys)
124
+ keys.each { |key| delete(key) }
125
+ self
126
+ end
127
+
128
+ # Used to provide the same interface as Hash.
129
+ #
130
+ # @return [Mash] This mash unchanged.
131
+ def stringify_keys!; self end
132
+
133
+ # @return [Hash] The mash as a Hash with symbolized keys.
134
+ def symbolize_keys
135
+ h = Hash.new(default)
136
+ each { |key, val| h[key.to_sym] = val }
137
+ h
138
+ end
139
+
140
+ # @return [Hash] The mash as a Hash with string keys.
141
+ def to_hash
142
+ Hash.new(default).merge(self)
143
+ end
144
+
145
+ protected
146
+ # @param [Object] key The key to convert.
147
+ #
148
+ # @param [Object]
149
+ # The converted key. If the key was a symbol, it will be converted to a
150
+ # string.
151
+ #
152
+ # @api private
153
+ def convert_key(key)
154
+ key.kind_of?(Symbol) ? key.to_s : key
155
+ end
156
+
157
+ # @param [Object] value The value to convert.
158
+ #
159
+ # @return [Object]
160
+ # The converted value. A Hash or an Array of hashes, will be converted to
161
+ # their Mash equivalents.
162
+ #
163
+ # @api private
164
+ def convert_value(value)
165
+ if value.class == Hash
166
+ mash = Mash.new(value)
167
+ mash.default = value.default
168
+ mash
169
+ elsif value.is_a?(Array)
170
+ value.collect { |e| convert_value(e) }
171
+ else
172
+ value
173
+ end
174
+ end
175
+ end
176
+ end
@@ -16,7 +16,7 @@ module DataMapper
16
16
  if @default.respond_to?(:call)
17
17
  @default.call(resource, self)
18
18
  else
19
- @default.try_dup
19
+ DataMapper::Ext.try_dup(@default)
20
20
  end
21
21
  end
22
22
 
@@ -1,3 +1,3 @@
1
1
  module DataMapper
2
- VERSION = '1.1.0.rc2'
2
+ VERSION = '1.1.0.rc3'
3
3
  end
@@ -281,7 +281,8 @@ share_examples_for 'it creates a many accessor' do
281
281
  @expected.should_not be_nil
282
282
 
283
283
  # set the model scope to only return the first record
284
- @model.default_scope.update(@model.key(@repository.name).zip(@expected.key).to_hash)
284
+ @model.default_scope.update(
285
+ DataMapper::Ext::Array.to_hash(@model.key(@repository.name).zip(@expected.key)))
285
286
 
286
287
  @return = @car.model.get(*@car.key).__send__(@name)
287
288
  end
@@ -123,7 +123,10 @@ share_examples_for 'A public Collection' do
123
123
  end
124
124
 
125
125
  it 'should update the Collection inline' do
126
- @articles.each { |resource| resource.attributes.only(:title, :content).should == { :title => 'Sample Article', :content => 'New Content' } }
126
+ @articles.each { |resource|
127
+ DataMapper::Ext::Hash.only(resource.attributes, :title, :content).should ==
128
+ { :title => 'Sample Article', :content => 'New Content' }
129
+ }
127
130
  end
128
131
  end
129
132
  end
@@ -919,7 +922,7 @@ share_examples_for 'A public Collection' do
919
922
  end
920
923
 
921
924
  it 'should be a Resource with attributes matching the Hash' do
922
- @return.first.attributes.only(*@array.first.keys).should == @array.first
925
+ DataMapper::Ext::Hash.only(@return.first.attributes, *@array.first.keys).should == @array.first
923
926
  end
924
927
  end
925
928
  end
@@ -342,7 +342,8 @@ share_examples_for 'Finder Interface' do
342
342
 
343
343
  describe 'with a collection' do
344
344
  before :all do
345
- @collection = @article_model.all(@article_model.key.zip(@original.key).to_hash)
345
+ @collection = @article_model.all(
346
+ DataMapper::Ext::Array.to_hash(@article_model.key.zip(@original.key)))
346
347
 
347
348
  @return = @articles.all(:original => @collection)
348
349
  end
@@ -479,7 +480,8 @@ share_examples_for 'Finder Interface' do
479
480
 
480
481
  describe 'with a collection' do
481
482
  before :all do
482
- @collection = @article_model.all(@article_model.key.zip(@new.key).to_hash)
483
+ @collection = @article_model.all(
484
+ DataMapper::Ext::Array.to_hash(@article_model.key.zip(@new.key)))
483
485
 
484
486
  @return = @articles.all(:previous => @collection)
485
487
  end
@@ -615,7 +617,8 @@ share_examples_for 'Finder Interface' do
615
617
 
616
618
  describe 'with a collection' do
617
619
  before :all do
618
- @collection = @article_model.all(@article_model.key.zip(@new.key).to_hash)
620
+ @collection = @article_model.all(
621
+ DataMapper::Ext::Array.to_hash(@article_model.key.zip(@new.key)))
619
622
 
620
623
  @return = @articles.all(:revisions => @collection)
621
624
  end
@@ -755,7 +758,8 @@ share_examples_for 'Finder Interface' do
755
758
 
756
759
  describe 'with a collection' do
757
760
  before :all do
758
- @collection = @publication_model.all(@publication_model.key.zip(@publication.key).to_hash)
761
+ @collection = @publication_model.all(
762
+ DataMapper::Ext::Array.to_hash(@publication_model.key.zip(@publication.key)))
759
763
 
760
764
  @return = @articles.all(:publications => @collection)
761
765
  end
@@ -1093,7 +1097,7 @@ share_examples_for 'Finder Interface' do
1093
1097
  end
1094
1098
 
1095
1099
  it 'should be expected Resource' do
1096
- @resource.attributes.only(*@conditions.keys).should == @conditions
1100
+ DataMapper::Ext::Hash.only(@resource.attributes, *@conditions.keys).should == @conditions
1097
1101
  end
1098
1102
 
1099
1103
  it 'should be a saved Resource' do
@@ -1136,7 +1140,7 @@ share_examples_for 'Finder Interface' do
1136
1140
  end
1137
1141
 
1138
1142
  it 'should be expected Resource' do
1139
- @resource.attributes.only(*@conditions.keys).should == @conditions
1143
+ DataMapper::Ext::Hash.only(@resource.attributes, *@conditions.keys).should == @conditions
1140
1144
  end
1141
1145
 
1142
1146
  it 'should not be a saved Resource' do
@@ -1,5 +1,5 @@
1
1
  require 'spec_helper'
2
- require DataMapper.root / 'lib' / 'dm-core' / 'spec' / 'shared' / 'adapter_spec'
2
+ require 'dm-core/spec/shared/adapter_spec'
3
3
 
4
4
  describe 'Adapter' do
5
5
  supported_by :in_memory do
@@ -791,7 +791,7 @@ describe DataMapper::Query do
791
791
  it 'should raise an exception' do
792
792
  lambda {
793
793
  DataMapper::Query.new(@repository, @model, @options.update(:conditions => { 'unknown.id' => 1 }))
794
- }.should raise_error(ArgumentError, "condition \"unknown.id\" does not map to a relationship in #{@model}")
794
+ }.should raise_error(ArgumentError, "condition \"unknown.id\" does not map to a property or relationship in #{@model}")
795
795
  end
796
796
  end
797
797
 
@@ -1952,7 +1952,7 @@ describe DataMapper::Query do
1952
1952
  end
1953
1953
 
1954
1954
  it 'should return expected value' do
1955
- @return.should == <<-INSPECT.compress_lines
1955
+ @return.should == DataMapper::Ext::String.compress_lines(<<-INSPECT)
1956
1956
  #<DataMapper::Query
1957
1957
  @repository=:default
1958
1958
  @model=User
@@ -21,7 +21,8 @@ describe DataMapper::Resource::State::Immutable do
21
21
  @parent = @model.create(:name => 'John Doe')
22
22
 
23
23
  @resource = @model.create(:name => 'Dan Kubb', :parent => @parent)
24
- @resource = @model.first(@model.key.zip(@resource.key).to_hash.merge(:fields => [ :name, :parent_id ]))
24
+ attributes = DataMapper::Ext::Array.to_hash(@model.key.zip(@resource.key))
25
+ @resource = @model.first(attributes.merge(:fields => [ :name, :parent_id ]))
25
26
 
26
27
  @state = @resource.persisted_state
27
28
  @state.should be_kind_of(DataMapper::Resource::State::Immutable)
@@ -181,9 +181,7 @@ describe DataMapper::Resource::State do
181
181
  describe '#hash' do
182
182
  subject { @state.hash }
183
183
 
184
- it 'should be the hash of the resource' do
185
- should == @resource.hash
186
- end
184
+ it { should == @state.class.hash ^ @resource.hash }
187
185
  end
188
186
 
189
187
  describe '#resource' do
@@ -49,7 +49,8 @@ share_examples_for 'Resource::State::Persisted#get' do
49
49
  @resource.should be_dirty
50
50
  @resource.save.should be(true)
51
51
 
52
- @resource = @model.first(@model.key.zip(@resource.key).to_hash.merge(:fields => @model.key))
52
+ attributes = DataMapper::Ext::Array.to_hash(@model.key.zip(@resource.key))
53
+ @resource = @model.first(attributes.merge(:fields => @model.key))
53
54
  @state = @state.class.new(@resource)
54
55
 
55
56
  # make sure the subject is not loaded
@@ -18,7 +18,7 @@ share_examples_for 'A semipublic Subject' do
18
18
  subject { @subject_without_default.default_for(@resource) }
19
19
 
20
20
  it 'should match the default value' do
21
- should be_blank
21
+ DataMapper::Ext.blank?(subject).should == true
22
22
  end
23
23
 
24
24
  it 'should be used as a default for the subject accessor' do
@@ -0,0 +1,10 @@
1
+ class Hash
2
+ def except(*keys)
3
+ dup.except!(*keys)
4
+ end
5
+
6
+ def except!(*keys)
7
+ keys.each { |key| delete(key) }
8
+ self
9
+ end
10
+ end
@@ -0,0 +1,46 @@
1
+ class Class
2
+ def class_inheritable_reader(*ivars)
3
+ instance_reader = ivars.pop[:reader] if ivars.last.is_a?(Hash)
4
+
5
+ ivars.each do |ivar|
6
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
7
+ def self.#{ivar}
8
+ return @#{ivar} if defined?(@#{ivar})
9
+ return nil if self.object_id == #{self.object_id}
10
+ ivar = superclass.#{ivar}
11
+ return nil if ivar.nil?
12
+ @#{ivar} = DataMapper::Ext.try_dup(ivar)
13
+ end
14
+ RUBY
15
+
16
+ unless instance_reader == false
17
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{ivar}
19
+ self.class.#{ivar}
20
+ end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+
26
+ def class_inheritable_writer(*ivars)
27
+ instance_writer = ivars.pop[:instance_writer] if ivars.last.is_a?(Hash)
28
+ ivars.each do |ivar|
29
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
30
+ def self.#{ivar}=(obj)
31
+ @#{ivar} = obj
32
+ end
33
+ RUBY
34
+ unless instance_writer == false
35
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
36
+ def #{ivar}=(obj) self.class.#{ivar} = obj end
37
+ RUBY
38
+ end
39
+ end
40
+ end
41
+
42
+ def class_inheritable_accessor(*syms)
43
+ class_inheritable_reader(*syms)
44
+ class_inheritable_writer(*syms)
45
+ end
46
+ end
@@ -1,25 +1,15 @@
1
1
  require 'spec_helper'
2
- require 'dm-core/core_ext/array'
2
+ require 'dm-core/ext/array'
3
+ require 'dm-core/support/mash'
3
4
 
4
- begin
5
- require 'active_support/hash_with_indifferent_access'
6
- unless defined?(Mash)
7
- Mash = ActiveSupport::HashWithIndifferentAccess
8
- end
9
- rescue LoadError
10
- require 'extlib/mash'
11
- end
12
-
13
- describe Array do
5
+ describe DataMapper::Ext::Array do
14
6
  before :all do
15
7
  @array = [ [ :a, [ 1 ] ], [ :b, [ 2 ] ], [ :c, [ 3 ] ] ].freeze
16
8
  end
17
9
 
18
- it { @array.should respond_to(:to_hash) }
19
-
20
- describe '#to_hash' do
10
+ describe '.to_hash' do
21
11
  before :all do
22
- @return = @array.to_hash
12
+ @return = DataMapper::Ext::Array.to_hash(@array)
23
13
  end
24
14
 
25
15
  it 'should return a Hash' do
@@ -31,15 +21,13 @@ describe Array do
31
21
  end
32
22
  end
33
23
 
34
- it { @array.should respond_to(:to_mash) }
35
-
36
- describe '#to_mash' do
24
+ describe '.to_mash' do
37
25
  before :all do
38
- @return = @array.to_mash
26
+ @return = DataMapper::Ext::Array.to_mash(@array)
39
27
  end
40
28
 
41
29
  it 'should return a Mash' do
42
- @return.should be_kind_of(Mash)
30
+ @return.should be_kind_of(DataMapper::Mash)
43
31
  end
44
32
 
45
33
  it 'should return expected value' do