toystore 0.13.0 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +12 -0
- data/Guardfile +2 -2
- data/README.md +26 -35
- data/examples/attributes_abbreviation.rb +2 -2
- data/examples/attributes_virtual.rb +2 -2
- data/examples/identity_map.rb +2 -2
- data/examples/memcached.rb +2 -2
- data/examples/memory.rb +2 -2
- data/examples/mongo.rb +2 -2
- data/examples/namespacing_keys.rb +1 -1
- data/examples/plain_old_object.rb +2 -2
- data/examples/plain_old_object_on_roids.rb +13 -22
- data/examples/redis.rb +2 -2
- data/examples/riak.rb +2 -2
- data/lib/toy.rb +4 -0
- data/lib/toy/attributes.rb +10 -21
- data/lib/toy/cloneable.rb +1 -3
- data/lib/toy/dirty.rb +0 -6
- data/lib/toy/equality.rb +3 -14
- data/lib/toy/extensions/symbol.rb +17 -0
- data/lib/toy/extensions/uuid.rb +6 -2
- data/lib/toy/identity.rb +39 -2
- data/lib/toy/inspect.rb +3 -4
- data/lib/toy/object.rb +1 -5
- data/lib/toy/persistence.rb +34 -5
- data/lib/toy/querying.rb +9 -9
- data/lib/toy/reloadable.rb +3 -3
- data/lib/toy/store.rb +1 -0
- data/lib/toy/types/json.rb +20 -0
- data/lib/toy/version.rb +1 -1
- data/spec/helper.rb +1 -0
- data/spec/toy/attributes_spec.rb +21 -59
- data/spec/toy/cloneable_spec.rb +1 -8
- data/spec/toy/equality_spec.rb +18 -19
- data/spec/toy/extensions/symbol_spec.rb +26 -0
- data/spec/toy/extensions/uuid_spec.rb +31 -11
- data/spec/toy/identity/uuid_key_factory_spec.rb +4 -4
- data/spec/toy/identity_spec.rb +111 -5
- data/spec/toy/inheritance_spec.rb +4 -4
- data/spec/toy/inspect_spec.rb +3 -3
- data/spec/toy/object_spec.rb +0 -40
- data/spec/toy/persistence_spec.rb +99 -1
- data/spec/toy/reloadable_spec.rb +9 -4
- data/spec/toy/serialization_spec.rb +16 -21
- data/spec/toy/types/json_spec.rb +37 -0
- data/spec/toy/validations_spec.rb +13 -0
- metadata +10 -4
data/lib/toy/cloneable.rb
CHANGED
data/lib/toy/dirty.rb
CHANGED
@@ -5,12 +5,6 @@ module Toy
|
|
5
5
|
include Attributes
|
6
6
|
include Cloneable
|
7
7
|
|
8
|
-
def initialize(*)
|
9
|
-
super
|
10
|
-
# never register initial id assignment as a change
|
11
|
-
@changed_attributes.delete('id') if @changed_attributes
|
12
|
-
end
|
13
|
-
|
14
8
|
def initialize_copy(*)
|
15
9
|
super.tap do
|
16
10
|
@previously_changed = {}
|
data/lib/toy/equality.rb
CHANGED
@@ -3,23 +3,12 @@ module Toy
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
def eql?(other)
|
6
|
-
|
7
|
-
return true if other.respond_to?(:target) &&
|
8
|
-
self.class.eql?(other.target.class) &&
|
9
|
-
id == other.target.id
|
10
|
-
false
|
11
|
-
end
|
12
|
-
alias :== :eql?
|
13
|
-
|
14
|
-
def equal?(other)
|
15
|
-
if other.respond_to?(:proxy_respond_to?) && other.respond_to?(:target)
|
16
|
-
other = other.target
|
17
|
-
end
|
18
|
-
super other
|
6
|
+
self.class.eql?(other.class) && attributes == other.attributes
|
19
7
|
end
|
8
|
+
alias_method :==, :eql?
|
20
9
|
|
21
10
|
def hash
|
22
|
-
|
11
|
+
attributes.hash
|
23
12
|
end
|
24
13
|
end
|
25
14
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Toy
|
2
|
+
module Extensions
|
3
|
+
module Symbol
|
4
|
+
def to_store(value, *)
|
5
|
+
value.nil? ? nil : value.to_s
|
6
|
+
end
|
7
|
+
|
8
|
+
def from_store(value, *)
|
9
|
+
value.nil? ? nil : value.to_sym
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Symbol
|
16
|
+
extend Toy::Extensions::Symbol
|
17
|
+
end
|
data/lib/toy/extensions/uuid.rb
CHANGED
@@ -2,11 +2,15 @@ module Toy
|
|
2
2
|
module Extensions
|
3
3
|
module UUID
|
4
4
|
def to_store(value, *)
|
5
|
-
|
5
|
+
return nil if value.nil?
|
6
|
+
return value if value.is_a?(self)
|
7
|
+
new(value)
|
6
8
|
end
|
7
9
|
|
8
10
|
def from_store(value, *)
|
9
|
-
|
11
|
+
return nil if value.nil?
|
12
|
+
return value if value.is_a?(self)
|
13
|
+
new(value)
|
10
14
|
end
|
11
15
|
end
|
12
16
|
end
|
data/lib/toy/identity.rb
CHANGED
@@ -7,7 +7,7 @@ module Toy
|
|
7
7
|
end
|
8
8
|
|
9
9
|
module ClassMethods
|
10
|
-
def key(name_or_factory = :uuid)
|
10
|
+
def key(name_or_factory = :uuid, options = {})
|
11
11
|
@key_factory = if name_or_factory == :uuid
|
12
12
|
UUIDKeyFactory.new
|
13
13
|
elsif name_or_factory == :native_uuid
|
@@ -20,7 +20,7 @@ module Toy
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
attribute :id, @key_factory.key_type
|
23
|
+
attribute :id, @key_factory.key_type, :virtual => true
|
24
24
|
@key_factory
|
25
25
|
end
|
26
26
|
|
@@ -42,5 +42,42 @@ module Toy
|
|
42
42
|
def key_factory
|
43
43
|
self.class.key_factory
|
44
44
|
end
|
45
|
+
|
46
|
+
def initialize(*args)
|
47
|
+
super
|
48
|
+
write_attribute :id, self.class.next_key(self) unless id?
|
49
|
+
|
50
|
+
# never register initial id assignment as a change
|
51
|
+
@changed_attributes.delete('id') if @changed_attributes
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize_copy(*args)
|
55
|
+
super
|
56
|
+
write_attribute :id, self.class.next_key(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def eql?(other)
|
60
|
+
return true if self.class.eql?(other.class) &&
|
61
|
+
id == other.id
|
62
|
+
|
63
|
+
return true if other.respond_to?(:target) &&
|
64
|
+
self.class.eql?(other.target.class) &&
|
65
|
+
id == other.target.id
|
66
|
+
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
alias_method :==, :eql?
|
71
|
+
|
72
|
+
def equal?(other)
|
73
|
+
if other.respond_to?(:proxy_respond_to?) && other.respond_to?(:target)
|
74
|
+
other = other.target
|
75
|
+
end
|
76
|
+
super other
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_key
|
80
|
+
key_factory.to_key(self)
|
81
|
+
end
|
45
82
|
end
|
46
83
|
end
|
data/lib/toy/inspect.rb
CHANGED
@@ -4,21 +4,20 @@ module Toy
|
|
4
4
|
|
5
5
|
module ClassMethods
|
6
6
|
def inspect
|
7
|
-
keys = attributes.keys
|
7
|
+
keys = attributes.keys
|
8
8
|
nice_string = keys.sort.map do |name|
|
9
9
|
type = attributes[name].type
|
10
10
|
"#{name}:#{type}"
|
11
11
|
end.join(" ")
|
12
|
-
"#{name}(
|
12
|
+
"#{name}(#{nice_string})"
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
def inspect
|
17
|
-
keys = self.class.attributes.keys
|
17
|
+
keys = self.class.attributes.keys
|
18
18
|
attributes_as_nice_string = keys.map(&:to_s).sort.map do |name|
|
19
19
|
"#{name}: #{read_attribute(name).inspect}"
|
20
20
|
end
|
21
|
-
attributes_as_nice_string.unshift("id: #{read_attribute(:id).inspect}")
|
22
21
|
"#<#{self.class}:#{object_id} #{attributes_as_nice_string.join(', ')}>"
|
23
22
|
end
|
24
23
|
end
|
data/lib/toy/object.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Toy
|
2
2
|
module Object
|
3
|
-
extend
|
3
|
+
extend ActiveSupport::Concern
|
4
4
|
extend ActiveModel::Naming
|
5
5
|
include ActiveModel::Conversion
|
6
6
|
include ActiveModel::Validations
|
@@ -17,9 +17,5 @@ module Toy
|
|
17
17
|
def persisted?
|
18
18
|
false
|
19
19
|
end
|
20
|
-
|
21
|
-
def to_key
|
22
|
-
key_factory.to_key(self)
|
23
|
-
end
|
24
20
|
end
|
25
21
|
end
|
data/lib/toy/persistence.rb
CHANGED
@@ -30,6 +30,15 @@ module Toy
|
|
30
30
|
def destroy(*ids)
|
31
31
|
ids.each { |id| get(id).try(:destroy) }
|
32
32
|
end
|
33
|
+
|
34
|
+
def persisted_attributes
|
35
|
+
@persisted_attributes ||= attributes.values.select(&:persisted?)
|
36
|
+
end
|
37
|
+
|
38
|
+
def attribute(*args)
|
39
|
+
@persisted_attributes = nil
|
40
|
+
super
|
41
|
+
end
|
33
42
|
end
|
34
43
|
|
35
44
|
def adapter
|
@@ -81,7 +90,31 @@ module Toy
|
|
81
90
|
|
82
91
|
def delete
|
83
92
|
@_destroyed = true
|
84
|
-
adapter.delete(
|
93
|
+
adapter.delete(persisted_id)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Public: Choke point for overriding what id is used to write and delete.
|
97
|
+
def persisted_id
|
98
|
+
attribute_name = 'id'
|
99
|
+
attribute = attribute_instance(attribute_name)
|
100
|
+
attribute_value = read_attribute(attribute_name)
|
101
|
+
attribute.to_store(attribute_value)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Public: Choke point for overriding what attributes get stored.
|
105
|
+
def persisted_attributes
|
106
|
+
attributes = {}
|
107
|
+
self.class.persisted_attributes.each do |attribute|
|
108
|
+
if (value = attribute.to_store(read_attribute(attribute.name)))
|
109
|
+
attributes[attribute.persisted_name] = value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
attributes
|
113
|
+
end
|
114
|
+
|
115
|
+
# Public: Choke point for overriding how data gets written.
|
116
|
+
def persist
|
117
|
+
adapter.write(persisted_id, persisted_attributes)
|
85
118
|
end
|
86
119
|
|
87
120
|
private
|
@@ -96,9 +129,5 @@ module Toy
|
|
96
129
|
persist
|
97
130
|
true
|
98
131
|
end
|
99
|
-
|
100
|
-
def persist
|
101
|
-
adapter.write(id, persisted_attributes)
|
102
|
-
end
|
103
132
|
end
|
104
133
|
end
|
data/lib/toy/querying.rb
CHANGED
@@ -3,23 +3,23 @@ module Toy
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
module ClassMethods
|
6
|
-
def
|
6
|
+
def read(id, options = nil)
|
7
7
|
if (attrs = adapter.read(id, options))
|
8
8
|
load(id, attrs)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
alias_method :
|
13
|
-
alias_method :find, :
|
12
|
+
alias_method :get, :read
|
13
|
+
alias_method :find, :read
|
14
14
|
|
15
|
-
def
|
15
|
+
def read!(id, options = nil)
|
16
16
|
get(id, options) || raise(Toy::NotFound.new(id))
|
17
17
|
end
|
18
18
|
|
19
|
-
alias_method :
|
20
|
-
alias_method :find!, :
|
19
|
+
alias_method :get!, :read!
|
20
|
+
alias_method :find!, :read!
|
21
21
|
|
22
|
-
def
|
22
|
+
def read_multiple(ids, options = nil)
|
23
23
|
result = adapter.read_multiple(ids, options)
|
24
24
|
result.each do |id, attrs|
|
25
25
|
result[id] = attrs.nil? ? nil : load(id, attrs)
|
@@ -27,8 +27,8 @@ module Toy
|
|
27
27
|
result
|
28
28
|
end
|
29
29
|
|
30
|
-
alias_method :
|
31
|
-
alias_method :find_multiple, :
|
30
|
+
alias_method :get_multiple, :read_multiple
|
31
|
+
alias_method :find_multiple, :read_multiple
|
32
32
|
|
33
33
|
def get_or_new(id)
|
34
34
|
get(id) || new(:id => id)
|
data/lib/toy/reloadable.rb
CHANGED
@@ -3,15 +3,15 @@ module Toy
|
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
5
|
def reload
|
6
|
-
if attrs = adapter.read(
|
7
|
-
attrs['id'] =
|
6
|
+
if attrs = adapter.read(persisted_id)
|
7
|
+
attrs['id'] = persisted_id
|
8
8
|
instance_variables.each { |ivar| instance_variable_set(ivar, nil) }
|
9
9
|
initialize_attributes
|
10
10
|
send(:attributes=, attrs, new_record?)
|
11
11
|
self.class.lists.each_key { |name| send(name).reset }
|
12
12
|
self.class.references.each_key { |name| send("reset_#{name}") }
|
13
13
|
else
|
14
|
-
raise NotFound.new(
|
14
|
+
raise NotFound.new(persisted_id)
|
15
15
|
end
|
16
16
|
self
|
17
17
|
end
|
data/lib/toy/store.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Toy
|
2
|
+
module Types
|
3
|
+
module JSON
|
4
|
+
def self.to_store(value, *)
|
5
|
+
return value if value.nil?
|
6
|
+
ActiveSupport::JSON.encode(value)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_store(value, *)
|
10
|
+
return value if value.nil?
|
11
|
+
|
12
|
+
if value.is_a?(String)
|
13
|
+
ActiveSupport::JSON.decode(value)
|
14
|
+
else
|
15
|
+
value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/toy/version.rb
CHANGED
data/spec/helper.rb
CHANGED
data/spec/toy/attributes_spec.rb
CHANGED
@@ -3,51 +3,9 @@ require 'helper'
|
|
3
3
|
describe Toy::Attributes do
|
4
4
|
uses_objects('User', 'Game')
|
5
5
|
|
6
|
-
describe "including" do
|
7
|
-
it "adds id attribute" do
|
8
|
-
User.attributes.keys.should == ['id']
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
6
|
describe ".attributes" do
|
13
|
-
it "defaults to hash
|
14
|
-
User.attributes.
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
describe "#persisted_attributes" do
|
19
|
-
before do
|
20
|
-
@over = Game.attribute(:over, Boolean)
|
21
|
-
@score = Game.attribute(:creator_score, Integer, :virtual => true)
|
22
|
-
@abbr = Game.attribute(:super_secret_hash, String, :abbr => :ssh)
|
23
|
-
@rewards = Game.attribute(:rewards, Set)
|
24
|
-
@game = Game.new({
|
25
|
-
:over => true,
|
26
|
-
:creator_score => 20,
|
27
|
-
:rewards => %w(twigs berries).to_set,
|
28
|
-
:ssh => 'h4x',
|
29
|
-
})
|
30
|
-
end
|
31
|
-
|
32
|
-
it "includes persisted attributes" do
|
33
|
-
@game.persisted_attributes.should have_key('over')
|
34
|
-
end
|
35
|
-
|
36
|
-
it "includes abbreviated names for abbreviated attributes" do
|
37
|
-
@game.persisted_attributes.should have_key('ssh')
|
38
|
-
end
|
39
|
-
|
40
|
-
it "does not include full names for abbreviated attributes" do
|
41
|
-
@game.persisted_attributes.should_not have_key('super_secret_hash')
|
42
|
-
end
|
43
|
-
|
44
|
-
it "does not include virtual attributes" do
|
45
|
-
@game.persisted_attributes.should_not have_key(:creator_score)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "includes to_store values for attributes" do
|
49
|
-
@game.persisted_attributes['rewards'].should be_instance_of(Array)
|
50
|
-
@game.persisted_attributes['rewards'].should == @rewards.to_store(@game.rewards)
|
7
|
+
it "defaults to empty hash" do
|
8
|
+
User.attributes.should eq({})
|
51
9
|
end
|
52
10
|
end
|
53
11
|
|
@@ -64,6 +22,22 @@ describe Toy::Attributes do
|
|
64
22
|
it "excludes attributes without a default" do
|
65
23
|
User.defaulted_attributes.should_not include(@name)
|
66
24
|
end
|
25
|
+
|
26
|
+
it "memoizes after first call" do
|
27
|
+
User.should_receive(:attributes).once.and_return({
|
28
|
+
'name' => @name,
|
29
|
+
'age' => @age,
|
30
|
+
})
|
31
|
+
User.defaulted_attributes
|
32
|
+
User.defaulted_attributes
|
33
|
+
User.defaulted_attributes
|
34
|
+
end
|
35
|
+
|
36
|
+
it "is unmemoized when declaring a new attribute" do
|
37
|
+
User.defaulted_attributes
|
38
|
+
age = User.attribute :location, String, :default => 'IN'
|
39
|
+
User.defaulted_attributes.map(&:name).sort.should eq(%w[age location])
|
40
|
+
end
|
67
41
|
end
|
68
42
|
|
69
43
|
describe ".attribute?" do
|
@@ -90,17 +64,6 @@ describe Toy::Attributes do
|
|
90
64
|
User.attribute :age, Integer
|
91
65
|
end
|
92
66
|
|
93
|
-
it "writes id" do
|
94
|
-
id = User.new.id
|
95
|
-
id.should_not be_nil
|
96
|
-
id.size.should == 36
|
97
|
-
end
|
98
|
-
|
99
|
-
it "does not attempt to set id if already set" do
|
100
|
-
user = User.new(:id => 'frank')
|
101
|
-
user.id.should == 'frank'
|
102
|
-
end
|
103
|
-
|
104
67
|
it "sets attributes" do
|
105
68
|
instance = User.new(:name => 'John', :age => 28)
|
106
69
|
instance.name.should == 'John'
|
@@ -118,9 +81,9 @@ describe Toy::Attributes do
|
|
118
81
|
end
|
119
82
|
|
120
83
|
describe "#attributes" do
|
121
|
-
it "defaults to hash
|
122
|
-
attrs =
|
123
|
-
attrs.
|
84
|
+
it "defaults to empty hash" do
|
85
|
+
attrs = ToyObject().new.attributes
|
86
|
+
attrs.should eq({})
|
124
87
|
end
|
125
88
|
|
126
89
|
it "includes all attributes that are not nil" do
|
@@ -128,7 +91,6 @@ describe Toy::Attributes do
|
|
128
91
|
User.attribute(:active, Boolean, :default => true)
|
129
92
|
user = User.new
|
130
93
|
user.attributes.should == {
|
131
|
-
'id' => user.id,
|
132
94
|
'active' => true,
|
133
95
|
}
|
134
96
|
end
|