toystore 0.5

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