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.
Files changed (88) hide show
  1. data/.gitignore +1 -2
  2. data/Changelog.md +9 -0
  3. data/Gemfile +5 -0
  4. data/Gemfile.lock +71 -0
  5. data/Guardfile +15 -0
  6. data/README.md +28 -0
  7. data/examples/attributes_abbreviation.rb +1 -2
  8. data/examples/attributes_virtual.rb +1 -2
  9. data/examples/identity_map.rb +7 -12
  10. data/examples/memcached.rb +1 -1
  11. data/examples/memory.rb +1 -1
  12. data/examples/mongo.rb +1 -1
  13. data/examples/redis.rb +1 -1
  14. data/examples/riak.rb +1 -1
  15. data/lib/toy.rb +40 -39
  16. data/lib/toy/attribute.rb +1 -6
  17. data/lib/toy/attributes.rb +61 -90
  18. data/lib/toy/caching.rb +11 -13
  19. data/lib/toy/callbacks.rb +12 -31
  20. data/lib/toy/cloneable.rb +20 -0
  21. data/lib/toy/collection.rb +8 -7
  22. data/lib/toy/dirty.rb +17 -36
  23. data/lib/toy/dirty_store.rb +32 -0
  24. data/lib/toy/equality.rb +2 -0
  25. data/lib/toy/extensions/boolean.rb +22 -18
  26. data/lib/toy/identity_map.rb +39 -62
  27. data/lib/toy/list.rb +23 -22
  28. data/lib/toy/logger.rb +2 -17
  29. data/lib/toy/mass_assignment_security.rb +3 -5
  30. data/lib/toy/middleware/identity_map.rb +23 -4
  31. data/lib/toy/object.rb +16 -0
  32. data/lib/toy/persistence.rb +72 -62
  33. data/lib/toy/proxies/list.rb +19 -18
  34. data/lib/toy/proxies/proxy.rb +7 -6
  35. data/lib/toy/querying.rb +2 -4
  36. data/lib/toy/reference.rb +28 -26
  37. data/lib/toy/reloadable.rb +17 -0
  38. data/lib/toy/serialization.rb +25 -25
  39. data/lib/toy/store.rb +3 -11
  40. data/lib/toy/validations.rb +9 -28
  41. data/lib/toy/version.rb +1 -1
  42. data/perf/reads.rb +7 -9
  43. data/perf/writes.rb +6 -8
  44. data/spec/helper.rb +3 -1
  45. data/spec/support/constants.rb +1 -4
  46. data/spec/support/identity_map_matcher.rb +5 -5
  47. data/spec/support/objects.rb +38 -0
  48. data/spec/toy/attribute_spec.rb +1 -1
  49. data/spec/toy/attributes_spec.rb +1 -153
  50. data/spec/toy/callbacks_spec.rb +1 -45
  51. data/spec/toy/cloneable_spec.rb +47 -0
  52. data/spec/toy/dirty_spec.rb +12 -44
  53. data/spec/toy/dirty_store_spec.rb +47 -0
  54. data/spec/toy/equality_spec.rb +5 -19
  55. data/spec/toy/extensions/boolean_spec.rb +2 -0
  56. data/spec/toy/identity/uuid_key_factory_spec.rb +2 -2
  57. data/spec/toy/identity_map_spec.rb +45 -37
  58. data/spec/toy/identity_spec.rb +1 -1
  59. data/spec/toy/inspect_spec.rb +1 -1
  60. data/spec/toy/lists_spec.rb +20 -5
  61. data/spec/toy/logger_spec.rb +1 -29
  62. data/spec/toy/mass_assignment_security_spec.rb +16 -5
  63. data/spec/toy/middleware/identity_map_spec.rb +68 -2
  64. data/spec/toy/persistence_spec.rb +88 -30
  65. data/spec/toy/reference_spec.rb +0 -1
  66. data/spec/toy/references_spec.rb +20 -0
  67. data/spec/toy/reloadable_spec.rb +81 -0
  68. data/spec/toy/serialization_spec.rb +1 -110
  69. data/spec/toy/validations_spec.rb +0 -21
  70. data/spec/toy_spec.rb +4 -5
  71. data/test/lint_test.rb +1 -1
  72. metadata +21 -26
  73. data/.autotest +0 -11
  74. data/LOGGING.rdoc +0 -12
  75. data/README.rdoc +0 -27
  76. data/examples/models.rb +0 -51
  77. data/lib/toy/dolly.rb +0 -30
  78. data/lib/toy/embedded_list.rb +0 -45
  79. data/lib/toy/embedded_lists.rb +0 -68
  80. data/lib/toy/index.rb +0 -74
  81. data/lib/toy/indices.rb +0 -56
  82. data/lib/toy/proxies/embedded_list.rb +0 -79
  83. data/spec/toy/dolly_spec.rb +0 -76
  84. data/spec/toy/embedded_list_spec.rb +0 -607
  85. data/spec/toy/embedded_lists_spec.rb +0 -172
  86. data/spec/toy/index_spec.rb +0 -230
  87. data/spec/toy/indices_spec.rb +0 -141
  88. 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
@@ -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
@@ -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