toystore 0.8.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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