toystore 0.8.3 → 0.9.0
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/.gitignore +1 -2
- data/Changelog.md +9 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +71 -0
- data/Guardfile +15 -0
- data/README.md +28 -0
- data/examples/attributes_abbreviation.rb +1 -2
- data/examples/attributes_virtual.rb +1 -2
- data/examples/identity_map.rb +7 -12
- data/examples/memcached.rb +1 -1
- data/examples/memory.rb +1 -1
- data/examples/mongo.rb +1 -1
- data/examples/redis.rb +1 -1
- data/examples/riak.rb +1 -1
- data/lib/toy.rb +40 -39
- data/lib/toy/attribute.rb +1 -6
- data/lib/toy/attributes.rb +61 -90
- data/lib/toy/caching.rb +11 -13
- data/lib/toy/callbacks.rb +12 -31
- data/lib/toy/cloneable.rb +20 -0
- data/lib/toy/collection.rb +8 -7
- data/lib/toy/dirty.rb +17 -36
- data/lib/toy/dirty_store.rb +32 -0
- data/lib/toy/equality.rb +2 -0
- data/lib/toy/extensions/boolean.rb +22 -18
- data/lib/toy/identity_map.rb +39 -62
- data/lib/toy/list.rb +23 -22
- data/lib/toy/logger.rb +2 -17
- data/lib/toy/mass_assignment_security.rb +3 -5
- data/lib/toy/middleware/identity_map.rb +23 -4
- data/lib/toy/object.rb +16 -0
- data/lib/toy/persistence.rb +72 -62
- data/lib/toy/proxies/list.rb +19 -18
- data/lib/toy/proxies/proxy.rb +7 -6
- data/lib/toy/querying.rb +2 -4
- data/lib/toy/reference.rb +28 -26
- data/lib/toy/reloadable.rb +17 -0
- data/lib/toy/serialization.rb +25 -25
- data/lib/toy/store.rb +3 -11
- data/lib/toy/validations.rb +9 -28
- data/lib/toy/version.rb +1 -1
- data/perf/reads.rb +7 -9
- data/perf/writes.rb +6 -8
- data/spec/helper.rb +3 -1
- data/spec/support/constants.rb +1 -4
- data/spec/support/identity_map_matcher.rb +5 -5
- data/spec/support/objects.rb +38 -0
- data/spec/toy/attribute_spec.rb +1 -1
- data/spec/toy/attributes_spec.rb +1 -153
- data/spec/toy/callbacks_spec.rb +1 -45
- data/spec/toy/cloneable_spec.rb +47 -0
- data/spec/toy/dirty_spec.rb +12 -44
- data/spec/toy/dirty_store_spec.rb +47 -0
- data/spec/toy/equality_spec.rb +5 -19
- data/spec/toy/extensions/boolean_spec.rb +2 -0
- data/spec/toy/identity/uuid_key_factory_spec.rb +2 -2
- data/spec/toy/identity_map_spec.rb +45 -37
- data/spec/toy/identity_spec.rb +1 -1
- data/spec/toy/inspect_spec.rb +1 -1
- data/spec/toy/lists_spec.rb +20 -5
- data/spec/toy/logger_spec.rb +1 -29
- data/spec/toy/mass_assignment_security_spec.rb +16 -5
- data/spec/toy/middleware/identity_map_spec.rb +68 -2
- data/spec/toy/persistence_spec.rb +88 -30
- data/spec/toy/reference_spec.rb +0 -1
- data/spec/toy/references_spec.rb +20 -0
- data/spec/toy/reloadable_spec.rb +81 -0
- data/spec/toy/serialization_spec.rb +1 -110
- data/spec/toy/validations_spec.rb +0 -21
- data/spec/toy_spec.rb +4 -5
- data/test/lint_test.rb +1 -1
- metadata +21 -26
- data/.autotest +0 -11
- data/LOGGING.rdoc +0 -12
- data/README.rdoc +0 -27
- data/examples/models.rb +0 -51
- data/lib/toy/dolly.rb +0 -30
- data/lib/toy/embedded_list.rb +0 -45
- data/lib/toy/embedded_lists.rb +0 -68
- data/lib/toy/index.rb +0 -74
- data/lib/toy/indices.rb +0 -56
- data/lib/toy/proxies/embedded_list.rb +0 -79
- data/spec/toy/dolly_spec.rb +0 -76
- data/spec/toy/embedded_list_spec.rb +0 -607
- data/spec/toy/embedded_lists_spec.rb +0 -172
- data/spec/toy/index_spec.rb +0 -230
- data/spec/toy/indices_spec.rb +0 -141
- data/specs.watchr +0 -52
data/LOGGING.rdoc
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
What the 3 letter codes mean when they show up in logging:
|
2
|
-
|
3
|
-
SET: write to store
|
4
|
-
GET: read from store
|
5
|
-
DEL: delete from store
|
6
|
-
KEY: check if key exists in store
|
7
|
-
|
8
|
-
IMG: read from identity map
|
9
|
-
IMS: write to identity map
|
10
|
-
IMD: delete from identity map
|
11
|
-
|
12
|
-
IEM: invalid embedded document
|
data/README.rdoc
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
= Toystore
|
2
|
-
|
3
|
-
An object mapper for anything that can read, write and delete data.
|
4
|
-
|
5
|
-
See examples/ for potential direction. The idea is that any key-value store (via adapters) that supports read, write, delete will work (memcache, membase, mongo, redis, couch, toyko. Potentially even RESTFUL services or sqlite with a single key-value table?)
|
6
|
-
|
7
|
-
== Mailing List
|
8
|
-
|
9
|
-
https://groups.google.com/forum/#!forum/toystoreadapter
|
10
|
-
|
11
|
-
== Identity Map
|
12
|
-
|
13
|
-
By default, Toystore has identity map turned on. It assumes that any Toystore model has a unique id across all models. This means you either need to use the default uuid id's or create your own key factory that namespaces to model (see examples).
|
14
|
-
|
15
|
-
You also need to clear the map before each request. For this, there is a provided piece of middleware that you can use.
|
16
|
-
|
17
|
-
use(Toy::Middleware::IdentityMap)
|
18
|
-
|
19
|
-
It is autoloaded, so just add it to your config.ru or sinatra/rails app as you would any other middleware and you are good to go.
|
20
|
-
|
21
|
-
== Note on Patches/Pull Requests
|
22
|
-
|
23
|
-
* Fork the project.
|
24
|
-
* Make your feature addition or bug fix.
|
25
|
-
* Add tests for it. This is important so we don't break it in a future version unintentionally.
|
26
|
-
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine, but bump version in a commit by itself so we can ignore when we pull)
|
27
|
-
* Send us a pull request. Bonus points for topic branches.
|
data/examples/models.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'pathname'
|
2
|
-
|
3
|
-
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
4
|
-
lib_path = root_path.join('lib')
|
5
|
-
$:.unshift(lib_path)
|
6
|
-
|
7
|
-
require 'toy'
|
8
|
-
require 'adapter/memory'
|
9
|
-
|
10
|
-
class Address
|
11
|
-
include Toy::Store
|
12
|
-
store :memory, {}
|
13
|
-
|
14
|
-
attribute :city, String
|
15
|
-
attribute :state, String
|
16
|
-
attribute :zip, String
|
17
|
-
|
18
|
-
index :zip
|
19
|
-
end
|
20
|
-
|
21
|
-
class PhoneNumber
|
22
|
-
include Toy::Store
|
23
|
-
|
24
|
-
attribute :area_code, String
|
25
|
-
attribute :number, String
|
26
|
-
end
|
27
|
-
|
28
|
-
class Company
|
29
|
-
include Toy::Store
|
30
|
-
store :memory, {}
|
31
|
-
|
32
|
-
attribute :name, String
|
33
|
-
end
|
34
|
-
|
35
|
-
class User
|
36
|
-
include Toy::Store
|
37
|
-
store :memory, {}
|
38
|
-
|
39
|
-
attribute :name, String
|
40
|
-
attribute :age, Integer
|
41
|
-
attribute :admin, Boolean, :default => false
|
42
|
-
attribute :ssn, String
|
43
|
-
timestamps
|
44
|
-
|
45
|
-
index :ssn
|
46
|
-
|
47
|
-
list :addresses, :dependendent => true
|
48
|
-
reference :employer, Company
|
49
|
-
|
50
|
-
# validations and callbacks are available too
|
51
|
-
end
|
data/lib/toy/dolly.rb
DELETED
@@ -1,30 +0,0 @@
|
|
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 = {}
|
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
|
data/lib/toy/embedded_list.rb
DELETED
@@ -1,45 +0,0 @@
|
|
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
|
data/lib/toy/embedded_lists.rb
DELETED
@@ -1,68 +0,0 @@
|
|
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/index.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
module Toy
|
2
|
-
class Index
|
3
|
-
attr_accessor :model, :name
|
4
|
-
|
5
|
-
def initialize(model, name)
|
6
|
-
@model, @name = model, name.to_sym
|
7
|
-
raise(ArgumentError, "No attribute #{name} for index") unless model.attribute?(name)
|
8
|
-
|
9
|
-
model.indices[name] = self
|
10
|
-
model.send(:include, IndexCallbacks)
|
11
|
-
create_finders
|
12
|
-
end
|
13
|
-
|
14
|
-
def eql?(other)
|
15
|
-
self.class.eql?(other.class) &&
|
16
|
-
model == other.model &&
|
17
|
-
name == other.name
|
18
|
-
end
|
19
|
-
alias :== :eql?
|
20
|
-
|
21
|
-
def key(value)
|
22
|
-
sha_value = Digest::SHA1.hexdigest(Array.wrap(value).sort.join('')) # sorted for predictability
|
23
|
-
[model.name, name, sha_value].join(':')
|
24
|
-
end
|
25
|
-
|
26
|
-
module IndexCallbacks
|
27
|
-
extend ActiveSupport::Concern
|
28
|
-
|
29
|
-
included do
|
30
|
-
after_create :index_create
|
31
|
-
after_update :index_update
|
32
|
-
after_destroy :index_destroy
|
33
|
-
end
|
34
|
-
|
35
|
-
def index_create
|
36
|
-
indices.each_key do |name|
|
37
|
-
create_index(name, send(name), id)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def index_update
|
42
|
-
indices.each_key do |name|
|
43
|
-
if send(:"#{name}_changed?")
|
44
|
-
destroy_index(name, send(:"#{name}_was"), id)
|
45
|
-
create_index(name, send(name), id)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def index_destroy
|
51
|
-
indices.each_key do |name|
|
52
|
-
destroy_index(name, send(name), id)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
def create_finders
|
59
|
-
model.class_eval """
|
60
|
-
def self.first_by_#{name}(value)
|
61
|
-
get(get_index(:#{name}, value)[0])
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.first_or_new_by_#{name}(value)
|
65
|
-
first_by_#{name}(value) || new(:#{name} => value)
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.first_or_create_by_#{name}(value)
|
69
|
-
first_by_#{name}(value) || create(:#{name} => value)
|
70
|
-
end
|
71
|
-
"""
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/lib/toy/indices.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
module Toy
|
2
|
-
module Indices
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
module ClassMethods
|
6
|
-
def indices
|
7
|
-
@indices ||= {}
|
8
|
-
end
|
9
|
-
|
10
|
-
def index(name)
|
11
|
-
Index.new(self, name)
|
12
|
-
end
|
13
|
-
|
14
|
-
def index_key(name, value)
|
15
|
-
if index = indices[name.to_sym]
|
16
|
-
index.key(value)
|
17
|
-
else
|
18
|
-
raise(ArgumentError, "Index for #{name} does not exist")
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def get_index(name, value)
|
23
|
-
key = index_key(name, value)
|
24
|
-
store.read(key) || []
|
25
|
-
end
|
26
|
-
|
27
|
-
def create_index(name, value, id)
|
28
|
-
key = index_key(name, value)
|
29
|
-
ids = get_index(name, value)
|
30
|
-
ids.push(id) unless ids.include?(id)
|
31
|
-
store.write(key, ids)
|
32
|
-
end
|
33
|
-
|
34
|
-
def destroy_index(name, value, id)
|
35
|
-
key = index_key(name, value)
|
36
|
-
ids = get_index(name, value)
|
37
|
-
ids.delete(id)
|
38
|
-
store.write(key, ids)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
module InstanceMethods
|
43
|
-
def indices
|
44
|
-
self.class.indices
|
45
|
-
end
|
46
|
-
|
47
|
-
def create_index(*args)
|
48
|
-
self.class.create_index(*args)
|
49
|
-
end
|
50
|
-
|
51
|
-
def destroy_index(*args)
|
52
|
-
self.class.destroy_index(*args)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,79 +0,0 @@
|
|
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 = if instance.is_a?(proxy_class)
|
38
|
-
instance
|
39
|
-
else
|
40
|
-
key = instance.delete('id')
|
41
|
-
proxy_class.load(key, instance)
|
42
|
-
end
|
43
|
-
assign_reference(instance)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def create(attrs={})
|
48
|
-
proxy_class.new(attrs).tap do |instance|
|
49
|
-
assign_reference(instance)
|
50
|
-
if instance.valid?
|
51
|
-
push(instance)
|
52
|
-
proxy_owner.save
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def destroy(*args, &block)
|
58
|
-
ids = block_given? ? target.select(&block).map(&:id) : args.flatten
|
59
|
-
target.delete_if { |instance| ids.include?(instance.id) }
|
60
|
-
proxy_owner.save
|
61
|
-
end
|
62
|
-
|
63
|
-
def destroy_all
|
64
|
-
target.clear
|
65
|
-
proxy_owner.save
|
66
|
-
end
|
67
|
-
|
68
|
-
private
|
69
|
-
def find_target
|
70
|
-
[]
|
71
|
-
end
|
72
|
-
|
73
|
-
def assign_reference(instance)
|
74
|
-
instance.parent_reference = proxy_owner
|
75
|
-
instance
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|