dm-core 1.1.0.rc2 → 1.1.0.rc3

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 (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