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.
- data/.gitignore +1 -0
- data/Gemfile +19 -20
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/dm-core.gemspec +15 -12
- data/lib/dm-core.rb +23 -14
- data/lib/dm-core/adapters.rb +7 -3
- data/lib/dm-core/adapters/abstract_adapter.rb +3 -9
- data/lib/dm-core/associations/many_to_many.rb +1 -1
- data/lib/dm-core/associations/many_to_one.rb +3 -4
- data/lib/dm-core/associations/one_to_many.rb +2 -2
- data/lib/dm-core/associations/one_to_one.rb +1 -1
- data/lib/dm-core/collection.rb +4 -6
- data/lib/dm-core/model.rb +22 -32
- data/lib/dm-core/model/property.rb +15 -39
- data/lib/dm-core/property.rb +75 -87
- data/lib/dm-core/property/discriminator.rb +3 -3
- data/lib/dm-core/property/lookup.rb +42 -0
- data/lib/dm-core/property/object.rb +5 -0
- data/lib/dm-core/property/serial.rb +6 -1
- data/lib/dm-core/query/conditions/comparison.rb +3 -3
- data/lib/dm-core/query/conditions/operation.rb +3 -3
- data/lib/dm-core/resource.rb +2 -2
- data/lib/dm-core/resource/state/dirty.rb +2 -2
- data/lib/dm-core/spec/lib/spec_helper.rb +11 -3
- data/lib/dm-core/spec/setup.rb +2 -2
- data/{spec/public/shared/property_shared_spec.rb → lib/dm-core/spec/shared/public/property_spec.rb} +51 -20
- data/{spec/semipublic/shared/property_shared_spec.rb → lib/dm-core/spec/shared/semipublic/property_spec.rb} +22 -26
- data/lib/dm-core/support/descendant_set.rb +84 -0
- data/lib/dm-core/support/naming_conventions.rb +8 -8
- data/lib/dm-core/types/discriminator.rb +2 -2
- data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
- data/spec/public/finalize_spec.rb +42 -11
- data/spec/public/property/discriminator_spec.rb +6 -6
- data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
- data/spec/semipublic/property/lookup_spec.rb +26 -0
- data/spec/semipublic/property_spec.rb +43 -0
- data/spec/semipublic/resource/state/dirty_spec.rb +4 -2
- data/spec/support/{types → properties}/huge_integer.rb +5 -5
- data/tasks/local_gemfile.rake +5 -7
- metadata +15 -19
- 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
|
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
|
@@ -160,14 +160,14 @@ module DataMapper
|
|
160
160
|
# @return [Set<AbstractComparison>]
|
161
161
|
# @api private
|
162
162
|
def self.descendants
|
163
|
-
@descendants ||=
|
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(
|
170
|
-
descendants <<
|
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 ||=
|
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(
|
109
|
-
descendants <<
|
108
|
+
def self.inherited(descendant)
|
109
|
+
descendants << descendant
|
110
110
|
end
|
111
111
|
|
112
112
|
# Get and set the slug for the operation class
|
data/lib/dm-core/resource.rb
CHANGED
@@ -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 =
|
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
|
-
|
47
|
+
elsif !value.eql?(original = get(subject))
|
48
48
|
# track the original value
|
49
|
-
original_attributes[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::
|
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
|
data/lib/dm-core/spec/setup.rb
CHANGED
@@ -148,8 +148,8 @@ module DataMapper
|
|
148
148
|
private
|
149
149
|
|
150
150
|
def infer_adapter_name
|
151
|
-
demodulized =
|
152
|
-
|
151
|
+
demodulized = DataMapper::Inflector.demodulize(self.class.name.chomp('Adapter'))
|
152
|
+
DataMapper::Inflector.underscore(demodulized).freeze
|
153
153
|
end
|
154
154
|
|
155
155
|
end
|
data/{spec/public/shared/property_shared_spec.rb → lib/dm-core/spec/shared/public/property_spec.rb}
RENAMED
@@ -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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
158
|
-
|
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
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|