toystore 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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/.autotest
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'autotest/growl'
|
2
|
+
|
3
|
+
Autotest.add_hook(:initialize) {|at|
|
4
|
+
at.add_exception %r{^\.git} # ignore Version Control System
|
5
|
+
at.add_exception %r{^./tmp} # ignore temp files, lest autotest will run again, and again...
|
6
|
+
# at.clear_mappings # take out the default (test/test*rb)
|
7
|
+
at.add_mapping(%r{^lib/.*\.rb$}) {|f, _|
|
8
|
+
Dir['spec/**/*.rb']
|
9
|
+
}
|
10
|
+
nil
|
11
|
+
}
|
data/.bundle/config
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
toystore (0.5)
|
5
|
+
activemodel (~> 3.0.3)
|
6
|
+
activesupport (~> 3.0.3)
|
7
|
+
adapter (~> 0.5.1)
|
8
|
+
simple_uuid (~> 0.1.1)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: http://rubygems.org/
|
12
|
+
specs:
|
13
|
+
activemodel (3.0.3)
|
14
|
+
activesupport (= 3.0.3)
|
15
|
+
builder (~> 2.1.2)
|
16
|
+
i18n (~> 0.4)
|
17
|
+
activesupport (3.0.3)
|
18
|
+
adapter (0.5.1)
|
19
|
+
builder (2.1.2)
|
20
|
+
diff-lcs (1.1.2)
|
21
|
+
i18n (0.5.0)
|
22
|
+
log_buddy (0.5.0)
|
23
|
+
rake (0.8.7)
|
24
|
+
rspec (2.4.0)
|
25
|
+
rspec-core (~> 2.4.0)
|
26
|
+
rspec-expectations (~> 2.4.0)
|
27
|
+
rspec-mocks (~> 2.4.0)
|
28
|
+
rspec-core (2.4.0)
|
29
|
+
rspec-expectations (2.4.0)
|
30
|
+
diff-lcs (~> 1.1.2)
|
31
|
+
rspec-mocks (2.4.0)
|
32
|
+
simple_uuid (0.1.1)
|
33
|
+
timecop (0.3.5)
|
34
|
+
tzinfo (0.3.23)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
activemodel (~> 3.0.3)
|
41
|
+
activesupport (~> 3.0.3)
|
42
|
+
adapter (~> 0.5.1)
|
43
|
+
log_buddy (~> 0.5.0)
|
44
|
+
rake (~> 0.8.7)
|
45
|
+
rspec (~> 2.3)
|
46
|
+
simple_uuid (~> 0.1.1)
|
47
|
+
timecop (~> 0.3.5)
|
48
|
+
toystore!
|
49
|
+
tzinfo (~> 0.3.23)
|
data/LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Copyright (c) 2011, Zynga Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
5
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
7
|
+
* Neither the name of Zynga Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
8
|
+
|
9
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/LOGGING.rdoc
ADDED
@@ -0,0 +1,16 @@
|
|
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
|
+
RTG: get cache read
|
13
|
+
RTS: get cache set
|
14
|
+
WTS: cache write set
|
15
|
+
|
16
|
+
IEM: invalid embedded document
|
data/README.rdoc
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
= Toystore
|
2
|
+
|
3
|
+
An object mapper for anything that can read, write and delete data.
|
4
|
+
|
5
|
+
See examples/models.rb for potential direction. The idea is that any key-value store (via adapters) that supports read, write, delete will work (memcache, membase, redis, couch, toyko. Potentially even RESTFUL services or sqlite with a single key-value table?)
|
6
|
+
|
7
|
+
== Note on Patches/Pull Requests
|
8
|
+
|
9
|
+
* Fork the project.
|
10
|
+
* Make your feature addition or bug fix.
|
11
|
+
* Add tests for it. This is important so we don't break it in a future version unintentionally.
|
12
|
+
* 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)
|
13
|
+
* Send us a pull request. Bonus points for topic branches.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'toystore'
|
4
|
+
require 'adapter/memcached'
|
5
|
+
|
6
|
+
class GameList
|
7
|
+
include Toy::Store
|
8
|
+
store :memcached, Memcached.new
|
9
|
+
|
10
|
+
attribute :source, Hash
|
11
|
+
end
|
12
|
+
|
13
|
+
list = GameList.create(:source => {'foo' => 'bar'})
|
14
|
+
|
15
|
+
pp list
|
16
|
+
pp GameList.get(list.id)
|
17
|
+
|
18
|
+
list.destroy
|
19
|
+
|
20
|
+
pp GameList.get(list.id)
|
data/examples/memory.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'toystore'
|
4
|
+
require 'adapter/memory'
|
5
|
+
|
6
|
+
class User
|
7
|
+
include Toy::Store
|
8
|
+
store :memory, {}
|
9
|
+
|
10
|
+
attribute :name, String
|
11
|
+
end
|
12
|
+
|
13
|
+
user = User.create(:name => 'John')
|
14
|
+
|
15
|
+
pp user
|
16
|
+
pp User.get(user.id)
|
17
|
+
|
18
|
+
user.destroy
|
19
|
+
|
20
|
+
pp User.get(user.id)
|
data/examples/models.rb
ADDED
@@ -0,0 +1,51 @@
|
|
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/examples/redis.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'toystore'
|
4
|
+
require 'adapter/redis'
|
5
|
+
|
6
|
+
class User
|
7
|
+
include Toy::Store
|
8
|
+
store :redis, Redis.new
|
9
|
+
|
10
|
+
attribute :name, String
|
11
|
+
end
|
12
|
+
|
13
|
+
user = User.create(:name => 'John')
|
14
|
+
|
15
|
+
pp user
|
16
|
+
pp User.get(user.id)
|
17
|
+
|
18
|
+
user.destroy
|
19
|
+
|
20
|
+
pp User.get(user.id)
|
data/lib/toy.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'pathname'
|
3
|
+
require 'forwardable'
|
4
|
+
require 'digest/sha1'
|
5
|
+
|
6
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
7
|
+
|
8
|
+
require 'adapter'
|
9
|
+
require 'simple_uuid'
|
10
|
+
require 'active_model'
|
11
|
+
require 'active_support/json'
|
12
|
+
require 'active_support/core_ext/object'
|
13
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
14
|
+
require 'active_support/core_ext/hash/keys'
|
15
|
+
require 'active_support/core_ext/class/inheritable_attributes'
|
16
|
+
require 'active_support/core_ext/string/conversions'
|
17
|
+
require 'active_support/core_ext/string/inflections'
|
18
|
+
|
19
|
+
extensions_path = root_path.join('lib', 'toy', 'extensions')
|
20
|
+
Dir[extensions_path + '**/*.rb'].each { |file| require(file) }
|
21
|
+
|
22
|
+
module Toy
|
23
|
+
extend self
|
24
|
+
|
25
|
+
# Resets all tracking of things in memory. Useful for running
|
26
|
+
# before each request in development mode with Rails and such.
|
27
|
+
def reset
|
28
|
+
identity_map.clear
|
29
|
+
plugins.clear
|
30
|
+
models.clear
|
31
|
+
end
|
32
|
+
|
33
|
+
# Clears all the stores for all the models. Useful in specs/tests/etc.
|
34
|
+
# Do not use in production, harty harr harr.
|
35
|
+
#
|
36
|
+
# Note: that if your models are auto-loaded like in Rails, you will need
|
37
|
+
# to make sure they are loaded in order to clear them or ToyStore will
|
38
|
+
# not be aware of their existence.
|
39
|
+
def clear
|
40
|
+
models.each do |model|
|
41
|
+
model.store.clear if model.has_store?
|
42
|
+
model.cache.clear if model.has_cache?
|
43
|
+
model.identity_map.clear if model.identity_map_on?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require 'toy/exceptions'
|
49
|
+
require 'toy/connection'
|
50
|
+
require 'toy/attribute'
|
51
|
+
require 'toy/attributes'
|
52
|
+
require 'toy/callbacks'
|
53
|
+
require 'toy/dirty'
|
54
|
+
require 'toy/dolly'
|
55
|
+
require 'toy/equality'
|
56
|
+
require 'toy/inspect'
|
57
|
+
require 'toy/persistence'
|
58
|
+
require 'toy/mass_assignment_security'
|
59
|
+
require 'toy/plugins'
|
60
|
+
require 'toy/store'
|
61
|
+
require 'toy/serialization'
|
62
|
+
require 'toy/timestamps'
|
63
|
+
require 'toy/validations'
|
64
|
+
require 'toy/querying'
|
65
|
+
require 'toy/identity_map'
|
66
|
+
|
67
|
+
require 'toy/collection'
|
68
|
+
require 'toy/embedded_list'
|
69
|
+
require 'toy/embedded_lists'
|
70
|
+
require 'toy/list'
|
71
|
+
require 'toy/lists'
|
72
|
+
require 'toy/reference'
|
73
|
+
require 'toy/references'
|
74
|
+
|
75
|
+
require 'toy/index'
|
76
|
+
require 'toy/indices'
|
77
|
+
require 'toy/identity/abstract_key_factory'
|
78
|
+
require 'toy/identity/uuid_key_factory'
|
79
|
+
require 'toy/identity'
|
80
|
+
require 'toy/caching'
|
81
|
+
require 'toy/logger'
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Toy
|
2
|
+
class Attribute
|
3
|
+
attr_reader :model, :name, :type, :options
|
4
|
+
|
5
|
+
def initialize(model, name, type, options={})
|
6
|
+
options.assert_valid_keys(:default, :embedded_list, :virtual, :abbr)
|
7
|
+
|
8
|
+
@model, @name, @type, @options = model, name.to_sym, type, options
|
9
|
+
@virtual = options.fetch(:virtual, false)
|
10
|
+
|
11
|
+
if abbr?
|
12
|
+
options[:abbr] = abbr.to_sym
|
13
|
+
model.alias_attribute(abbr, name)
|
14
|
+
end
|
15
|
+
|
16
|
+
model.attributes[name.to_s] = self
|
17
|
+
end
|
18
|
+
|
19
|
+
def from_store(value)
|
20
|
+
value = default if default? && value.nil?
|
21
|
+
type.from_store(value, self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_store(value)
|
25
|
+
value = default if default? && value.nil?
|
26
|
+
type.to_store(value, self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def default
|
30
|
+
if options.key?(:default)
|
31
|
+
options[:default].respond_to?(:call) ? options[:default].call : options[:default]
|
32
|
+
else
|
33
|
+
type.respond_to?(:store_default) ? type.store_default : nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def default?
|
38
|
+
options.key?(:default) || type.respond_to?(:store_default)
|
39
|
+
end
|
40
|
+
|
41
|
+
def virtual?
|
42
|
+
@virtual
|
43
|
+
end
|
44
|
+
|
45
|
+
def persisted?
|
46
|
+
!virtual?
|
47
|
+
end
|
48
|
+
|
49
|
+
def abbr?
|
50
|
+
options.key?(:abbr)
|
51
|
+
end
|
52
|
+
|
53
|
+
def abbr
|
54
|
+
options[:abbr]
|
55
|
+
end
|
56
|
+
|
57
|
+
def store_key
|
58
|
+
(abbr? ? abbr : name).to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
# Stores reference to related embedded list
|
62
|
+
def embedded_list
|
63
|
+
options[:embedded_list]
|
64
|
+
end
|
65
|
+
|
66
|
+
def eql?(other)
|
67
|
+
self.class.eql?(other.class) &&
|
68
|
+
model == other.model &&
|
69
|
+
name == other.name
|
70
|
+
end
|
71
|
+
alias :== :eql?
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Toy
|
2
|
+
module Attributes
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
include ActiveModel::AttributeMethods
|
5
|
+
|
6
|
+
included do
|
7
|
+
attribute_method_suffix('', '=', '?')
|
8
|
+
attribute :id, String
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def attributes
|
13
|
+
@attributes ||= {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def defaulted_attributes
|
17
|
+
attributes.values.select(&:default?)
|
18
|
+
end
|
19
|
+
|
20
|
+
def attribute(key, type, options = {})
|
21
|
+
Attribute.new(self, key, type, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def attribute?(key)
|
25
|
+
attributes.has_key?(key.to_s)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module InstanceMethods
|
30
|
+
def initialize(attrs={})
|
31
|
+
@_new_record = true unless defined?(@_new_record)
|
32
|
+
initialize_attributes_with_defaults
|
33
|
+
self.attributes = attrs
|
34
|
+
write_attribute :id, self.class.next_key(self) unless id?
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_from_database(attrs={})
|
38
|
+
@_new_record = false
|
39
|
+
send(:initialize, attrs)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def reload
|
44
|
+
if attrs = store.read(store_key)
|
45
|
+
instance_variables.each { |ivar| instance_variable_set(ivar, nil) }
|
46
|
+
initialize_attributes_with_defaults
|
47
|
+
self.attributes = attrs
|
48
|
+
self.class.lists.each_key { |name| send(name).reset }
|
49
|
+
self.class.references.each_key { |name| send(name).reset }
|
50
|
+
else
|
51
|
+
raise NotFound.new(id)
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def id
|
57
|
+
read_attribute(:id)
|
58
|
+
end
|
59
|
+
|
60
|
+
def attributes
|
61
|
+
@attributes
|
62
|
+
end
|
63
|
+
|
64
|
+
def persisted_attributes
|
65
|
+
{}.tap do |attrs|
|
66
|
+
self.class.attributes.each do |name, attribute|
|
67
|
+
next if attribute.virtual?
|
68
|
+
attrs[attribute.store_key] = attribute.to_store(read_attribute(attribute.name))
|
69
|
+
end
|
70
|
+
end.merge(embedded_attributes)
|
71
|
+
end
|
72
|
+
|
73
|
+
def embedded_attributes
|
74
|
+
{}.tap do |attrs|
|
75
|
+
self.class.embedded_lists.each_key do |name|
|
76
|
+
attrs[name.to_s] = send(name).map(&:persisted_attributes)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def attributes=(attrs)
|
82
|
+
return if attrs.nil?
|
83
|
+
attrs.each do |key, value|
|
84
|
+
if attribute_method?(key)
|
85
|
+
write_attribute(key, value)
|
86
|
+
elsif respond_to?("#{key}=")
|
87
|
+
send("#{key}=", value)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def [](key)
|
93
|
+
read_attribute(key)
|
94
|
+
end
|
95
|
+
|
96
|
+
def []=(key, value)
|
97
|
+
write_attribute(key, value)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def read_attribute(key)
|
102
|
+
@attributes[key]
|
103
|
+
end
|
104
|
+
|
105
|
+
def write_attribute(key, value)
|
106
|
+
@attributes[key] = attribute_definition(key).try(:from_store, value)
|
107
|
+
end
|
108
|
+
|
109
|
+
def attribute_definition(key)
|
110
|
+
self.class.attributes[key.to_s]
|
111
|
+
end
|
112
|
+
|
113
|
+
def attribute_method?(key)
|
114
|
+
self.class.attribute?(key)
|
115
|
+
end
|
116
|
+
|
117
|
+
def attribute(key)
|
118
|
+
read_attribute(key)
|
119
|
+
end
|
120
|
+
|
121
|
+
def attribute=(key, value)
|
122
|
+
write_attribute(key, value)
|
123
|
+
end
|
124
|
+
|
125
|
+
def attribute?(key)
|
126
|
+
read_attribute(key).present?
|
127
|
+
end
|
128
|
+
|
129
|
+
def initialize_attributes_with_defaults
|
130
|
+
@attributes = {}.with_indifferent_access
|
131
|
+
self.class.defaulted_attributes.each do |attribute|
|
132
|
+
@attributes[attribute.name] = attribute.default
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|