ricordami 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -0
- data/ISSUES.md +0 -0
- data/MIT-LICENSE +21 -0
- data/README.md +454 -0
- data/TODO.md +21 -0
- data/examples/calls.rb +64 -0
- data/examples/singers.rb +42 -0
- data/lib/ricordami/attribute.rb +52 -0
- data/lib/ricordami/can_be_queried.rb +133 -0
- data/lib/ricordami/can_be_validated.rb +27 -0
- data/lib/ricordami/can_have_relationships.rb +152 -0
- data/lib/ricordami/configuration.rb +39 -0
- data/lib/ricordami/connection.rb +22 -0
- data/lib/ricordami/exceptions.rb +14 -0
- data/lib/ricordami/has_attributes.rb +159 -0
- data/lib/ricordami/has_indices.rb +105 -0
- data/lib/ricordami/is_lockable.rb +52 -0
- data/lib/ricordami/is_persisted.rb +123 -0
- data/lib/ricordami/is_retrievable.rb +35 -0
- data/lib/ricordami/key_namer.rb +63 -0
- data/lib/ricordami/model.rb +29 -0
- data/lib/ricordami/query.rb +68 -0
- data/lib/ricordami/relationship.rb +40 -0
- data/lib/ricordami/unique_index.rb +59 -0
- data/lib/ricordami/unique_validator.rb +21 -0
- data/lib/ricordami/value_index.rb +26 -0
- data/lib/ricordami/version.rb +3 -0
- data/lib/ricordami.rb +26 -0
- data/spec/acceptance/manage_relationships_spec.rb +42 -0
- data/spec/acceptance/model_with_validation_spec.rb +78 -0
- data/spec/acceptance/query_model_spec.rb +93 -0
- data/spec/acceptance_helper.rb +2 -0
- data/spec/ricordami/attribute_spec.rb +113 -0
- data/spec/ricordami/can_be_queried_spec.rb +254 -0
- data/spec/ricordami/can_be_validated_spec.rb +115 -0
- data/spec/ricordami/can_have_relationships_spec.rb +255 -0
- data/spec/ricordami/configuration_spec.rb +45 -0
- data/spec/ricordami/connection_spec.rb +25 -0
- data/spec/ricordami/exceptions_spec.rb +43 -0
- data/spec/ricordami/has_attributes_spec.rb +266 -0
- data/spec/ricordami/has_indices_spec.rb +73 -0
- data/spec/ricordami/is_lockable_spec.rb +45 -0
- data/spec/ricordami/is_persisted_spec.rb +186 -0
- data/spec/ricordami/is_retrievable_spec.rb +55 -0
- data/spec/ricordami/key_namer_spec.rb +56 -0
- data/spec/ricordami/model_spec.rb +65 -0
- data/spec/ricordami/query_spec.rb +156 -0
- data/spec/ricordami/relationship_spec.rb +123 -0
- data/spec/ricordami/unique_index_spec.rb +87 -0
- data/spec/ricordami/unique_validator_spec.rb +41 -0
- data/spec/ricordami/value_index_spec.rb +40 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/constants.rb +43 -0
- data/spec/support/db_manager.rb +18 -0
- data/test/bin/data_loader.rb +107 -0
- data/test/data/domains.txt +462 -0
- data/test/data/first_names.txt +1220 -0
- data/test/data/last_names.txt +1028 -0
- data/test/data/people_100_000.csv.bz2 +0 -0
- data/test/data/people_10_000.csv.bz2 +0 -0
- data/test/data/people_1_000_000.csv.bz2 +0 -0
- metadata +258 -0
@@ -0,0 +1,255 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require "spec_helper"
|
3
|
+
require "ricordami/can_have_relationships"
|
4
|
+
|
5
|
+
describe Ricordami::CanHaveRelationships do
|
6
|
+
uses_constants("Computer", "Software", "Editor")
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
Computer.send(:include, Ricordami::CanHaveRelationships)
|
10
|
+
Computer.attribute :model
|
11
|
+
Computer.index :unique => :model, :get_by => true
|
12
|
+
Computer.references_many :softwares
|
13
|
+
Software.send(:include, Ricordami::CanHaveRelationships)
|
14
|
+
Software.attribute :name
|
15
|
+
Software.index :unique => :name, :get_by => true
|
16
|
+
Software.referenced_in :computer
|
17
|
+
Software.referenced_in :editor
|
18
|
+
Editor.send(:include, Ricordami::CanHaveRelationships)
|
19
|
+
Editor.attribute :corp_name
|
20
|
+
Editor.index :unique => :corp_name, :get_by => true
|
21
|
+
Editor.references_one :software
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "class" do
|
25
|
+
it "can declare a :referenced_in relationship with #referenced_in" do
|
26
|
+
Software.referenced_in :computer
|
27
|
+
Software.relationships[:computer].should be_a(Ricordami::Relationship)
|
28
|
+
Software.relationships[:computer].type.should == :referenced_in
|
29
|
+
Software.relationships[:computer].name.should == :computer
|
30
|
+
Software.relationships[:computer].object_kind.should == :computer
|
31
|
+
end
|
32
|
+
|
33
|
+
it "can declare a :references_many relationship with #references_many" do
|
34
|
+
Computer.references_many :softwares, :dependent => :nullify, :as => :softs
|
35
|
+
Computer.relationships[:softs].should be_a(Ricordami::Relationship)
|
36
|
+
Computer.relationships[:softs].type.should == :references_many
|
37
|
+
Computer.relationships[:softs].name.should == :softs
|
38
|
+
Computer.relationships[:softs].object_kind.should == :software
|
39
|
+
Computer.relationships[:softs].dependent.should == :nullify
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can declare a :references_one relationship with #references_one" do
|
43
|
+
Editor.references_one :software, :dependent => :nullify
|
44
|
+
Editor.relationships[:software].should be_a(Ricordami::Relationship)
|
45
|
+
Editor.relationships[:software].type.should == :references_one
|
46
|
+
Editor.relationships[:software].name.should == :software
|
47
|
+
Editor.relationships[:software].object_kind.should == :software
|
48
|
+
Editor.relationships[:software].dependent.should == :nullify
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "instance that is referenced..." do
|
53
|
+
it "creates an attribute for the referrer id" do
|
54
|
+
game = Software.create(:name => "Masquerade")
|
55
|
+
game.computer_id.should be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "indexes the referrer id" do
|
59
|
+
iic = Computer.create(:model => "IIc")
|
60
|
+
mac = Computer.create(:model => "Macintosh")
|
61
|
+
Software.create(:name => "Masquerade", :computer_id => iic.id)
|
62
|
+
Software.create(:name => "Transylvania", :computer_id => iic.id)
|
63
|
+
Software.create(:name => "Dungeon Master", :computer_id => mac.id)
|
64
|
+
Software.where(:computer_id => iic.id).map(&:name).should =~ ["Masquerade", "Transylvania"]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can access the referrer with a method of the name of the reference" do
|
68
|
+
game = Software.create(:name => "Masquerade")
|
69
|
+
game.computer.should be_nil
|
70
|
+
computer = Computer.create(:model => "IIc")
|
71
|
+
game.computer_id = computer.id
|
72
|
+
game.computer.should be_a(Computer)
|
73
|
+
game.computer.model.should == "IIc"
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "handling caching" do
|
77
|
+
before(:each) do
|
78
|
+
%w(IIc Macintosh).each { |model| Computer.create(:model => model).should be_true }
|
79
|
+
end
|
80
|
+
|
81
|
+
it "caches the referrer isntance after it was cached" do
|
82
|
+
computer = Computer.get_by_model("IIc")
|
83
|
+
game = Software.create(:name => "Masquerade", :computer_id => computer.id)
|
84
|
+
game.computer.should == game.computer
|
85
|
+
end
|
86
|
+
|
87
|
+
it "updates the referrer when changing the referrer id" do
|
88
|
+
game = Software.create(:name => "Sorcery")
|
89
|
+
game.computer.should be_nil
|
90
|
+
game.computer_id = Computer.get_by_model("IIc").id
|
91
|
+
game.computer.model.should == "IIc"
|
92
|
+
game.computer_id = Computer.get_by_model("Macintosh").id
|
93
|
+
game.computer.model.should == "Macintosh"
|
94
|
+
game.computer_id = nil
|
95
|
+
game.computer.should be_nil
|
96
|
+
end
|
97
|
+
|
98
|
+
it "sweeps the referrer instance from the cache after it is reloaded" do
|
99
|
+
game = Software.create(:name => "Swashbuckler",
|
100
|
+
:computer_id => Computer.get_by_model("IIc").id)
|
101
|
+
game.computer.model.should == "IIc"
|
102
|
+
game.update_attributes(:computer_id => Computer.get_by_model("Macintosh").id)
|
103
|
+
game.computer.model.should == "Macintosh"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "instance that references many..." do
|
109
|
+
before(:each) do
|
110
|
+
@iic = Computer.create(:model => "IIc")
|
111
|
+
@mac = Computer.create(:model => "MacBook Air")
|
112
|
+
[
|
113
|
+
[@iic, "Masquerade"], [@iic, "Transylvania"], [@iic, "Bruce Lee"],
|
114
|
+
[@iic, "Karateka"], [@mac, "Half-Life"], [@mac, "Chopper 2"]
|
115
|
+
].each do |computer, soft_name|
|
116
|
+
Software.create(:name => soft_name, :computer_id => computer.id)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "has a method to access its referenced objects" do
|
121
|
+
@iic.should respond_to(:softwares)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "can list all the objects it references with the reference method" do
|
125
|
+
@iic.softwares.map(&:name).should =~ ["Masquerade", "Transylvania",
|
126
|
+
"Bruce Lee", "Karateka"]
|
127
|
+
@mac.softwares.map(&:name).should =~ ["Half-Life", "Chopper 2"]
|
128
|
+
end
|
129
|
+
|
130
|
+
it "can return there is no reference objects when non persisted" do
|
131
|
+
Computer.new.softwares.map(&:name).should == []
|
132
|
+
Computer.new.softwares.should be_empty
|
133
|
+
Computer.new.softwares.count.should == 0
|
134
|
+
end
|
135
|
+
|
136
|
+
it "can build a new reference object through the reference method" do
|
137
|
+
soft = @mac.softwares.build
|
138
|
+
soft.should_not be_persisted
|
139
|
+
soft.computer_id.should == @mac.id
|
140
|
+
end
|
141
|
+
|
142
|
+
it "can create a new reference object through the reference method" do
|
143
|
+
soft = @mac.softwares.create(:name => "Call of Duty 4")
|
144
|
+
soft.should be_persisted
|
145
|
+
soft.computer_id.should == @mac.id
|
146
|
+
soft.name.should == "Call of Duty 4"
|
147
|
+
end
|
148
|
+
|
149
|
+
it "can have more than 1 reference for the same type of object" do
|
150
|
+
Computer.references_many :softwares, :as => :softs, :alias => :host, :dependent => :delete
|
151
|
+
Software.referenced_in :computer, :as => :host, :alias => :softs
|
152
|
+
iie = Computer.create(:model => "IIe")
|
153
|
+
iie.softs.create(:name => "Masquerade")
|
154
|
+
iie.softwares.create(:name => "Conan")
|
155
|
+
iie.softs.map(&:name).should == ["Masquerade"]
|
156
|
+
iie.softwares.map(&:name).should == ["Conan"]
|
157
|
+
end
|
158
|
+
|
159
|
+
it "deletes dependents when delete is set to :dependent" do
|
160
|
+
Computer.references_many :softwares, :as => :softs, :dependent => :delete
|
161
|
+
iie = Computer.create(:model => "IIe")
|
162
|
+
iie.softs.create(:name => "Castle Wolfenstein")
|
163
|
+
Software.get_by_name("Castle Wolfenstein").should_not be_nil
|
164
|
+
iie.delete
|
165
|
+
lambda {
|
166
|
+
Software.get_by_name("Castle Wolfenstein")
|
167
|
+
}.should raise_error(Ricordami::NotFound)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "sets to nil reference's referrer_id when delete is set to :nullify" do
|
171
|
+
Computer.references_many :softwares, :as => :softs, :alias => :host, :dependent => :nullify
|
172
|
+
Software.referenced_in :computer, :as => :host, :alias => :softs
|
173
|
+
iie = Computer.create(:model => "IIe")
|
174
|
+
iie.reload
|
175
|
+
iie.softs.create(:name => "Castle Wolfenstein")
|
176
|
+
Software.get_by_name("Castle Wolfenstein").host_id.should == iie.id
|
177
|
+
iie.delete
|
178
|
+
Software.get_by_name("Castle Wolfenstein").host_id.should be_empty
|
179
|
+
end
|
180
|
+
|
181
|
+
it "can build a new referrer object through a build referrer method" do
|
182
|
+
software = Software.create(:name => "Karateka")
|
183
|
+
editor = software.build_editor(:corp_name => "Brøderbund")
|
184
|
+
editor.should_not be_persisted
|
185
|
+
editor.id.should == software.editor_id
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "instance that references one..." do
|
190
|
+
before(:each) do
|
191
|
+
@aes = Editor.create(:corp_name => "American Eagle Software")
|
192
|
+
Software.create(:name => "Masquerade", :editor_id => @aes.id)
|
193
|
+
@datasoft = Editor.create(:corp_name => "Datasoft")
|
194
|
+
Software.create(:name => "Bruce Lee", :editor_id => @datasoft.id)
|
195
|
+
end
|
196
|
+
|
197
|
+
it "has a method to access its referenced object" do
|
198
|
+
@aes.should respond_to(:software)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "can fetch the object it references with the reference method" do
|
202
|
+
@aes.software.name.should == "Masquerade"
|
203
|
+
@datasoft.software.name.should == "Bruce Lee"
|
204
|
+
end
|
205
|
+
|
206
|
+
it "can return there is no reference objects when non persisted" do
|
207
|
+
Editor.new.software.should be_nil
|
208
|
+
end
|
209
|
+
|
210
|
+
it "can build a new reference object through a build reference method" do
|
211
|
+
soft = @aes.build_software
|
212
|
+
soft.should_not be_persisted
|
213
|
+
soft.editor_id.should == @aes.id
|
214
|
+
end
|
215
|
+
|
216
|
+
it "can create a new reference object through the reference method" do
|
217
|
+
soft = @datasoft.create_software(:name => "Alternate Reality: The City")
|
218
|
+
soft.should be_persisted
|
219
|
+
soft.editor_id.should == @datasoft.id
|
220
|
+
soft.name.should == "Alternate Reality: The City"
|
221
|
+
end
|
222
|
+
|
223
|
+
it "can have more than 1 reference for the same type of object" do
|
224
|
+
Editor.references_one :software, :as => :soft, :alias => :second_editor, :dependent => :delete
|
225
|
+
Software.referenced_in :editor, :as => :second_editor, :alias => :soft
|
226
|
+
broderbund = Editor.create(:corp_name => "Brøderbund")
|
227
|
+
broderbund.create_software(:name => "Karateka")
|
228
|
+
broderbund.create_soft(:name => "Lode Runner")
|
229
|
+
broderbund.software.name.should == "Karateka"
|
230
|
+
broderbund.soft.name.should == "Lode Runner"
|
231
|
+
end
|
232
|
+
|
233
|
+
it "deletes dependents when delete is set to :dependent" do
|
234
|
+
Editor.references_one :software, :as => :soft, :dependent => :delete
|
235
|
+
broderbund = Editor.create(:corp_name => "Brøderbund")
|
236
|
+
broderbund.create_soft(:name => "Karateka")
|
237
|
+
Software.get_by_name("Karateka").should_not be_nil
|
238
|
+
broderbund.delete
|
239
|
+
lambda {
|
240
|
+
Software.get_by_name("Karateka")
|
241
|
+
}.should raise_error(Ricordami::NotFound)
|
242
|
+
end
|
243
|
+
|
244
|
+
it "sets to nil reference's referrer_id when delete is set to :nullify" do
|
245
|
+
Editor.references_one :software, :as => :soft, :alias => :developper, :dependent => :nullify
|
246
|
+
Software.referenced_in :editor, :as => :developper, :alias => :soft
|
247
|
+
broderbund = Editor.create(:corp_name => "Brøderbund")
|
248
|
+
broderbund.reload
|
249
|
+
broderbund.create_soft(:name => "Choplifter")
|
250
|
+
Software.get_by_name("Choplifter").developper_id.should == broderbund.id
|
251
|
+
broderbund.delete
|
252
|
+
Software.get_by_name("Choplifter").developper_id.should be_empty
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ricordami::Configuration, " using #configure" do
|
4
|
+
it "raises an error of no block is given" do
|
5
|
+
lambda { Ricordami.configure }.should raise_error(ArgumentError)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "raises an error when a config attribute doesn't exist" do
|
9
|
+
lambda {
|
10
|
+
Ricordami.configuration.does_not_exist
|
11
|
+
}.should raise_error(Ricordami::AttributeNotSupported)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can configure redis host with #redis_host" do
|
15
|
+
Ricordami.configure { |config| config.redis_host = "my_host" }
|
16
|
+
Ricordami.configuration.redis_host.should == "my_host"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "can configure redis port with #redis_port" do
|
20
|
+
Ricordami.configure { |config| config.redis_port = 6379 }
|
21
|
+
Ricordami.configuration.redis_port.should == 6379
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can configure redis db (0, 1, 2, ..., 8) with #redis_db" do
|
25
|
+
Ricordami.configure { |config| config.redis_db = 1 }
|
26
|
+
Ricordami.configuration.redis_db.should == 1
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can configure redis to use as thread-safe with #thread_safe" do
|
30
|
+
Ricordami.configure { |config| config.thread_safe = true }
|
31
|
+
Ricordami.configuration.thread_safe.should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can configure all attributes at once using #from_hash" do
|
35
|
+
Ricordami.configure do |config|
|
36
|
+
config.from_hash(:redis_host => "serge",
|
37
|
+
:redis_port => 6380,
|
38
|
+
:redis_db => 2)
|
39
|
+
end
|
40
|
+
Ricordami.configuration.redis_host.should == "serge"
|
41
|
+
Ricordami.configuration.redis_port.should == 6380
|
42
|
+
Ricordami.configuration.redis_db.should == 2
|
43
|
+
Ricordami.configuration.thread_safe.should be_false
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "rspec/mocks"
|
3
|
+
|
4
|
+
describe Ricordami::Connection do
|
5
|
+
describe "the module" do
|
6
|
+
before(:all) do
|
7
|
+
module Mixer
|
8
|
+
extend self
|
9
|
+
include Ricordami::Connection
|
10
|
+
end
|
11
|
+
RSpec::Mocks::setup(self)
|
12
|
+
@config = mock(:config)
|
13
|
+
[:redis_host, :redis_port, :redis_db, :thread_safe].each do |name|
|
14
|
+
@config.stub!(name)
|
15
|
+
end
|
16
|
+
Mixer.stub!(:configuration).and_return(@config)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "has a connection to Redis" do
|
20
|
+
Mixer.driver.should be_a(Redis)
|
21
|
+
Mixer.driver.get("not_exist")
|
22
|
+
Mixer.driver.client.connection.should be_connected
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ricordami::Error do
|
4
|
+
it "has an error to notify when data was not found on the server" do
|
5
|
+
Ricordami::NotFound.new.should be_a_kind_of(Ricordami::Error)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "has an error to notify when an attribute is not supported" do
|
9
|
+
Ricordami::AttributeNotSupported.new.should be_a_kind_of(Ricordami::Error)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "has an error to notify an attempty to change a read-only attribute" do
|
13
|
+
Ricordami::ReadOnlyAttribute.new.should be_a_kind_of(Ricordami::Error)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has an error to notify when an index declaration is invalid" do
|
17
|
+
Ricordami::InvalidIndexDefinition.new.should be_a_kind_of(Ricordami::Error)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "has an error to notify when a model is deleted" do
|
21
|
+
Ricordami::ModelHasBeenDeleted.new.should be_a_kind_of(Ricordami::Error)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "has an error to notify when a type is not supported" do
|
25
|
+
Ricordami::TypeNotSupported.new.should be_a_kind_of(Ricordami::Error)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "has an error to notify when an index is missing" do
|
29
|
+
Ricordami::MissingIndex.new.should be_a_kind_of(Ricordami::Error)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "has an error to notify when an event is not supported" do
|
33
|
+
Ricordami::EventNotSupported.new.should be_a_kind_of(Ricordami::Error)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "has an error to notify when an option value is invalid" do
|
37
|
+
Ricordami::OptionValueInvalid.new.should be_a_kind_of(Ricordami::Error)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "has an error to notify when mandatory arguments are missing" do
|
41
|
+
Ricordami::MissingMandatoryArgs.new.should be_a_kind_of(Ricordami::Error)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Ricordami::HasAttributes do
|
4
|
+
describe "the class" do
|
5
|
+
uses_constants("Boat")
|
6
|
+
|
7
|
+
it "can declare attributes using #attribute" do
|
8
|
+
Boat.attribute :sail
|
9
|
+
Boat.attributes[:sail].should be_a(Ricordami::Attribute)
|
10
|
+
Boat.attributes[:sail].name.should == :sail
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can pass attribute options when declaring an attribute" do
|
14
|
+
Boat.attribute :color, :default => "black", :read_only => true
|
15
|
+
Boat.attributes[:color].default_value.should == "black"
|
16
|
+
Boat.attributes[:color].should be_read_only
|
17
|
+
end
|
18
|
+
|
19
|
+
it "creates an index if :indexed is set" do
|
20
|
+
Boat.attribute :size, :indexed => :value
|
21
|
+
Boat.indices.should have_key(:size)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "creates a unique index if :indexed is :unique" do
|
25
|
+
Boat.attribute :size, :indexed => :unique
|
26
|
+
Boat.indices[:size].should be_a(Ricordami::UniqueIndex)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "creates a value index if :indexed is :value" do
|
30
|
+
Boat.attribute :size, :indexed => :value
|
31
|
+
Boat.indices[:size].should be_a(Ricordami::ValueIndex)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "replaces :initial value with a generator if it's a symbol" do
|
35
|
+
Boat.attribute :id, :initial => :sequence
|
36
|
+
attribute = Boat.attributes[:id]
|
37
|
+
attribute.initial_value.should == "1"
|
38
|
+
attribute.initial_value.should == "2"
|
39
|
+
attribute.initial_value.should == "3"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe "an instance" do
|
44
|
+
uses_constants("User")
|
45
|
+
|
46
|
+
it "can be initialized with a hash of attribute values" do
|
47
|
+
User.attribute :name
|
48
|
+
User.attribute :age
|
49
|
+
User.attribute :ssn
|
50
|
+
user = User.new(:name => "jean", :age => "20", :ssn => "1234567890")
|
51
|
+
user.name.should == "jean"
|
52
|
+
user.age.should == "20"
|
53
|
+
user.ssn.should == "1234567890"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "can set attributes using writers" do
|
57
|
+
User.attribute :email
|
58
|
+
user = User.new
|
59
|
+
user.email.should be_nil
|
60
|
+
user.email = "blah@toto.com"
|
61
|
+
user.email.should == "blah@toto.com"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "sets default values when initializing without argument" do
|
65
|
+
User.attribute :age, :default => "18"
|
66
|
+
User.new.age.should == "18"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "overwrites default values with arguments when initializing" do
|
70
|
+
User.attribute :age, :default => "18"
|
71
|
+
User.new(:age => "12").age.should == "12"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can't change a read-only attribute that was initialized" do
|
75
|
+
User.attribute :name
|
76
|
+
User.attribute :ssn, :read_only => true
|
77
|
+
user = User.new(:name => "James Bond", :ssn => "007")
|
78
|
+
lambda {
|
79
|
+
user.ssn = "1234567890"
|
80
|
+
}.should raise_error(Ricordami::ReadOnlyAttribute)
|
81
|
+
lambda {
|
82
|
+
user.name = "Titi"
|
83
|
+
}.should_not raise_error(Ricordami::ReadOnlyAttribute)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "can set a read-only attribute only once if it was not initialized" do
|
87
|
+
User.attribute :name
|
88
|
+
User.attribute :ssn, :read_only => true
|
89
|
+
user = User.new
|
90
|
+
lambda {
|
91
|
+
user.ssn = "1234567890"
|
92
|
+
user.name = "Titi"
|
93
|
+
user.name = "Bob Loblaw"
|
94
|
+
}.should_not raise_error(Ricordami::ReadOnlyAttribute)
|
95
|
+
lambda {
|
96
|
+
user.ssn = "0987654321"
|
97
|
+
}.should raise_error(Ricordami::ReadOnlyAttribute)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "has an 'id' attribute set by default to a sequence if not overriden when saved" do
|
101
|
+
user = User.create
|
102
|
+
user.id.should be_present
|
103
|
+
user.id.should == "1"
|
104
|
+
end
|
105
|
+
|
106
|
+
it "overides the value of its 'id' attribute when a value is passed" do
|
107
|
+
user = User.create(:id => "foo")
|
108
|
+
user.id.should == "foo"
|
109
|
+
end
|
110
|
+
|
111
|
+
it "defines 'id' as a read_only attribute" do
|
112
|
+
user = User.create
|
113
|
+
lambda {
|
114
|
+
user.id = "try me"
|
115
|
+
}.should raise_error(Ricordami::ReadOnlyAttribute)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "returns the name of the attributes key with #attributes_key_name" do
|
119
|
+
user = User.new(:id => "ze_id")
|
120
|
+
user.instance_eval { attributes_key_name }.should == "User:att:ze_id"
|
121
|
+
end
|
122
|
+
|
123
|
+
it "updates the attribute values in memory with #update_mem_attributes!" do
|
124
|
+
User.attribute :age
|
125
|
+
User.attribute :sex
|
126
|
+
user = User.new(:age => "12", :sex => "male")
|
127
|
+
user.update_mem_attributes!(:age => "18", :sex => "female")
|
128
|
+
user.age.should == "18"
|
129
|
+
user.sex.should == "female"
|
130
|
+
end
|
131
|
+
|
132
|
+
it "can't change read-only attributes with #update_mem_attributes!" do
|
133
|
+
User.attribute :name
|
134
|
+
User.attribute :ssn, :read_only => true
|
135
|
+
user = User.new(:name => "James Bond", :ssn => "007")
|
136
|
+
lambda {
|
137
|
+
user.update_mem_attributes!(:ssn => "1234567890")
|
138
|
+
}.should raise_error(Ricordami::ReadOnlyAttribute)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "updates the attribute values in memory with #update_mem_attributes" do
|
142
|
+
User.attribute :age
|
143
|
+
user = User.new(:age => "12")
|
144
|
+
user.update_mem_attributes(:age => "18")
|
145
|
+
user.age.should == "18"
|
146
|
+
end
|
147
|
+
|
148
|
+
it "changes read-only attributes with #update_mem_attributes" do
|
149
|
+
User.attribute :ssn, :read_only => true
|
150
|
+
user = User.new(:ssn => "007")
|
151
|
+
lambda {
|
152
|
+
user.update_mem_attributes(:ssn => "1234567890")
|
153
|
+
}.should_not raise_error
|
154
|
+
end
|
155
|
+
|
156
|
+
it "can't change the attributes of a model that was deleted" do
|
157
|
+
User.attribute :age
|
158
|
+
user = User.create(:age => "42")
|
159
|
+
user.delete
|
160
|
+
lambda { user.age = "12" }.should raise_error(Ricordami::ModelHasBeenDeleted)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "keeps track of dirty attributes" do
|
165
|
+
uses_constants("Plane")
|
166
|
+
before(:each) do
|
167
|
+
Plane.attribute :brand
|
168
|
+
Plane.attribute :model
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "a new record" do
|
172
|
+
before(:each) { @plane = Plane.new }
|
173
|
+
let(:plane) { @plane }
|
174
|
+
|
175
|
+
it "was not changed if it doesn't have attributes" do
|
176
|
+
plane.should_not be_changed
|
177
|
+
plane.changed.should be_empty
|
178
|
+
plane.changes.should be_empty
|
179
|
+
end
|
180
|
+
|
181
|
+
it "was changed when initialized with attributes" do
|
182
|
+
plane = Plane.new(:brand => "Airbus", :model => "320")
|
183
|
+
plane.should be_changed
|
184
|
+
plane.changed.should =~ ["brand", "model"]
|
185
|
+
plane.changes.should == {"brand" => [nil, "Airbus"], "model" => [nil, "320"]}
|
186
|
+
end
|
187
|
+
|
188
|
+
it "knows when an attribute value changes" do
|
189
|
+
plane.brand = "Boeing"
|
190
|
+
plane.should be_changed
|
191
|
+
plane.changed.should == ["brand"]
|
192
|
+
plane.changes["brand"].should == [nil, "Boeing"]
|
193
|
+
end
|
194
|
+
|
195
|
+
it "knows when an attribute value actually didn't change" do
|
196
|
+
plane.brand = nil
|
197
|
+
plane.should_not be_changed
|
198
|
+
end
|
199
|
+
|
200
|
+
it "was not changed after it was saved" do
|
201
|
+
plane.model = "380"
|
202
|
+
plane.save
|
203
|
+
plane.should_not be_changed
|
204
|
+
end
|
205
|
+
|
206
|
+
it "knows when it was changed before being saved" do
|
207
|
+
plane.model = "380"
|
208
|
+
plane.save
|
209
|
+
plane.previous_changes["model"].should == [nil, "380"]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "a persisted record" do
|
214
|
+
before(:each) { Plane.create(:id => "A320", :brand => "Airbus", :model => "320") }
|
215
|
+
let(:plane) { Plane["A320"] }
|
216
|
+
|
217
|
+
it "was not changed when it's just loaded from the DB" do
|
218
|
+
plane.should_not be_changed
|
219
|
+
end
|
220
|
+
|
221
|
+
it "was not changed after reloading it with #reload" do
|
222
|
+
plane.brand = "Toys'R US"
|
223
|
+
plane.reload
|
224
|
+
plane.should_not be_changed
|
225
|
+
end
|
226
|
+
|
227
|
+
it "knows when an attribute value actually didn't change" do
|
228
|
+
plane.brand = "Airbus"
|
229
|
+
plane.should_not be_changed
|
230
|
+
plane.brand = "Boeing"
|
231
|
+
plane.brand = "Airbus"
|
232
|
+
plane.should_not be_changed
|
233
|
+
end
|
234
|
+
|
235
|
+
it "knows which attributes changed when running #update_attributes" do
|
236
|
+
plane.brand = "Boeing"
|
237
|
+
plane.update_attributes(:brand => "Airbus", :model => "380")
|
238
|
+
plane.should_not be_changed
|
239
|
+
plane.previous_changes.should == {"model" => ["320", "380"]}
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "attribute value typing" do
|
244
|
+
uses_constants("Person")
|
245
|
+
before(:each) do
|
246
|
+
Person.attribute :name
|
247
|
+
Person.attribute :age, :type => :integer
|
248
|
+
Person.attribute :parts, :type => :float
|
249
|
+
end
|
250
|
+
|
251
|
+
it "converts value types when initializing an object" do
|
252
|
+
person = Person.new(:name => 123, :age => "21", :parts => "5.2")
|
253
|
+
person.name.should == "123"
|
254
|
+
person.age.should == 21
|
255
|
+
person.parts.should == 5.2
|
256
|
+
end
|
257
|
+
|
258
|
+
it "doesn't convert the value type if value is nil" do
|
259
|
+
person = Person.new(:name => nil, :age => nil, :parts => nil)
|
260
|
+
person.name.should be_nil
|
261
|
+
person.age.should be_nil
|
262
|
+
person.parts.should be_nil
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|