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/caching.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Toy
|
2
|
+
module Caching
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
def cache_key(*suffixes)
|
7
|
+
cache_key = case
|
8
|
+
when new_record?
|
9
|
+
"#{self.class.name}:new"
|
10
|
+
when timestamp = self[:updated_at]
|
11
|
+
"#{self.class.name}:#{id}-#{timestamp.utc.to_s(:number)}"
|
12
|
+
else
|
13
|
+
"#{self.class.name}:#{id}"
|
14
|
+
end
|
15
|
+
cache_key += ":#{suffixes.join(':')}" unless suffixes.empty?
|
16
|
+
cache_key
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Toy
|
2
|
+
module Callbacks
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveSupport::Callbacks
|
5
|
+
|
6
|
+
included do
|
7
|
+
extend ActiveModel::Callbacks
|
8
|
+
define_model_callbacks :save, :create, :update, :destroy
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def save(*)
|
13
|
+
run_callbacks(:save) { super }
|
14
|
+
end
|
15
|
+
|
16
|
+
def destroy
|
17
|
+
run_callbacks(:destroy) { super }
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_callbacks(callback, &block)
|
21
|
+
callback = :create if callback == :update && !persisted?
|
22
|
+
|
23
|
+
embedded_records = self.class.embedded_lists.keys.inject([]) do |records, key|
|
24
|
+
records += send(key).target
|
25
|
+
end
|
26
|
+
|
27
|
+
block = embedded_records.inject(block) do |chain, record|
|
28
|
+
if record.class.respond_to?("_#{callback}_callbacks")
|
29
|
+
lambda { record.run_callbacks(callback, &chain) }
|
30
|
+
else
|
31
|
+
chain
|
32
|
+
end
|
33
|
+
end
|
34
|
+
super callback, &block
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def create
|
40
|
+
run_callbacks(:create) { super }
|
41
|
+
end
|
42
|
+
|
43
|
+
def update
|
44
|
+
run_callbacks(:update) { super }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Toy
|
2
|
+
module Collection
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
attr_accessor :model, :name, :options
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(model, name, *args, &block)
|
10
|
+
@model = model
|
11
|
+
@name = name.to_sym
|
12
|
+
@options = args.extract_options!
|
13
|
+
@type = args.shift
|
14
|
+
|
15
|
+
model.send(list_method)[name] = self
|
16
|
+
|
17
|
+
options[:extensions] = modularized_extensions(block, options[:extensions])
|
18
|
+
after_initialize
|
19
|
+
end
|
20
|
+
|
21
|
+
def type
|
22
|
+
@type ||= name.to_s.classify.constantize
|
23
|
+
end
|
24
|
+
|
25
|
+
def instance_variable
|
26
|
+
@instance_variable ||= :"@_#{name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_proxy(owner)
|
30
|
+
proxy_class.new(self, owner)
|
31
|
+
end
|
32
|
+
|
33
|
+
def extensions
|
34
|
+
options[:extensions]
|
35
|
+
end
|
36
|
+
|
37
|
+
def eql?(other)
|
38
|
+
self.class.eql?(other.class) &&
|
39
|
+
model == other.model &&
|
40
|
+
name == other.name
|
41
|
+
end
|
42
|
+
alias :== :eql?
|
43
|
+
|
44
|
+
private
|
45
|
+
def proxy_class
|
46
|
+
raise('Not Implemented')
|
47
|
+
end
|
48
|
+
|
49
|
+
def modularized_extensions(*extensions)
|
50
|
+
extensions.flatten.compact.map do |extension|
|
51
|
+
Proc === extension ? Module.new(&extension) : extension
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Toy
|
2
|
+
module Connection
|
3
|
+
def logger
|
4
|
+
@@logger ||= init_default_logger
|
5
|
+
end
|
6
|
+
|
7
|
+
def logger=(logger)
|
8
|
+
@@logger = logger
|
9
|
+
end
|
10
|
+
|
11
|
+
def key_factory=(key_factory)
|
12
|
+
@@key_factory = key_factory
|
13
|
+
end
|
14
|
+
|
15
|
+
def key_factory
|
16
|
+
@@key_factory ||= Toy::Identity::UUIDKeyFactory.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def init_default_logger
|
20
|
+
if Object.const_defined?("RAILS_DEFAULT_LOGGER")
|
21
|
+
@@logger = Object.const_get("RAILS_DEFAULT_LOGGER")
|
22
|
+
else
|
23
|
+
require 'logger'
|
24
|
+
@@logger = ::Logger.new(STDOUT)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/toy/dirty.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Toy
|
2
|
+
module Dirty
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveModel::Dirty
|
5
|
+
|
6
|
+
module InstanceMethods
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
if new_record?
|
10
|
+
# never register initial id assignment as a change
|
11
|
+
@changed_attributes.delete('id')
|
12
|
+
else
|
13
|
+
@previously_changed = {}
|
14
|
+
@changed_attributes.clear if @changed_attributes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize_copy(*)
|
19
|
+
super.tap do
|
20
|
+
@previously_changed = {}
|
21
|
+
@changed_attributes = {}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def reload
|
26
|
+
super.tap do
|
27
|
+
@previously_changed = {}
|
28
|
+
@changed_attributes = {}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def save(*)
|
33
|
+
super.tap do
|
34
|
+
@previously_changed = changes
|
35
|
+
@changed_attributes.clear if @changed_attributes
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def write_attribute(name, value)
|
40
|
+
name = name.to_s
|
41
|
+
current = read_attribute(name)
|
42
|
+
attribute_will_change!(name) if current != value
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/toy/dolly.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Toy
|
2
|
+
module Dolly
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def initialize_copy(other)
|
6
|
+
@_new_record = true
|
7
|
+
@_destroyed = false
|
8
|
+
@attributes = {}.with_indifferent_access
|
9
|
+
|
10
|
+
self.class.embedded_lists.each do |name, list|
|
11
|
+
instance_variable_set(list.instance_variable, nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
self.class.lists.each do |name, list|
|
15
|
+
instance_variable_set(list.instance_variable, nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
other.attributes.except('id').each do |key, value|
|
19
|
+
value = value.duplicable? ? value.clone : value
|
20
|
+
send("#{key}=", value)
|
21
|
+
end
|
22
|
+
|
23
|
+
other.class.embedded_lists.keys.each do |name|
|
24
|
+
send("#{name}=", other.send(name).map(&:clone))
|
25
|
+
end
|
26
|
+
|
27
|
+
write_attribute(:id, self.class.next_key(self))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'toy/proxies/embedded_list'
|
2
|
+
|
3
|
+
module Toy
|
4
|
+
class EmbeddedList
|
5
|
+
include Toy::Collection
|
6
|
+
|
7
|
+
def after_initialize
|
8
|
+
create_accessors
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def create_accessors
|
13
|
+
model.class_eval """
|
14
|
+
def #{name}
|
15
|
+
#{instance_variable} ||= self.class.#{list_method}[:#{name}].new_proxy(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def #{name}=(records)
|
19
|
+
#{name}.replace(records)
|
20
|
+
end
|
21
|
+
|
22
|
+
def #{name.to_s.singularize}_attributes=(attrs)
|
23
|
+
self.#{name} = attrs.map do |value|
|
24
|
+
value = value.is_a?(Hash) ? value : value[1]
|
25
|
+
#{type}.new(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def #{name.to_s.singularize}_attributes
|
30
|
+
#{name}.map(&:attributes)
|
31
|
+
end
|
32
|
+
"""
|
33
|
+
|
34
|
+
type.class_eval { attr_accessor :parent_reference }
|
35
|
+
end
|
36
|
+
|
37
|
+
def proxy_class
|
38
|
+
Toy::Proxies::EmbeddedList
|
39
|
+
end
|
40
|
+
|
41
|
+
def list_method
|
42
|
+
:embedded_lists
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Toy
|
2
|
+
module EmbeddedLists
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def embedded_lists
|
7
|
+
@embedded_lists ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def embedded_list?(key)
|
11
|
+
embedded_lists.keys.include?(key.to_sym)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parent_reference_module
|
15
|
+
@parent_reference_module ||= begin
|
16
|
+
mod = Module.new
|
17
|
+
include mod
|
18
|
+
mod
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parent_references
|
23
|
+
@parent_references ||= []
|
24
|
+
end
|
25
|
+
|
26
|
+
def parent_reference?(key)
|
27
|
+
parent_references.include?(key.to_sym)
|
28
|
+
end
|
29
|
+
|
30
|
+
def parent_reference(*names)
|
31
|
+
names.flatten.each do |name|
|
32
|
+
parent_references << name
|
33
|
+
parent_reference_module.module_eval <<-CODE
|
34
|
+
def #{name}
|
35
|
+
parent_reference
|
36
|
+
end
|
37
|
+
|
38
|
+
def #{name}?
|
39
|
+
parent_reference.present?
|
40
|
+
end
|
41
|
+
CODE
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @examples
|
46
|
+
# list :moves # assumes Move
|
47
|
+
# list :moves, :dependent => true # assumes Move
|
48
|
+
# list :recent_moves, Move # uses Move
|
49
|
+
# list :recent_moves, Move, :dependent => true # uses Move
|
50
|
+
#
|
51
|
+
# embedded_list :moves do
|
52
|
+
# def recent
|
53
|
+
# target.select { |t| t.recent? }
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# module RecentExtension
|
58
|
+
# def recent
|
59
|
+
# target.select { |t| t.recent? }
|
60
|
+
# end
|
61
|
+
# end
|
62
|
+
# embedded_list :moves, :extensions => [RecentExtension]
|
63
|
+
def embedded_list(name, *args, &block)
|
64
|
+
EmbeddedList.new(self, name, *args, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/toy/equality.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Toy
|
2
|
+
module Equality
|
3
|
+
def eql?(other)
|
4
|
+
return true if self.class.eql?(other.class) && id == other.id
|
5
|
+
return true if other.respond_to?(:target) &&
|
6
|
+
self.class.eql?(other.target.class) &&
|
7
|
+
id == other.target.id
|
8
|
+
false
|
9
|
+
end
|
10
|
+
alias :== :eql?
|
11
|
+
|
12
|
+
def equal?(other)
|
13
|
+
if other.respond_to?(:proxy_respond_to?) && other.respond_to?(:target)
|
14
|
+
other = other.target
|
15
|
+
end
|
16
|
+
super other
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Toy
|
2
|
+
class Error < StandardError; end
|
3
|
+
|
4
|
+
class RecordInvalidError < Error
|
5
|
+
attr_reader :record
|
6
|
+
def initialize(record)
|
7
|
+
@record = record
|
8
|
+
super("Invalid record: #{@record.errors.full_messages.to_sentence}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class NotFound < Error
|
13
|
+
def initialize(id)
|
14
|
+
super("Could not find document with id: #{id.inspect}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class UndefinedLock < Error
|
19
|
+
def initialize(klass, name)
|
20
|
+
super("Undefined lock :#{name} for class #{klass.name}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class AdapterNoLocky < Error
|
25
|
+
def initialize(adapter)
|
26
|
+
super("#{adapter.name.to_s.capitalize} adapter does not support locking")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Toy
|
2
|
+
module Extensions
|
3
|
+
module Array
|
4
|
+
def store_default
|
5
|
+
[]
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_store(value, *)
|
9
|
+
value = value.respond_to?(:lines) ? value.lines : value
|
10
|
+
value.to_a
|
11
|
+
end
|
12
|
+
|
13
|
+
def from_store(value, *)
|
14
|
+
value || store_default
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Array
|
21
|
+
extend Toy::Extensions::Array
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Toy
|
2
|
+
module Extensions
|
3
|
+
module Boolean
|
4
|
+
Mapping = {
|
5
|
+
true => true,
|
6
|
+
'true' => true,
|
7
|
+
'TRUE' => true,
|
8
|
+
'True' => true,
|
9
|
+
't' => true,
|
10
|
+
'T' => true,
|
11
|
+
'1' => true,
|
12
|
+
1 => true,
|
13
|
+
1.0 => true,
|
14
|
+
false => false,
|
15
|
+
'false' => false,
|
16
|
+
'FALSE' => false,
|
17
|
+
'False' => false,
|
18
|
+
'f' => false,
|
19
|
+
'F' => false,
|
20
|
+
'0' => false,
|
21
|
+
0 => false,
|
22
|
+
0.0 => false,
|
23
|
+
nil => nil
|
24
|
+
}
|
25
|
+
|
26
|
+
def to_store(value, *)
|
27
|
+
if value.is_a?(Boolean)
|
28
|
+
value
|
29
|
+
else
|
30
|
+
Mapping[value]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def from_store(value, *)
|
35
|
+
value.nil? ? nil : !!value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Boolean
|
42
|
+
extend Toy::Extensions::Boolean
|
43
|
+
end
|