toystore 0.5
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/.autotest +11 -0
- data/.bundle/config +2 -0
- data/.gitignore +6 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +9 -0
- data/LOGGING.rdoc +16 -0
- data/README.rdoc +13 -0
- data/Rakefile +7 -0
- data/examples/memcached.rb +20 -0
- data/examples/memory.rb +20 -0
- data/examples/models.rb +51 -0
- data/examples/redis.rb +20 -0
- data/lib/toy.rb +81 -0
- data/lib/toy/attribute.rb +73 -0
- data/lib/toy/attributes.rb +137 -0
- data/lib/toy/caching.rb +20 -0
- data/lib/toy/callbacks.rb +48 -0
- data/lib/toy/collection.rb +55 -0
- data/lib/toy/connection.rb +28 -0
- data/lib/toy/dirty.rb +47 -0
- data/lib/toy/dolly.rb +30 -0
- data/lib/toy/embedded_list.rb +45 -0
- data/lib/toy/embedded_lists.rb +68 -0
- data/lib/toy/equality.rb +19 -0
- data/lib/toy/exceptions.rb +29 -0
- data/lib/toy/extensions/array.rb +22 -0
- data/lib/toy/extensions/boolean.rb +43 -0
- data/lib/toy/extensions/date.rb +24 -0
- data/lib/toy/extensions/float.rb +13 -0
- data/lib/toy/extensions/hash.rb +17 -0
- data/lib/toy/extensions/integer.rb +22 -0
- data/lib/toy/extensions/nil_class.rb +17 -0
- data/lib/toy/extensions/object.rb +26 -0
- data/lib/toy/extensions/set.rb +23 -0
- data/lib/toy/extensions/string.rb +17 -0
- data/lib/toy/extensions/time.rb +29 -0
- data/lib/toy/identity.rb +26 -0
- data/lib/toy/identity/abstract_key_factory.rb +10 -0
- data/lib/toy/identity/uuid_key_factory.rb +9 -0
- data/lib/toy/identity_map.rb +109 -0
- data/lib/toy/index.rb +74 -0
- data/lib/toy/indices.rb +56 -0
- data/lib/toy/inspect.rb +12 -0
- data/lib/toy/list.rb +46 -0
- data/lib/toy/lists.rb +37 -0
- data/lib/toy/logger.rb +26 -0
- data/lib/toy/mass_assignment_security.rb +16 -0
- data/lib/toy/persistence.rb +138 -0
- data/lib/toy/plugins.rb +23 -0
- data/lib/toy/proxies/embedded_list.rb +74 -0
- data/lib/toy/proxies/list.rb +97 -0
- data/lib/toy/proxies/proxy.rb +59 -0
- data/lib/toy/querying.rb +57 -0
- data/lib/toy/reference.rb +134 -0
- data/lib/toy/references.rb +19 -0
- data/lib/toy/serialization.rb +81 -0
- data/lib/toy/store.rb +36 -0
- data/lib/toy/timestamps.rb +22 -0
- data/lib/toy/validations.rb +45 -0
- data/lib/toy/version.rb +3 -0
- data/lib/toystore.rb +1 -0
- data/spec/helper.rb +35 -0
- data/spec/spec.opts +3 -0
- data/spec/support/constants.rb +41 -0
- data/spec/support/identity_map_matcher.rb +20 -0
- data/spec/support/name_and_number_key_factory.rb +5 -0
- data/spec/toy/attribute_spec.rb +176 -0
- data/spec/toy/attributes_spec.rb +394 -0
- data/spec/toy/caching_spec.rb +62 -0
- data/spec/toy/callbacks_spec.rb +97 -0
- data/spec/toy/connection_spec.rb +47 -0
- data/spec/toy/dirty_spec.rb +99 -0
- data/spec/toy/dolly_spec.rb +76 -0
- data/spec/toy/embedded_list_spec.rb +607 -0
- data/spec/toy/embedded_lists_spec.rb +172 -0
- data/spec/toy/equality_spec.rb +46 -0
- data/spec/toy/exceptions_spec.rb +18 -0
- data/spec/toy/extensions/array_spec.rb +25 -0
- data/spec/toy/extensions/boolean_spec.rb +41 -0
- data/spec/toy/extensions/date_spec.rb +48 -0
- data/spec/toy/extensions/float_spec.rb +14 -0
- data/spec/toy/extensions/hash_spec.rb +21 -0
- data/spec/toy/extensions/integer_spec.rb +29 -0
- data/spec/toy/extensions/nil_class_spec.rb +14 -0
- data/spec/toy/extensions/set_spec.rb +27 -0
- data/spec/toy/extensions/string_spec.rb +28 -0
- data/spec/toy/extensions/time_spec.rb +94 -0
- data/spec/toy/identity/abstract_key_factory_spec.rb +7 -0
- data/spec/toy/identity/uuid_key_factory_spec.rb +7 -0
- data/spec/toy/identity_map_spec.rb +150 -0
- data/spec/toy/identity_spec.rb +52 -0
- data/spec/toy/index_spec.rb +230 -0
- data/spec/toy/indices_spec.rb +141 -0
- data/spec/toy/inspect_spec.rb +15 -0
- data/spec/toy/list_spec.rb +576 -0
- data/spec/toy/lists_spec.rb +95 -0
- data/spec/toy/logger_spec.rb +33 -0
- data/spec/toy/mass_assignment_security_spec.rb +116 -0
- data/spec/toy/persistence_spec.rb +312 -0
- data/spec/toy/plugins_spec.rb +39 -0
- data/spec/toy/querying_spec.rb +162 -0
- data/spec/toy/reference_spec.rb +400 -0
- data/spec/toy/references_spec.rb +86 -0
- data/spec/toy/serialization_spec.rb +354 -0
- data/spec/toy/store_spec.rb +41 -0
- data/spec/toy/timestamps_spec.rb +63 -0
- data/spec/toy/validations_spec.rb +171 -0
- data/spec/toy_spec.rb +26 -0
- data/specs.watchr +52 -0
- data/test/lint_test.rb +40 -0
- data/toystore.gemspec +24 -0
- metadata +290 -0
data/lib/toy/inspect.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Toy
|
2
|
+
module Inspect
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def inspect
|
6
|
+
attributes_as_nice_string = self.class.attributes.keys.map(&:to_s).sort.map do |name|
|
7
|
+
"#{name}: #{read_attribute(name).inspect}"
|
8
|
+
end.join(", ")
|
9
|
+
"#<#{self.class}:#{object_id} #{attributes_as_nice_string}>"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/toy/list.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'toy/proxies/list'
|
2
|
+
|
3
|
+
module Toy
|
4
|
+
class List
|
5
|
+
include Toy::Collection
|
6
|
+
|
7
|
+
def after_initialize
|
8
|
+
model.attribute(key, Array)
|
9
|
+
create_accessors
|
10
|
+
end
|
11
|
+
|
12
|
+
def key
|
13
|
+
@key ||= :"#{name.to_s.singularize}_ids"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def proxy_class
|
18
|
+
Toy::Proxies::List
|
19
|
+
end
|
20
|
+
|
21
|
+
def list_method
|
22
|
+
:lists
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_accessors
|
26
|
+
model.class_eval """
|
27
|
+
def #{name}
|
28
|
+
#{instance_variable} ||= self.class.#{list_method}[:#{name}].new_proxy(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def #{name}=(records)
|
32
|
+
#{name}.replace(records)
|
33
|
+
end
|
34
|
+
"""
|
35
|
+
|
36
|
+
if options[:dependent]
|
37
|
+
model.class_eval """
|
38
|
+
after_destroy :destroy_#{name}
|
39
|
+
def destroy_#{name}
|
40
|
+
#{name}.each { |o| o.destroy }
|
41
|
+
end
|
42
|
+
"""
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/toy/lists.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Toy
|
2
|
+
module Lists
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def lists
|
7
|
+
@lists ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def list?(key)
|
11
|
+
lists.keys.include?(key.to_sym)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @examples
|
15
|
+
# list :games # assumes Game
|
16
|
+
# list :games, :dependent => true # assumes Game
|
17
|
+
# list :active_games, Game # uses Game
|
18
|
+
# list :active_games, Game, :dependent => true # uses Game
|
19
|
+
#
|
20
|
+
# list :games do
|
21
|
+
# def active
|
22
|
+
# target.select { |t| t.active? }
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# module ActiveExtension
|
27
|
+
# def active
|
28
|
+
# target.select { |t| t.active? }
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
# list :games, :extensions => [ActiveExtension]
|
32
|
+
def list(name, *args, &block)
|
33
|
+
List.new(self, name, *args, &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/toy/logger.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Toy
|
2
|
+
module Logger
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def logger
|
7
|
+
Toy.logger
|
8
|
+
end
|
9
|
+
|
10
|
+
def log_operation(operation, adapter, key, value)
|
11
|
+
logger.debug("ToyStore #{operation} :#{adapter.name} #{key.inspect}")
|
12
|
+
logger.debug(" #{value.inspect}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module InstanceMethods
|
17
|
+
def logger
|
18
|
+
Toy.logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def log_operation(*args)
|
22
|
+
self.class.log_operation(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Toy
|
2
|
+
module MassAssignmentSecurity
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveModel::MassAssignmentSecurity
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def initialize(attrs={})
|
8
|
+
super(sanitize_for_mass_assignment(attrs || {}))
|
9
|
+
end
|
10
|
+
|
11
|
+
def update_attributes(attrs={})
|
12
|
+
super(sanitize_for_mass_assignment(attrs || {}))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Toy
|
2
|
+
module Persistence
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def store(name=nil, client=nil, options=nil)
|
7
|
+
assert_client(name, client)
|
8
|
+
@store = Adapter[name].new(client, options) if !name.nil? && !client.nil?
|
9
|
+
assert_store(name, client, 'store')
|
10
|
+
@store
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_store?
|
14
|
+
!@store.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def cache(name=nil, client=nil)
|
18
|
+
assert_client(name, client)
|
19
|
+
@cache = Adapter[name].new(client) if !name.nil? && !client.nil?
|
20
|
+
assert_store(name, client, 'cache')
|
21
|
+
@cache
|
22
|
+
end
|
23
|
+
|
24
|
+
def has_cache?
|
25
|
+
!@cache.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def namespace(new_namespace=nil)
|
29
|
+
if new_namespace.nil?
|
30
|
+
@namespace ||= self.name
|
31
|
+
else
|
32
|
+
@namespace = new_namespace
|
33
|
+
end
|
34
|
+
|
35
|
+
@namespace
|
36
|
+
end
|
37
|
+
|
38
|
+
def store_key(id)
|
39
|
+
[namespace, id].join(':')
|
40
|
+
end
|
41
|
+
|
42
|
+
def create(attrs={})
|
43
|
+
new(attrs).tap { |doc| doc.save }
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete(*ids)
|
47
|
+
ids.each { |id| get(id).try(:delete) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def destroy(*ids)
|
51
|
+
ids.each { |id| get(id).try(:destroy) }
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def assert_client(name, client)
|
56
|
+
raise(ArgumentError, 'Client is required') if !name.nil? && client.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def assert_store(name, client, which)
|
60
|
+
raise(StandardError, "No #{which} has been set") if name.nil? && client.nil? && !send(:"has_#{which}?")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
module InstanceMethods
|
65
|
+
def store
|
66
|
+
self.class.store
|
67
|
+
end
|
68
|
+
|
69
|
+
def cache
|
70
|
+
self.class.cache
|
71
|
+
end
|
72
|
+
|
73
|
+
def store_key
|
74
|
+
self.class.store_key(id)
|
75
|
+
end
|
76
|
+
|
77
|
+
def new_record?
|
78
|
+
@_new_record == true
|
79
|
+
end
|
80
|
+
|
81
|
+
def destroyed?
|
82
|
+
@_destroyed == true
|
83
|
+
end
|
84
|
+
|
85
|
+
def persisted?
|
86
|
+
!new_record? && !destroyed?
|
87
|
+
end
|
88
|
+
|
89
|
+
def save(*)
|
90
|
+
new_record? ? create : update
|
91
|
+
end
|
92
|
+
|
93
|
+
def update_attributes(attrs)
|
94
|
+
self.attributes = attrs
|
95
|
+
save
|
96
|
+
end
|
97
|
+
|
98
|
+
def destroy
|
99
|
+
delete
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete
|
103
|
+
key = store_key
|
104
|
+
@_destroyed = true
|
105
|
+
logger.debug("ToyStore DEL #{key.inspect}")
|
106
|
+
store.delete(key)
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def create
|
111
|
+
persist!
|
112
|
+
end
|
113
|
+
|
114
|
+
def update
|
115
|
+
persist!
|
116
|
+
end
|
117
|
+
|
118
|
+
def persist
|
119
|
+
@_new_record = false
|
120
|
+
end
|
121
|
+
|
122
|
+
def persist!
|
123
|
+
key, attrs = store_key, persisted_attributes
|
124
|
+
|
125
|
+
if self.class.has_cache?
|
126
|
+
cache.write(key, attrs)
|
127
|
+
log_operation('WTS', cache, key, attrs)
|
128
|
+
end
|
129
|
+
|
130
|
+
store.write(key, attrs)
|
131
|
+
log_operation('SET', store, key, attrs)
|
132
|
+
persist
|
133
|
+
each_embedded_object(&:persist)
|
134
|
+
true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/toy/plugins.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Toy
|
2
|
+
def models
|
3
|
+
@models ||= Set.new
|
4
|
+
end
|
5
|
+
|
6
|
+
def plugins
|
7
|
+
@plugins ||= Set.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def plugin(mod)
|
11
|
+
Toy.models.each { |model| model.send(:include, mod) }
|
12
|
+
plugins << mod
|
13
|
+
end
|
14
|
+
|
15
|
+
module Plugins
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
|
18
|
+
included do
|
19
|
+
Toy.models << self
|
20
|
+
Toy.plugins.each { |mod| include mod }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'toy/proxies/proxy'
|
2
|
+
|
3
|
+
module Toy
|
4
|
+
module Proxies
|
5
|
+
class EmbeddedList < Toy::Proxies::Proxy
|
6
|
+
def get(id)
|
7
|
+
target.detect { |record| record.id == id }
|
8
|
+
end
|
9
|
+
|
10
|
+
def get!(id)
|
11
|
+
get(id) || raise(Toy::NotFound.new(id))
|
12
|
+
end
|
13
|
+
|
14
|
+
def include?(record)
|
15
|
+
return false if record.nil?
|
16
|
+
target.include?(record)
|
17
|
+
end
|
18
|
+
|
19
|
+
def push(instance)
|
20
|
+
assert_type(instance)
|
21
|
+
assign_reference(instance)
|
22
|
+
self.target.push(instance)
|
23
|
+
end
|
24
|
+
alias :<< :push
|
25
|
+
|
26
|
+
def concat(*instances)
|
27
|
+
instances = instances.flatten
|
28
|
+
instances.each do |instance|
|
29
|
+
assert_type(instance)
|
30
|
+
assign_reference(instance)
|
31
|
+
end
|
32
|
+
self.target.concat instances
|
33
|
+
end
|
34
|
+
|
35
|
+
def replace(instances)
|
36
|
+
@target = instances.map do |instance|
|
37
|
+
instance = instance.is_a?(proxy_class) ? instance : proxy_class.load(instance)
|
38
|
+
assign_reference(instance)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def create(attrs={})
|
43
|
+
proxy_class.new(attrs).tap do |instance|
|
44
|
+
assign_reference(instance)
|
45
|
+
if instance.valid?
|
46
|
+
push(instance)
|
47
|
+
proxy_owner.save
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroy(*args, &block)
|
53
|
+
ids = block_given? ? target.select(&block).map(&:id) : args.flatten
|
54
|
+
target.delete_if { |instance| ids.include?(instance.id) }
|
55
|
+
proxy_owner.save
|
56
|
+
end
|
57
|
+
|
58
|
+
def destroy_all
|
59
|
+
target.clear
|
60
|
+
proxy_owner.save
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def find_target
|
65
|
+
[]
|
66
|
+
end
|
67
|
+
|
68
|
+
def assign_reference(instance)
|
69
|
+
instance.parent_reference = proxy_owner
|
70
|
+
instance
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'toy/proxies/proxy'
|
2
|
+
|
3
|
+
module Toy
|
4
|
+
module Proxies
|
5
|
+
class List < Toy::Proxies::Proxy
|
6
|
+
def get(id)
|
7
|
+
if target_ids.include?(id)
|
8
|
+
proxy_class.get(id)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def get!(id)
|
13
|
+
get(id) || raise(Toy::NotFound.new(id))
|
14
|
+
end
|
15
|
+
|
16
|
+
def include?(record)
|
17
|
+
return false if record.nil?
|
18
|
+
target_ids.include?(record.id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def push(record)
|
22
|
+
assert_type(record)
|
23
|
+
self.target_ids = target_ids + [record]
|
24
|
+
end
|
25
|
+
alias :<< :push
|
26
|
+
|
27
|
+
def concat(*records)
|
28
|
+
records = records.flatten
|
29
|
+
records.map { |record| assert_type(record) }
|
30
|
+
self.target_ids = target_ids + records
|
31
|
+
end
|
32
|
+
|
33
|
+
def replace(records)
|
34
|
+
reset
|
35
|
+
self.target_ids = records
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(attrs={})
|
39
|
+
if proxy_options[:inverse_of]
|
40
|
+
attrs.update(:"#{proxy_options[:inverse_of]}_id" => proxy_owner.id)
|
41
|
+
end
|
42
|
+
proxy_class.create(attrs).tap do |record|
|
43
|
+
if record.persisted?
|
44
|
+
proxy_owner.reload
|
45
|
+
push(record)
|
46
|
+
proxy_owner.save
|
47
|
+
reset
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def destroy(*args, &block)
|
53
|
+
ids = block_given? ? target.select(&block).map(&:id) : args.flatten
|
54
|
+
proxy_class.destroy(*ids)
|
55
|
+
proxy_owner.reload
|
56
|
+
self.target_ids = target_ids - ids
|
57
|
+
proxy_owner.save
|
58
|
+
reset
|
59
|
+
end
|
60
|
+
|
61
|
+
def destroy_all
|
62
|
+
proxy_class.destroy(*target_ids)
|
63
|
+
self.target_ids = []
|
64
|
+
proxy_owner.save
|
65
|
+
reset
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset
|
69
|
+
@target = nil
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def find_target
|
74
|
+
return [] if target_ids.blank?
|
75
|
+
proxy_class.get_multi(target_ids)
|
76
|
+
end
|
77
|
+
|
78
|
+
def target_ids
|
79
|
+
proxy_owner.send(proxy_key)
|
80
|
+
end
|
81
|
+
|
82
|
+
def target_ids=(value)
|
83
|
+
ids = value.map do |item|
|
84
|
+
if item.is_a?(proxy_class)
|
85
|
+
item.id
|
86
|
+
elsif item.is_a?(Hash)
|
87
|
+
item['id']
|
88
|
+
else
|
89
|
+
item
|
90
|
+
end
|
91
|
+
end
|
92
|
+
proxy_owner.send(:"#{proxy_key}=", ids)
|
93
|
+
reset
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|