dm-types 1.1.0 → 1.2.0.rc1
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/Gemfile +12 -11
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/dm-types.gemspec +43 -72
- data/lib/dm-types.rb +1 -0
- data/lib/dm-types/api_key.rb +30 -0
- data/lib/dm-types/csv.rb +3 -0
- data/lib/dm-types/enum.rb +4 -9
- data/lib/dm-types/epoch_time.rb +12 -2
- data/lib/dm-types/json.rb +7 -4
- data/lib/dm-types/paranoid/base.rb +8 -11
- data/lib/dm-types/support/dirty_minder.rb +166 -0
- data/lib/dm-types/yaml.rb +4 -1
- data/spec/fixtures/api_user.rb +14 -0
- data/spec/fixtures/person.rb +1 -0
- data/spec/integration/api_key_spec.rb +27 -0
- data/spec/integration/dirty_minder_spec.rb +197 -0
- data/spec/integration/epoch_time_spec.rb +61 -0
- data/spec/integration/ip_address_spec.rb +4 -8
- data/spec/integration/json_spec.rb +1 -0
- data/spec/integration/slug_spec.rb +30 -26
- data/spec/unit/epoch_time_spec.rb +21 -5
- data/spec/unit/json_spec.rb +2 -2
- data/spec/unit/paranoid_boolean_spec.rb +13 -1
- data/spec/unit/paranoid_datetime_spec.rb +12 -1
- data/spec/unit/yaml_spec.rb +4 -4
- metadata +146 -92
data/lib/dm-types/yaml.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'dm-core'
|
3
|
+
require 'dm-types/support/dirty_minder'
|
3
4
|
|
4
5
|
module DataMapper
|
5
6
|
class Property
|
@@ -15,7 +16,7 @@ module DataMapper
|
|
15
16
|
elsif value.is_a?(::String)
|
16
17
|
::YAML.load(value)
|
17
18
|
else
|
18
|
-
raise ArgumentError
|
19
|
+
raise ArgumentError, '+value+ of a property of YAML type must be nil or a String'
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
@@ -33,6 +34,8 @@ module DataMapper
|
|
33
34
|
value
|
34
35
|
end
|
35
36
|
|
37
|
+
include ::DataMapper::Property::DirtyMinder
|
38
|
+
|
36
39
|
end # class Yaml
|
37
40
|
end # class Property
|
38
41
|
end # module DataMapper
|
data/spec/fixtures/person.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
try_spec do
|
4
|
+
require './spec/fixtures/api_user'
|
5
|
+
|
6
|
+
describe DataMapper::TypesFixtures::APIUser do
|
7
|
+
supported_by :all do
|
8
|
+
subject { described_class.new(:name => 'alice') }
|
9
|
+
|
10
|
+
let(:original_api_key) { subject.api_key }
|
11
|
+
|
12
|
+
it "should have a default value" do
|
13
|
+
original_api_key.should_not be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should preserve the default value" do
|
17
|
+
subject.api_key.should == original_api_key
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should generate unique API Keys for each resource" do
|
21
|
+
other_resource = described_class.new(:name => 'eve')
|
22
|
+
|
23
|
+
other_resource.api_key.should_not == original_api_key
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
try_spec do
|
4
|
+
|
5
|
+
# FIXME: DirtyMinder is currently unsupported on RBX, because unlike the other
|
6
|
+
# supported Rubies, RBX core class (e.g. Array, Hash) methods use #send(). In
|
7
|
+
# other words, the other Rubies don't use #send() (they map directly to their
|
8
|
+
# C functions).
|
9
|
+
#
|
10
|
+
# The current methodology takes advantage of this by using #send() to forward
|
11
|
+
# method invocations we've hooked. Supporting RBX will require finding
|
12
|
+
# another way, possibly for all Rubies. In the meantime, something is better
|
13
|
+
# than nothing.
|
14
|
+
next if defined?(RUBY_ENGINE) and RUBY_ENGINE == 'rbx'
|
15
|
+
|
16
|
+
require './spec/fixtures/person'
|
17
|
+
|
18
|
+
describe DataMapper::TypesFixtures::Person do
|
19
|
+
supported_by :all do
|
20
|
+
before :all do
|
21
|
+
@resource = DataMapper::TypesFixtures::Person.new(:name => 'Thomas Edison')
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'with positions indirectly mutated as a hash' do
|
25
|
+
before :all do
|
26
|
+
@resource.positions = {
|
27
|
+
'company' => "Soon To Be Dirty, LLC",
|
28
|
+
'title' => "Layperson",
|
29
|
+
'details' => { 'awesome' => true },
|
30
|
+
}
|
31
|
+
@resource.save
|
32
|
+
@resource.reload
|
33
|
+
@resource.positions['title'].should == 'Layperson'
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "when I change positions" do
|
37
|
+
before :all do
|
38
|
+
@resource.clean?.should == true
|
39
|
+
@resource.positions['title'] = 'Chief Layer of People'
|
40
|
+
@resource.save
|
41
|
+
@resource.reload
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should remember the new position" do
|
45
|
+
@resource.positions['title'].should == 'Chief Layer of People'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "when I add a new attribute of the position" do
|
50
|
+
before :all do
|
51
|
+
@resource.clean?.should == true
|
52
|
+
@resource.positions['pays_buttloads_of_money'] = true
|
53
|
+
@resource.save
|
54
|
+
@resource.reload
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should remember the new attribute" do
|
58
|
+
@resource.positions['pays_buttloads_of_money'].should be(true)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "when I change the details of the position" do
|
63
|
+
before :all do
|
64
|
+
@resource.clean?.should == true
|
65
|
+
@resource.positions['details'].merge!('awesome' => "VERY TRUE")
|
66
|
+
@resource.save
|
67
|
+
@resource.reload
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should remember the changed detail" do
|
71
|
+
pending "not supported (YET)" do
|
72
|
+
# TODO: Not supported (yet?) -- this is a much harder problem to
|
73
|
+
# solve: using mutating accessors of nested objects. We could
|
74
|
+
# detect it from #dirty? (using the #hash method), but #dirty?
|
75
|
+
# only returns the status of known-mutated properties (not full,
|
76
|
+
# on-demand scan of object dirty-ness).
|
77
|
+
@resource.positions['details']['awesome'].should == 'VERY TRUE'
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "when I reload the resource while the property is dirty" do
|
83
|
+
before :all do
|
84
|
+
@resource.positions['title'] = 'Chief Layer of People'
|
85
|
+
@resource.reload
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should reflect the previously set/persisted value" do
|
89
|
+
@resource.positions.should_not be_nil
|
90
|
+
@resource.positions['title'].should == 'Layperson'
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end # positions indirectly mutated as a hash
|
95
|
+
|
96
|
+
describe 'with positions indirectly mutated as an array' do
|
97
|
+
before :all do
|
98
|
+
@resource.positions = [
|
99
|
+
{ 'company' => "Soon To Be Dirty, LLC",
|
100
|
+
'title' => "Layperson",
|
101
|
+
'details' => { 'awesome' => true },
|
102
|
+
},
|
103
|
+
]
|
104
|
+
@resource.save
|
105
|
+
@resource.reload
|
106
|
+
@resource.positions.first['title'].should == 'Layperson'
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "when I remove the position" do
|
110
|
+
before :all do
|
111
|
+
@resource.clean?.should == true
|
112
|
+
@resource.positions.pop
|
113
|
+
@resource.save
|
114
|
+
@resource.reload
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should know there aren't any positions" do
|
118
|
+
@resource.positions.should == []
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "when I add a new position" do
|
123
|
+
before :all do
|
124
|
+
@resource.clean?.should == true
|
125
|
+
@resource.positions << {
|
126
|
+
'company' => "Down and Dirty, LP",
|
127
|
+
'title' => "Porn Star",
|
128
|
+
'details' => { 'awesome' => "also true" },
|
129
|
+
}
|
130
|
+
@resource.save
|
131
|
+
@resource.reload
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should know there's two positions" do
|
135
|
+
@resource.positions.length.should == 2
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should know which position is which" do
|
139
|
+
@resource.positions.first['title'].should == "Layperson"
|
140
|
+
@resource.positions.last['title'].should == "Porn Star"
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "when I change the details of one of the positions" do
|
144
|
+
before :all do
|
145
|
+
@resource.positions.last['details'].merge!('high_risk' => true)
|
146
|
+
@resource.save
|
147
|
+
@resource.reload
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should remember the changed detail" do
|
151
|
+
pending "not supported (YET)" do
|
152
|
+
# TODO: Not supported (yet?) -- this is a much harder problem to
|
153
|
+
# solve: using mutating accessors of nested objects. We could
|
154
|
+
# detect it from #dirty? (using the #hash method), but #dirty?
|
155
|
+
# only returns the status of known-mutated properties (not full,
|
156
|
+
# on-demand scan of object dirty-ness).
|
157
|
+
@resource.positions.last['details']['high_risk'].should == true
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end # when I add a new position
|
162
|
+
|
163
|
+
describe "when I remove the position with a block-based mutator" do
|
164
|
+
before :all do
|
165
|
+
@resource.clean?.should == true
|
166
|
+
@resource.positions.reject! { |_| true }
|
167
|
+
@resource.save
|
168
|
+
@resource.reload
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should know there aren't any positions" do
|
172
|
+
@resource.positions.should == []
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "when I mutate positions through a reference" do
|
177
|
+
before :all do
|
178
|
+
@resource.clean?.should == true
|
179
|
+
@positions = @resource.positions
|
180
|
+
@positions << {
|
181
|
+
'company' => "Ooga Booga, Inc",
|
182
|
+
'title' => "Rocker",
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should reflect the change in both the property and the reference" do
|
187
|
+
@resource.positions.length.should == 2
|
188
|
+
@resource.positions.last['title'].should == 'Rocker'
|
189
|
+
@positions.last['title'].should == 'Rocker'
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
end # positions indirectly mutated as an array
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
try_spec do
|
4
|
+
|
5
|
+
require './spec/fixtures/person'
|
6
|
+
|
7
|
+
describe DataMapper::TypesFixtures::Person do
|
8
|
+
supported_by :all do
|
9
|
+
before :all do
|
10
|
+
@resource = DataMapper::TypesFixtures::Person.new(:name => '')
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'with a birthday' do
|
14
|
+
before :all do
|
15
|
+
@resource.birthday = '1983-05-03'
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'after typecasting string input' do
|
19
|
+
it 'has a valid birthday' do
|
20
|
+
@resource.birthday.should == ::Time.parse('1983-05-03')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'when dumped and loaded again' do
|
25
|
+
before :all do
|
26
|
+
@resource.save.should be(true)
|
27
|
+
@resource.reload
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'has a valid birthday' do
|
31
|
+
@resource.birthday.should == ::Time.parse('1983-05-03')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'without a birthday' do
|
37
|
+
before :all do
|
38
|
+
@resource.birthday = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'after typecasting nil' do
|
42
|
+
it 'has a nil value for birthday' do
|
43
|
+
@resource.birthday.should be_nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'when dumped and loaded again' do
|
48
|
+
before :all do
|
49
|
+
@resource.save.should be(true)
|
50
|
+
@resource.reload
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'has a nil value for birthday' do
|
54
|
+
@resource.birthday.should be_nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,15 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
Spec::Matchers.define :run_ipv4 do
|
4
|
-
match
|
5
|
-
model.runs_ipv4?
|
6
|
-
end
|
3
|
+
Spec::Matchers.define :run_ipv4 do
|
4
|
+
match { |model| model.runs_ipv4? }
|
7
5
|
end
|
8
6
|
|
9
|
-
Spec::Matchers.define :run_ipv6 do
|
10
|
-
match
|
11
|
-
model.runs_ipv6?
|
12
|
-
end
|
7
|
+
Spec::Matchers.define :run_ipv6 do
|
8
|
+
match { |model| model.runs_ipv6? }
|
13
9
|
end
|
14
10
|
|
15
11
|
try_spec do
|
@@ -25,35 +25,39 @@ try_spec do
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
it "has slug equal to '#{slug}'" do
|
46
|
-
@resource.slug.should == slug
|
47
|
-
end
|
48
|
-
|
49
|
-
describe "and persisted" do
|
28
|
+
# FIXME: when stringex fixes the problems it has with it's YAML
|
29
|
+
# files not being parsable by psych remove this conditional.
|
30
|
+
unless RUBY_PLATFORM =~ /java/ && JRUBY_VERSION >= '1.6' && RUBY_VERSION >= '1.9.2'
|
31
|
+
[
|
32
|
+
[ 'Iñtërnâtiônàlizætiøn', 'internationalizaetion' ],
|
33
|
+
[ "This is Dan's Blog", 'this-is-dans-blog' ],
|
34
|
+
[ 'This is My Site, and Blog', 'this-is-my-site-and-blog' ],
|
35
|
+
[ 'Google searches for holy grail of Python performance', 'google-searches-for-holy-grail-of-python-performance' ],
|
36
|
+
[ 'iPhone dev: Creating length-controlled data sources', 'iphone-dev-creating-length-controlled-data-sources' ],
|
37
|
+
[ "Review: Nintendo's New DSi -- A Quantum Leap Forward", 'review-nintendos-new-dsi-a-quantum-leap-forward' ],
|
38
|
+
[ "Arriva BraiVe, è l'auto-robot che si 'guida' da sola'", 'arriva-braive-e-lauto-robot-che-si-guida-da-sola' ],
|
39
|
+
[ "La ley antipiratería reduce un 33% el tráfico online en Suecia", 'la-ley-antipirateria-reduce-un-33-percent-el-trafico-online-en-suecia' ],
|
40
|
+
[ "L'Etat américain du Texas s'apprête à interdire Windows Vista", 'letat-americain-du-texas-sapprete-a-interdire-windows-vista' ],
|
41
|
+
].each do |title, slug|
|
42
|
+
describe "set with title '#{title}'" do
|
50
43
|
before :all do
|
51
|
-
@resource.
|
52
|
-
@resource.
|
44
|
+
@resource = DataMapper::TypesFixtures::Article.new(:title => title)
|
45
|
+
@resource.valid?.should be(true)
|
53
46
|
end
|
54
47
|
|
55
|
-
it
|
56
|
-
|
48
|
+
it "has slug equal to '#{slug}'" do
|
49
|
+
@resource.slug.should == slug
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "and persisted" do
|
53
|
+
before :all do
|
54
|
+
@resource.save.should be(true)
|
55
|
+
@resource.reload
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'can be found by slug' do
|
59
|
+
DataMapper::TypesFixtures::Article.first(:slug => slug).should == @resource
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
@@ -21,22 +21,38 @@ describe DataMapper::Property::EpochTime do
|
|
21
21
|
it { should == value.to_i }
|
22
22
|
end
|
23
23
|
|
24
|
+
describe 'with nil' do
|
25
|
+
let(:value) { nil }
|
26
|
+
|
27
|
+
it { should == value }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#typecast' do
|
32
|
+
subject { @property.typecast(value) }
|
33
|
+
|
24
34
|
describe 'with a DateTime instance' do
|
25
35
|
let(:value) { DateTime.now }
|
26
36
|
|
27
|
-
it { should == Time.parse(value.to_s)
|
37
|
+
it { should == Time.parse(value.to_s) }
|
28
38
|
end
|
29
39
|
|
30
40
|
describe 'with a number' do
|
31
41
|
let(:value) { Time.now.to_i }
|
32
42
|
|
33
|
-
it { should == value }
|
43
|
+
it { should == ::Time.at(value) }
|
34
44
|
end
|
35
45
|
|
36
|
-
describe 'with
|
37
|
-
let(:value) {
|
46
|
+
describe 'with a numeric string' do
|
47
|
+
let(:value) { Time.now.to_i.to_s }
|
38
48
|
|
39
|
-
it { should == value }
|
49
|
+
it { should == ::Time.at(value.to_i) }
|
50
|
+
end
|
51
|
+
|
52
|
+
describe 'with a DateTime string' do
|
53
|
+
let(:value) { '2011-07-11 15:00:04 UTC' }
|
54
|
+
|
55
|
+
it { should == ::Time.parse(value) }
|
40
56
|
end
|
41
57
|
end
|
42
58
|
|