dm-core 1.0.0 → 1.0.1

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 (42) hide show
  1. data/.gitignore +1 -0
  2. data/Gemfile +19 -20
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/dm-core.gemspec +15 -12
  6. data/lib/dm-core.rb +23 -14
  7. data/lib/dm-core/adapters.rb +7 -3
  8. data/lib/dm-core/adapters/abstract_adapter.rb +3 -9
  9. data/lib/dm-core/associations/many_to_many.rb +1 -1
  10. data/lib/dm-core/associations/many_to_one.rb +3 -4
  11. data/lib/dm-core/associations/one_to_many.rb +2 -2
  12. data/lib/dm-core/associations/one_to_one.rb +1 -1
  13. data/lib/dm-core/collection.rb +4 -6
  14. data/lib/dm-core/model.rb +22 -32
  15. data/lib/dm-core/model/property.rb +15 -39
  16. data/lib/dm-core/property.rb +75 -87
  17. data/lib/dm-core/property/discriminator.rb +3 -3
  18. data/lib/dm-core/property/lookup.rb +42 -0
  19. data/lib/dm-core/property/object.rb +5 -0
  20. data/lib/dm-core/property/serial.rb +6 -1
  21. data/lib/dm-core/query/conditions/comparison.rb +3 -3
  22. data/lib/dm-core/query/conditions/operation.rb +3 -3
  23. data/lib/dm-core/resource.rb +2 -2
  24. data/lib/dm-core/resource/state/dirty.rb +2 -2
  25. data/lib/dm-core/spec/lib/spec_helper.rb +11 -3
  26. data/lib/dm-core/spec/setup.rb +2 -2
  27. data/{spec/public/shared/property_shared_spec.rb → lib/dm-core/spec/shared/public/property_spec.rb} +51 -20
  28. data/{spec/semipublic/shared/property_shared_spec.rb → lib/dm-core/spec/shared/semipublic/property_spec.rb} +22 -26
  29. data/lib/dm-core/support/descendant_set.rb +84 -0
  30. data/lib/dm-core/support/naming_conventions.rb +8 -8
  31. data/lib/dm-core/types/discriminator.rb +2 -2
  32. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  33. data/spec/public/finalize_spec.rb +42 -11
  34. data/spec/public/property/discriminator_spec.rb +6 -6
  35. data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
  36. data/spec/semipublic/property/lookup_spec.rb +26 -0
  37. data/spec/semipublic/property_spec.rb +43 -0
  38. data/spec/semipublic/resource/state/dirty_spec.rb +4 -2
  39. data/spec/support/{types → properties}/huge_integer.rb +5 -5
  40. data/tasks/local_gemfile.rake +5 -7
  41. metadata +15 -19
  42. data/lib/dm-core/model/descendant_set.rb +0 -81
@@ -8,7 +8,7 @@ module DataMapper
8
8
 
9
9
  # @api private
10
10
  def bind
11
- model.default_scope(repository_name).update(name => model.descendants)
11
+ model.default_scope(repository_name).update(name => model.descendants.dup << model)
12
12
 
13
13
  model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
14
14
  extend Chainable
@@ -38,11 +38,11 @@ module DataMapper
38
38
  private
39
39
 
40
40
  def set_discriminator_scope_for(model)
41
- model.default_scope(#{repository_name.inspect}).update(#{name.inspect} => model.descendants)
41
+ model.default_scope(#{repository_name.inspect}).update(#{name.inspect} => model.descendants.dup << model)
42
42
  end
43
43
  end
44
44
  RUBY
45
45
  end
46
46
  end # class Discriminator
47
- end # module Types
47
+ end # module Property
48
48
  end # module DataMapper
@@ -0,0 +1,42 @@
1
+ module DataMapper
2
+ class Property
3
+ module Lookup
4
+
5
+ protected
6
+
7
+ #
8
+ # Provides transparent access to the Properties defined in
9
+ # {Property}. It also provides access to the legacy {Types} namespace.
10
+ #
11
+ # @param [Symbol] name
12
+ # The name of the property to lookup.
13
+ #
14
+ # @return [Property, Type]
15
+ # The property with the given name.
16
+ #
17
+ # @raise [NameError]
18
+ # The property could not be found.
19
+ #
20
+ # @api private
21
+ #
22
+ # @since 1.0.1
23
+ #
24
+ def const_missing(name)
25
+ if const = Property.find_class(name)
26
+ return const
27
+ end
28
+
29
+ # only check within DataMapper::Types, if it was loaded.
30
+ if DataMapper.const_defined?(:Types)
31
+ if DataMapper::Types.const_defined?(name)
32
+ type = DataMapper::Types.const_get(name)
33
+
34
+ return type if type < DataMapper::Type
35
+ end
36
+ end
37
+
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
@@ -27,6 +27,11 @@ module DataMapper
27
27
  value
28
28
  end
29
29
  end
30
+
31
+ # @api private
32
+ def to_child_key
33
+ self.class
34
+ end
30
35
  end
31
36
  end
32
37
  end
@@ -3,6 +3,11 @@ module DataMapper
3
3
  class Serial < Integer
4
4
  serial true
5
5
  min 1
6
+
7
+ # @api private
8
+ def to_child_key
9
+ Property::Integer
10
+ end
6
11
  end # class Text
7
- end # module Types
12
+ end # module Property
8
13
  end # module DataMapper
@@ -160,14 +160,14 @@ module DataMapper
160
160
  # @return [Set<AbstractComparison>]
161
161
  # @api private
162
162
  def self.descendants
163
- @descendants ||= Set.new
163
+ @descendants ||= DescendantSet.new
164
164
  end
165
165
 
166
166
  # Registers AbstractComparison subclasses (used in Comparison)
167
167
  #
168
168
  # @api private
169
- def self.inherited(comparison_class)
170
- descendants << comparison_class
169
+ def self.inherited(descendant)
170
+ descendants << descendant
171
171
  end
172
172
 
173
173
  # Setter/getter: allows subclasses to easily set their slug
@@ -97,7 +97,7 @@ module DataMapper
97
97
  #
98
98
  # @api private
99
99
  def self.descendants
100
- @descendants ||= Set.new
100
+ @descendants ||= DescendantSet.new
101
101
  end
102
102
 
103
103
  # Hook executed when inheriting from AbstractComparison
@@ -105,8 +105,8 @@ module DataMapper
105
105
  # @return [undefined]
106
106
  #
107
107
  # @api private
108
- def self.inherited(operation_class)
109
- descendants << operation_class
108
+ def self.inherited(descendant)
109
+ descendants << descendant
110
110
  end
111
111
 
112
112
  # Get and set the slug for the operation class
@@ -756,8 +756,8 @@ module DataMapper
756
756
  # attribute values used in the new instance
757
757
  #
758
758
  # @api public
759
- def initialize(attributes = {}) # :nodoc:
760
- self.attributes = attributes || {}
759
+ def initialize(attributes = nil) # :nodoc:
760
+ self.attributes = attributes if attributes
761
761
  end
762
762
 
763
763
  # @api private
@@ -44,9 +44,9 @@ module DataMapper
44
44
  if original_attributes[subject].eql?(value)
45
45
  original_attributes.delete(subject)
46
46
  end
47
- else
47
+ elsif !value.eql?(original = get(subject))
48
48
  # track the original value
49
- original_attributes[subject] = get(subject)
49
+ original_attributes[subject] = original
50
50
  end
51
51
  end
52
52
 
@@ -14,10 +14,10 @@ module DataMapper
14
14
  # global model cleanup
15
15
  def self.cleanup_models
16
16
  descendants = DataMapper::Model.descendants.to_a
17
- while model = descendants.shift
18
- descendants.concat(model.descendants.to_a - [ model ])
19
17
 
18
+ while model = descendants.shift
20
19
  model_name = model.name.to_s.strip
20
+
21
21
  unless model_name.empty? || model_name[0] == ?#
22
22
  parts = model_name.split('::')
23
23
  constant_name = parts.pop.to_sym
@@ -31,6 +31,10 @@ module DataMapper
31
31
 
32
32
  DataMapper::Model.descendants.delete(model)
33
33
  end
34
+
35
+ unless DataMapper::Model.descendants.empty?
36
+ raise 'DataMapper::Model.descendants is not empty'
37
+ end
34
38
  end
35
39
 
36
40
  def self.remove_ivars(object, instance_variables = object.instance_variables)
@@ -42,7 +46,7 @@ module DataMapper
42
46
 
43
47
  # skip "global" and non-DM objects
44
48
  next if object.kind_of?(DataMapper::Logger) ||
45
- object.kind_of?(DataMapper::Model::DescendantSet) ||
49
+ object.kind_of?(DataMapper::DescendantSet) ||
46
50
  object.kind_of?(DataMapper::Adapters::AbstractAdapter) ||
47
51
  object.class.name[0, 13] == 'DataObjects::'
48
52
 
@@ -55,6 +59,10 @@ module DataMapper
55
59
  next unless object.instance_variable_defined?(ivar)
56
60
 
57
61
  value = object.instance_variable_get(ivar)
62
+
63
+ # skip descendant sets
64
+ next if value.kind_of?(DataMapper::DescendantSet)
65
+
58
66
  object.__send__(:remove_instance_variable, ivar) unless object.frozen?
59
67
 
60
68
  # skip when the value was seen
@@ -148,8 +148,8 @@ module DataMapper
148
148
  private
149
149
 
150
150
  def infer_adapter_name
151
- demodulized = ActiveSupport::Inflector.demodulize(self.class.name.chomp('Adapter'))
152
- ActiveSupport::Inflector.underscore(demodulized).freeze
151
+ demodulized = DataMapper::Inflector.demodulize(self.class.name.chomp('Adapter'))
152
+ DataMapper::Inflector.underscore(demodulized).freeze
153
153
  end
154
154
 
155
155
  end
@@ -43,17 +43,20 @@ share_examples_for 'A public Property' do
43
43
 
44
44
  describe "auto-generated option setters" do
45
45
  before :all do
46
- [@type, @subtype].each do |type|
47
- type.foo true
48
- type.bar 1
49
- @property = type.new(@model, @name)
50
- end
46
+ @type.foo true
47
+ @type.bar 1
48
+ @property = @type.new(@model, @name)
51
49
  end
52
50
 
53
51
  it "should set the pre-defined option values" do
54
52
  @property.options[:foo].should == true
55
53
  @property.options[:bar].should == 1
56
54
  end
55
+
56
+ it "should ask the superclass for the value if unknown" do
57
+ @subtype.foo.should == true
58
+ @subtype.bar.should == 1
59
+ end
57
60
  end
58
61
  end
59
62
  end
@@ -154,23 +157,51 @@ share_examples_for 'A public Property' do
154
157
  end
155
158
  end
156
159
 
157
- [:instance_of?, :kind_of?].each do |method|
158
- describe "##{method}" do
159
- before :all do
160
- @property = @type.new(@model, @name)
161
- end
160
+ describe '#instance_of?' do
161
+ subject { property.instance_of?(klass) }
162
162
 
163
- describe "when provided Property" do
164
- it "should return true" do
165
- @property.send(method, DataMapper::Property).should be(true)
166
- end
167
- end
163
+ let(:property) { @type.new(@model, @name) }
168
164
 
169
- describe "when provided property class" do
170
- it "should return true" do
171
- @property.send(method, @type).should be(true)
172
- end
173
- end
165
+ context 'when provided the property class' do
166
+ let(:klass) { @type }
167
+
168
+ it { should be(true) }
169
+ end
170
+
171
+ context 'when provided the property superclass' do
172
+ let(:klass) { @type.superclass }
173
+
174
+ it { should be(false) }
175
+ end
176
+
177
+ context 'when provided the DataMapper::Property class' do
178
+ let(:klass) { DataMapper::Property }
179
+
180
+ it { should be(false) }
181
+ end
182
+ end
183
+
184
+ describe '#kind_of?' do
185
+ subject { property.kind_of?(klass) }
186
+
187
+ let(:property) { @type.new(@model, @name) }
188
+
189
+ context 'when provided the property class' do
190
+ let(:klass) { @type }
191
+
192
+ it { should be(true) }
193
+ end
194
+
195
+ context 'when provided the property superclass' do
196
+ let(:klass) { @type.superclass }
197
+
198
+ it { should be(true) }
199
+ end
200
+
201
+ context 'when provided the DataMapper::Property class' do
202
+ let(:klass) { DataMapper::Property }
203
+
204
+ it { should be(true) }
174
205
  end
175
206
  end
176
207
  end
@@ -88,6 +88,28 @@ share_examples_for 'A semipublic Property' do
88
88
  end
89
89
  end
90
90
 
91
+ describe "#typecast" do
92
+ describe "when is able to do typecasting on it's own" do
93
+ it 'delegates all the work to the type' do
94
+ return_value = mock(@other_value)
95
+ @property.should_receive(:typecast_to_primitive).with(@invalid_value).and_return(return_value)
96
+ @property.typecast(@invalid_value)
97
+ end
98
+ end
99
+
100
+ describe 'when value is nil' do
101
+ it 'returns value unchanged' do
102
+ @property.typecast(nil).should be(nil)
103
+ end
104
+
105
+ describe 'when value is a Ruby primitive' do
106
+ it 'returns value unchanged' do
107
+ @property.typecast(@value).should == @value
108
+ end
109
+ end
110
+ end
111
+ end
112
+
91
113
  describe '#valid?' do
92
114
  describe 'when provided a valid value' do
93
115
  it 'should return true' do
@@ -115,30 +137,4 @@ share_examples_for 'A semipublic Property' do
115
137
  end
116
138
  end
117
139
  end
118
-
119
- describe "#typecast" do
120
- describe "when is able to do typecasting on it's own" do
121
- it 'delegates all the work to the type' do
122
- return_value = mock(@other_value)
123
- @property.should_receive(:typecast_to_primitive).with(@invalid_value).and_return(return_value)
124
- @property.typecast(@invalid_value)
125
- end
126
- end
127
-
128
- describe 'when value is nil' do
129
- it 'returns value unchanged' do
130
- @property.typecast(nil).should be(nil)
131
- end
132
-
133
- describe 'when value is a Ruby primitive' do
134
- it 'returns value unchanged' do
135
- @property.typecast(@value).should == @value
136
- end
137
- end
138
- end
139
- end
140
-
141
- describe '#value' do
142
- it 'returns value for a core type'
143
- end
144
140
  end
@@ -0,0 +1,84 @@
1
+ module DataMapper
2
+ class DescendantSet
3
+ include Enumerable
4
+
5
+ # Initialize a DescendantSet instance
6
+ #
7
+ # @param [#to_ary] descendants
8
+ # initialize with the descendants
9
+ #
10
+ # @return [undefined]
11
+ #
12
+ # @api private
13
+ def initialize(descendants = [])
14
+ @descendants = descendants.to_ary
15
+ end
16
+
17
+ # Copy a DescendantSet instance
18
+ #
19
+ # @param [DescendantSet] original
20
+ # the original descendants
21
+ #
22
+ # @return [undefined]
23
+ #
24
+ # @api private
25
+ def initialize_copy(original)
26
+ @descendants = @descendants.dup
27
+ end
28
+
29
+ # Add a descendant
30
+ #
31
+ # @param [Module] descendant
32
+ #
33
+ # @return [DescendantSet]
34
+ # self
35
+ #
36
+ # @api private
37
+ def <<(descendant)
38
+ @descendants << descendant
39
+ self
40
+ end
41
+
42
+ # Remove a descendant
43
+ #
44
+ # Also removes from all descendants
45
+ #
46
+ # @param [Module] descendant
47
+ #
48
+ # @return [DescendantSet]
49
+ # self
50
+ #
51
+ # @api private
52
+ def delete(descendant)
53
+ @descendants.delete(descendant)
54
+ each { |d| d.descendants.delete(descendant) }
55
+ end
56
+
57
+ # Iterate over each descendant
58
+ #
59
+ # @yield [descendant]
60
+ # @yieldparam [Module] descendant
61
+ #
62
+ # @return [DescendantSet]
63
+ # self
64
+ #
65
+ # @api private
66
+ def each
67
+ @descendants.each do |descendant|
68
+ yield descendant
69
+ descendant.descendants.each { |dd| yield dd }
70
+ end
71
+ self
72
+ end
73
+
74
+ # Test if there are any descendants
75
+ #
76
+ # @return [Boolean]
77
+ #
78
+ # @api private
79
+ def empty?
80
+ @descendants.empty?
81
+ end
82
+
83
+ end # class DescendantSet
84
+ end # module DataMapper