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.
Files changed (113) hide show
  1. data/.autotest +11 -0
  2. data/.bundle/config +2 -0
  3. data/.gitignore +6 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +49 -0
  6. data/LICENSE +9 -0
  7. data/LOGGING.rdoc +16 -0
  8. data/README.rdoc +13 -0
  9. data/Rakefile +7 -0
  10. data/examples/memcached.rb +20 -0
  11. data/examples/memory.rb +20 -0
  12. data/examples/models.rb +51 -0
  13. data/examples/redis.rb +20 -0
  14. data/lib/toy.rb +81 -0
  15. data/lib/toy/attribute.rb +73 -0
  16. data/lib/toy/attributes.rb +137 -0
  17. data/lib/toy/caching.rb +20 -0
  18. data/lib/toy/callbacks.rb +48 -0
  19. data/lib/toy/collection.rb +55 -0
  20. data/lib/toy/connection.rb +28 -0
  21. data/lib/toy/dirty.rb +47 -0
  22. data/lib/toy/dolly.rb +30 -0
  23. data/lib/toy/embedded_list.rb +45 -0
  24. data/lib/toy/embedded_lists.rb +68 -0
  25. data/lib/toy/equality.rb +19 -0
  26. data/lib/toy/exceptions.rb +29 -0
  27. data/lib/toy/extensions/array.rb +22 -0
  28. data/lib/toy/extensions/boolean.rb +43 -0
  29. data/lib/toy/extensions/date.rb +24 -0
  30. data/lib/toy/extensions/float.rb +13 -0
  31. data/lib/toy/extensions/hash.rb +17 -0
  32. data/lib/toy/extensions/integer.rb +22 -0
  33. data/lib/toy/extensions/nil_class.rb +17 -0
  34. data/lib/toy/extensions/object.rb +26 -0
  35. data/lib/toy/extensions/set.rb +23 -0
  36. data/lib/toy/extensions/string.rb +17 -0
  37. data/lib/toy/extensions/time.rb +29 -0
  38. data/lib/toy/identity.rb +26 -0
  39. data/lib/toy/identity/abstract_key_factory.rb +10 -0
  40. data/lib/toy/identity/uuid_key_factory.rb +9 -0
  41. data/lib/toy/identity_map.rb +109 -0
  42. data/lib/toy/index.rb +74 -0
  43. data/lib/toy/indices.rb +56 -0
  44. data/lib/toy/inspect.rb +12 -0
  45. data/lib/toy/list.rb +46 -0
  46. data/lib/toy/lists.rb +37 -0
  47. data/lib/toy/logger.rb +26 -0
  48. data/lib/toy/mass_assignment_security.rb +16 -0
  49. data/lib/toy/persistence.rb +138 -0
  50. data/lib/toy/plugins.rb +23 -0
  51. data/lib/toy/proxies/embedded_list.rb +74 -0
  52. data/lib/toy/proxies/list.rb +97 -0
  53. data/lib/toy/proxies/proxy.rb +59 -0
  54. data/lib/toy/querying.rb +57 -0
  55. data/lib/toy/reference.rb +134 -0
  56. data/lib/toy/references.rb +19 -0
  57. data/lib/toy/serialization.rb +81 -0
  58. data/lib/toy/store.rb +36 -0
  59. data/lib/toy/timestamps.rb +22 -0
  60. data/lib/toy/validations.rb +45 -0
  61. data/lib/toy/version.rb +3 -0
  62. data/lib/toystore.rb +1 -0
  63. data/spec/helper.rb +35 -0
  64. data/spec/spec.opts +3 -0
  65. data/spec/support/constants.rb +41 -0
  66. data/spec/support/identity_map_matcher.rb +20 -0
  67. data/spec/support/name_and_number_key_factory.rb +5 -0
  68. data/spec/toy/attribute_spec.rb +176 -0
  69. data/spec/toy/attributes_spec.rb +394 -0
  70. data/spec/toy/caching_spec.rb +62 -0
  71. data/spec/toy/callbacks_spec.rb +97 -0
  72. data/spec/toy/connection_spec.rb +47 -0
  73. data/spec/toy/dirty_spec.rb +99 -0
  74. data/spec/toy/dolly_spec.rb +76 -0
  75. data/spec/toy/embedded_list_spec.rb +607 -0
  76. data/spec/toy/embedded_lists_spec.rb +172 -0
  77. data/spec/toy/equality_spec.rb +46 -0
  78. data/spec/toy/exceptions_spec.rb +18 -0
  79. data/spec/toy/extensions/array_spec.rb +25 -0
  80. data/spec/toy/extensions/boolean_spec.rb +41 -0
  81. data/spec/toy/extensions/date_spec.rb +48 -0
  82. data/spec/toy/extensions/float_spec.rb +14 -0
  83. data/spec/toy/extensions/hash_spec.rb +21 -0
  84. data/spec/toy/extensions/integer_spec.rb +29 -0
  85. data/spec/toy/extensions/nil_class_spec.rb +14 -0
  86. data/spec/toy/extensions/set_spec.rb +27 -0
  87. data/spec/toy/extensions/string_spec.rb +28 -0
  88. data/spec/toy/extensions/time_spec.rb +94 -0
  89. data/spec/toy/identity/abstract_key_factory_spec.rb +7 -0
  90. data/spec/toy/identity/uuid_key_factory_spec.rb +7 -0
  91. data/spec/toy/identity_map_spec.rb +150 -0
  92. data/spec/toy/identity_spec.rb +52 -0
  93. data/spec/toy/index_spec.rb +230 -0
  94. data/spec/toy/indices_spec.rb +141 -0
  95. data/spec/toy/inspect_spec.rb +15 -0
  96. data/spec/toy/list_spec.rb +576 -0
  97. data/spec/toy/lists_spec.rb +95 -0
  98. data/spec/toy/logger_spec.rb +33 -0
  99. data/spec/toy/mass_assignment_security_spec.rb +116 -0
  100. data/spec/toy/persistence_spec.rb +312 -0
  101. data/spec/toy/plugins_spec.rb +39 -0
  102. data/spec/toy/querying_spec.rb +162 -0
  103. data/spec/toy/reference_spec.rb +400 -0
  104. data/spec/toy/references_spec.rb +86 -0
  105. data/spec/toy/serialization_spec.rb +354 -0
  106. data/spec/toy/store_spec.rb +41 -0
  107. data/spec/toy/timestamps_spec.rb +63 -0
  108. data/spec/toy/validations_spec.rb +171 -0
  109. data/spec/toy_spec.rb +26 -0
  110. data/specs.watchr +52 -0
  111. data/test/lint_test.rb +40 -0
  112. data/toystore.gemspec +24 -0
  113. metadata +290 -0
@@ -0,0 +1,12 @@
1
+ module Toy
2
+ module Inspect
3
+ extend ActiveSupport::Concern
4
+
5
+ def inspect
6
+ attributes_as_nice_string = self.class.attributes.keys.map(&:to_s).sort.map do |name|
7
+ "#{name}: #{read_attribute(name).inspect}"
8
+ end.join(", ")
9
+ "#<#{self.class}:#{object_id} #{attributes_as_nice_string}>"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,46 @@
1
+ require 'toy/proxies/list'
2
+
3
+ module Toy
4
+ class List
5
+ include Toy::Collection
6
+
7
+ def after_initialize
8
+ model.attribute(key, Array)
9
+ create_accessors
10
+ end
11
+
12
+ def key
13
+ @key ||= :"#{name.to_s.singularize}_ids"
14
+ end
15
+
16
+ private
17
+ def proxy_class
18
+ Toy::Proxies::List
19
+ end
20
+
21
+ def list_method
22
+ :lists
23
+ end
24
+
25
+ def create_accessors
26
+ model.class_eval """
27
+ def #{name}
28
+ #{instance_variable} ||= self.class.#{list_method}[:#{name}].new_proxy(self)
29
+ end
30
+
31
+ def #{name}=(records)
32
+ #{name}.replace(records)
33
+ end
34
+ """
35
+
36
+ if options[:dependent]
37
+ model.class_eval """
38
+ after_destroy :destroy_#{name}
39
+ def destroy_#{name}
40
+ #{name}.each { |o| o.destroy }
41
+ end
42
+ """
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ module Toy
2
+ module Lists
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def lists
7
+ @lists ||= {}
8
+ end
9
+
10
+ def list?(key)
11
+ lists.keys.include?(key.to_sym)
12
+ end
13
+
14
+ # @examples
15
+ # list :games # assumes Game
16
+ # list :games, :dependent => true # assumes Game
17
+ # list :active_games, Game # uses Game
18
+ # list :active_games, Game, :dependent => true # uses Game
19
+ #
20
+ # list :games do
21
+ # def active
22
+ # target.select { |t| t.active? }
23
+ # end
24
+ # end
25
+ #
26
+ # module ActiveExtension
27
+ # def active
28
+ # target.select { |t| t.active? }
29
+ # end
30
+ # end
31
+ # list :games, :extensions => [ActiveExtension]
32
+ def list(name, *args, &block)
33
+ List.new(self, name, *args, &block)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module Toy
2
+ module Logger
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def logger
7
+ Toy.logger
8
+ end
9
+
10
+ def log_operation(operation, adapter, key, value)
11
+ logger.debug("ToyStore #{operation} :#{adapter.name} #{key.inspect}")
12
+ logger.debug(" #{value.inspect}")
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def logger
18
+ Toy.logger
19
+ end
20
+
21
+ def log_operation(*args)
22
+ self.class.log_operation(*args)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module Toy
2
+ module MassAssignmentSecurity
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::MassAssignmentSecurity
5
+
6
+ module InstanceMethods
7
+ def initialize(attrs={})
8
+ super(sanitize_for_mass_assignment(attrs || {}))
9
+ end
10
+
11
+ def update_attributes(attrs={})
12
+ super(sanitize_for_mass_assignment(attrs || {}))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,138 @@
1
+ module Toy
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def store(name=nil, client=nil, options=nil)
7
+ assert_client(name, client)
8
+ @store = Adapter[name].new(client, options) if !name.nil? && !client.nil?
9
+ assert_store(name, client, 'store')
10
+ @store
11
+ end
12
+
13
+ def has_store?
14
+ !@store.nil?
15
+ end
16
+
17
+ def cache(name=nil, client=nil)
18
+ assert_client(name, client)
19
+ @cache = Adapter[name].new(client) if !name.nil? && !client.nil?
20
+ assert_store(name, client, 'cache')
21
+ @cache
22
+ end
23
+
24
+ def has_cache?
25
+ !@cache.nil?
26
+ end
27
+
28
+ def namespace(new_namespace=nil)
29
+ if new_namespace.nil?
30
+ @namespace ||= self.name
31
+ else
32
+ @namespace = new_namespace
33
+ end
34
+
35
+ @namespace
36
+ end
37
+
38
+ def store_key(id)
39
+ [namespace, id].join(':')
40
+ end
41
+
42
+ def create(attrs={})
43
+ new(attrs).tap { |doc| doc.save }
44
+ end
45
+
46
+ def delete(*ids)
47
+ ids.each { |id| get(id).try(:delete) }
48
+ end
49
+
50
+ def destroy(*ids)
51
+ ids.each { |id| get(id).try(:destroy) }
52
+ end
53
+
54
+ private
55
+ def assert_client(name, client)
56
+ raise(ArgumentError, 'Client is required') if !name.nil? && client.nil?
57
+ end
58
+
59
+ def assert_store(name, client, which)
60
+ raise(StandardError, "No #{which} has been set") if name.nil? && client.nil? && !send(:"has_#{which}?")
61
+ end
62
+ end
63
+
64
+ module InstanceMethods
65
+ def store
66
+ self.class.store
67
+ end
68
+
69
+ def cache
70
+ self.class.cache
71
+ end
72
+
73
+ def store_key
74
+ self.class.store_key(id)
75
+ end
76
+
77
+ def new_record?
78
+ @_new_record == true
79
+ end
80
+
81
+ def destroyed?
82
+ @_destroyed == true
83
+ end
84
+
85
+ def persisted?
86
+ !new_record? && !destroyed?
87
+ end
88
+
89
+ def save(*)
90
+ new_record? ? create : update
91
+ end
92
+
93
+ def update_attributes(attrs)
94
+ self.attributes = attrs
95
+ save
96
+ end
97
+
98
+ def destroy
99
+ delete
100
+ end
101
+
102
+ def delete
103
+ key = store_key
104
+ @_destroyed = true
105
+ logger.debug("ToyStore DEL #{key.inspect}")
106
+ store.delete(key)
107
+ end
108
+
109
+ private
110
+ def create
111
+ persist!
112
+ end
113
+
114
+ def update
115
+ persist!
116
+ end
117
+
118
+ def persist
119
+ @_new_record = false
120
+ end
121
+
122
+ def persist!
123
+ key, attrs = store_key, persisted_attributes
124
+
125
+ if self.class.has_cache?
126
+ cache.write(key, attrs)
127
+ log_operation('WTS', cache, key, attrs)
128
+ end
129
+
130
+ store.write(key, attrs)
131
+ log_operation('SET', store, key, attrs)
132
+ persist
133
+ each_embedded_object(&:persist)
134
+ true
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,23 @@
1
+ module Toy
2
+ def models
3
+ @models ||= Set.new
4
+ end
5
+
6
+ def plugins
7
+ @plugins ||= Set.new
8
+ end
9
+
10
+ def plugin(mod)
11
+ Toy.models.each { |model| model.send(:include, mod) }
12
+ plugins << mod
13
+ end
14
+
15
+ module Plugins
16
+ extend ActiveSupport::Concern
17
+
18
+ included do
19
+ Toy.models << self
20
+ Toy.plugins.each { |mod| include mod }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,74 @@
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 = instance.is_a?(proxy_class) ? instance : proxy_class.load(instance)
38
+ assign_reference(instance)
39
+ end
40
+ end
41
+
42
+ def create(attrs={})
43
+ proxy_class.new(attrs).tap do |instance|
44
+ assign_reference(instance)
45
+ if instance.valid?
46
+ push(instance)
47
+ proxy_owner.save
48
+ end
49
+ end
50
+ end
51
+
52
+ def destroy(*args, &block)
53
+ ids = block_given? ? target.select(&block).map(&:id) : args.flatten
54
+ target.delete_if { |instance| ids.include?(instance.id) }
55
+ proxy_owner.save
56
+ end
57
+
58
+ def destroy_all
59
+ target.clear
60
+ proxy_owner.save
61
+ end
62
+
63
+ private
64
+ def find_target
65
+ []
66
+ end
67
+
68
+ def assign_reference(instance)
69
+ instance.parent_reference = proxy_owner
70
+ instance
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,97 @@
1
+ require 'toy/proxies/proxy'
2
+
3
+ module Toy
4
+ module Proxies
5
+ class List < Toy::Proxies::Proxy
6
+ def get(id)
7
+ if target_ids.include?(id)
8
+ proxy_class.get(id)
9
+ end
10
+ end
11
+
12
+ def get!(id)
13
+ get(id) || raise(Toy::NotFound.new(id))
14
+ end
15
+
16
+ def include?(record)
17
+ return false if record.nil?
18
+ target_ids.include?(record.id)
19
+ end
20
+
21
+ def push(record)
22
+ assert_type(record)
23
+ self.target_ids = target_ids + [record]
24
+ end
25
+ alias :<< :push
26
+
27
+ def concat(*records)
28
+ records = records.flatten
29
+ records.map { |record| assert_type(record) }
30
+ self.target_ids = target_ids + records
31
+ end
32
+
33
+ def replace(records)
34
+ reset
35
+ self.target_ids = records
36
+ end
37
+
38
+ def create(attrs={})
39
+ if proxy_options[:inverse_of]
40
+ attrs.update(:"#{proxy_options[:inverse_of]}_id" => proxy_owner.id)
41
+ end
42
+ proxy_class.create(attrs).tap do |record|
43
+ if record.persisted?
44
+ proxy_owner.reload
45
+ push(record)
46
+ proxy_owner.save
47
+ reset
48
+ end
49
+ end
50
+ end
51
+
52
+ def destroy(*args, &block)
53
+ ids = block_given? ? target.select(&block).map(&:id) : args.flatten
54
+ proxy_class.destroy(*ids)
55
+ proxy_owner.reload
56
+ self.target_ids = target_ids - ids
57
+ proxy_owner.save
58
+ reset
59
+ end
60
+
61
+ def destroy_all
62
+ proxy_class.destroy(*target_ids)
63
+ self.target_ids = []
64
+ proxy_owner.save
65
+ reset
66
+ end
67
+
68
+ def reset
69
+ @target = nil
70
+ end
71
+
72
+ private
73
+ def find_target
74
+ return [] if target_ids.blank?
75
+ proxy_class.get_multi(target_ids)
76
+ end
77
+
78
+ def target_ids
79
+ proxy_owner.send(proxy_key)
80
+ end
81
+
82
+ def target_ids=(value)
83
+ ids = value.map do |item|
84
+ if item.is_a?(proxy_class)
85
+ item.id
86
+ elsif item.is_a?(Hash)
87
+ item['id']
88
+ else
89
+ item
90
+ end
91
+ end
92
+ proxy_owner.send(:"#{proxy_key}=", ids)
93
+ reset
94
+ end
95
+ end
96
+ end
97
+ end