dm-core 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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