toystore 0.13.0 → 0.13.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/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
|