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/lib/toy/caching.rb CHANGED
@@ -2,19 +2,17 @@ module Toy
2
2
  module Caching
3
3
  extend ActiveSupport::Concern
4
4
 
5
- module InstanceMethods
6
- def cache_key(*suffixes)
7
- cache_key = case
8
- when new_record?
9
- "#{self.class.name}:new"
10
- when timestamp = self[:updated_at]
11
- "#{self.class.name}:#{id}-#{timestamp.utc.to_s(:number)}"
12
- else
13
- "#{self.class.name}:#{id}"
14
- end
15
- cache_key += ":#{suffixes.join(':')}" unless suffixes.empty?
16
- cache_key
17
- end
5
+ def cache_key(*suffixes)
6
+ cache_key = case
7
+ when new_record?
8
+ "#{self.class.name}:new"
9
+ when timestamp = self[:updated_at]
10
+ "#{self.class.name}:#{id}-#{timestamp.utc.to_s(:number)}"
11
+ else
12
+ "#{self.class.name}:#{id}"
13
+ end
14
+ cache_key += ":#{suffixes.join(':')}" unless suffixes.empty?
15
+ cache_key
18
16
  end
19
17
  end
20
18
  end
data/lib/toy/callbacks.rb CHANGED
@@ -8,41 +8,22 @@ module Toy
8
8
  define_model_callbacks :save, :create, :update, :destroy
9
9
  end
10
10
 
11
- module InstanceMethods
12
- def save(*)
13
- run_callbacks(:save) { super }
14
- end
15
-
16
- def destroy
17
- run_callbacks(:destroy) { super }
18
- end
19
-
20
- def run_callbacks(callback, &block)
21
- callback = :create if callback == :update && !persisted?
22
-
23
- embedded_records = self.class.embedded_lists.keys.inject([]) do |records, key|
24
- records += send(key).target
25
- end
11
+ def save(*)
12
+ run_callbacks(:save) { super }
13
+ end
26
14
 
27
- block = embedded_records.inject(block) do |chain, record|
28
- if record.class.respond_to?("_#{callback}_callbacks")
29
- lambda { record.run_callbacks(callback, &chain) }
30
- else
31
- chain
32
- end
33
- end
34
- super callback, &block
35
- end
15
+ def destroy
16
+ run_callbacks(:destroy) { super }
17
+ end
36
18
 
37
- private
19
+ private
38
20
 
39
- def create
40
- run_callbacks(:create) { super }
41
- end
21
+ def create
22
+ run_callbacks(:create) { super }
23
+ end
42
24
 
43
- def update
44
- run_callbacks(:update) { super }
45
- end
25
+ def update
26
+ run_callbacks(:update) { super }
46
27
  end
47
28
  end
48
29
  end
@@ -0,0 +1,20 @@
1
+ module Toy
2
+ module Cloneable
3
+ extend ActiveSupport::Concern
4
+
5
+ def initialize_copy(other)
6
+ instance_variables.each do |name|
7
+ instance_variable_set(name, nil)
8
+ end
9
+
10
+ @attributes = {}
11
+
12
+ other.attributes.except('id').each do |key, value|
13
+ value = value.duplicable? ? value.clone : value
14
+ send("#{key}=", value)
15
+ end
16
+
17
+ write_attribute(:id, self.class.next_key(self))
18
+ end
19
+ end
20
+ end
@@ -42,14 +42,15 @@ module Toy
42
42
  alias :== :eql?
43
43
 
44
44
  private
45
- def proxy_class
46
- raise('Not Implemented')
47
- end
48
45
 
49
- def modularized_extensions(*extensions)
50
- extensions.flatten.compact.map do |extension|
51
- Proc === extension ? Module.new(&extension) : extension
52
- end
46
+ def proxy_class
47
+ raise('Not Implemented')
48
+ end
49
+
50
+ def modularized_extensions(*extensions)
51
+ extensions.flatten.compact.map do |extension|
52
+ Proc === extension ? Module.new(&extension) : extension
53
53
  end
54
+ end
54
55
  end
55
56
  end
data/lib/toy/dirty.rb CHANGED
@@ -2,46 +2,27 @@ module Toy
2
2
  module Dirty
3
3
  extend ActiveSupport::Concern
4
4
  include ActiveModel::Dirty
5
+ include Attributes
6
+ include Cloneable
5
7
 
6
- module InstanceMethods
7
- def initialize(*)
8
- super
9
- if new_record?
10
- # never register initial id assignment as a change
11
- @changed_attributes.delete('id')
12
- else
13
- @previously_changed = {}
14
- @changed_attributes.clear if @changed_attributes
15
- end
16
- end
17
-
18
- def initialize_copy(*)
19
- super.tap do
20
- @previously_changed = {}
21
- @changed_attributes = {}
22
- end
23
- end
24
-
25
- def reload
26
- super.tap do
27
- @previously_changed = {}
28
- @changed_attributes = {}
29
- end
30
- end
8
+ def initialize(*)
9
+ super
10
+ # never register initial id assignment as a change
11
+ @changed_attributes.delete('id') if @changed_attributes
12
+ end
31
13
 
32
- def save(*)
33
- super.tap do
34
- @previously_changed = changes
35
- @changed_attributes.clear if @changed_attributes
36
- end
14
+ def initialize_copy(*)
15
+ super.tap do
16
+ @previously_changed = {}
17
+ @changed_attributes = {}
37
18
  end
19
+ end
38
20
 
39
- def write_attribute(name, value)
40
- name = name.to_s
41
- current = read_attribute(name)
42
- attribute_will_change!(name) if current != value
43
- super
44
- end
21
+ def write_attribute(name, value)
22
+ name = name.to_s
23
+ current = read_attribute(name)
24
+ attribute_will_change!(name) if current != value
25
+ super
45
26
  end
46
27
  end
47
28
  end
@@ -0,0 +1,32 @@
1
+ module Toy
2
+ module DirtyStore
3
+ extend ActiveSupport::Concern
4
+ include ActiveModel::Dirty
5
+ include Reloadable
6
+ include Persistence
7
+ include Querying
8
+
9
+ def initialize_from_database(*)
10
+ super
11
+
12
+ @previously_changed = {}
13
+ @changed_attributes.clear if @changed_attributes
14
+
15
+ self
16
+ end
17
+
18
+ def reload
19
+ super.tap do
20
+ @previously_changed = {}
21
+ @changed_attributes = {}
22
+ end
23
+ end
24
+
25
+ def save(*)
26
+ super.tap do
27
+ @previously_changed = changes
28
+ @changed_attributes.clear if @changed_attributes
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/toy/equality.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  module Toy
2
2
  module Equality
3
+ extend ActiveSupport::Concern
4
+
3
5
  def eql?(other)
4
6
  return true if self.class.eql?(other.class) && id == other.id
5
7
  return true if other.respond_to?(:target) &&
@@ -2,24 +2,28 @@ module Toy
2
2
  module Extensions
3
3
  module Boolean
4
4
  Mapping = {
5
- true => true,
6
- 'true' => true,
7
- 'TRUE' => true,
8
- 'True' => true,
9
- 't' => true,
10
- 'T' => true,
11
- '1' => true,
12
- 1 => true,
5
+ true => true,
6
+ 'true' => true,
7
+ 'TRUE' => true,
8
+ 'True' => true,
9
+ 't' => true,
10
+ 'T' => true,
11
+ '1' => true,
12
+ 'on' => true,
13
+ 'ON' => true,
14
+ 1 => true,
13
15
  1.0 => true,
14
- false => false,
15
- 'false' => false,
16
- 'FALSE' => false,
17
- 'False' => false,
18
- 'f' => false,
19
- 'F' => false,
20
- '0' => false,
21
- 0 => false,
22
- 0.0 => false,
16
+ false => false,
17
+ 'false' => false,
18
+ 'FALSE' => false,
19
+ 'False' => false,
20
+ 'f' => false,
21
+ 'F' => false,
22
+ '0' => false,
23
+ 'off' => false,
24
+ 'OFF' => false,
25
+ 0 => false,
26
+ 0.0 => false,
23
27
  nil => nil
24
28
  }
25
29
 
@@ -32,7 +36,7 @@ module Toy
32
36
  end
33
37
 
34
38
  def from_store(value, *)
35
- value.nil? ? nil : !!value
39
+ to_store(value)
36
40
  end
37
41
  end
38
42
  end
@@ -1,62 +1,62 @@
1
1
  module Toy
2
- def self.identity_map
3
- Thread.current[:toystore_identity_map] ||= {}
4
- end
5
2
 
6
3
  module IdentityMap
7
4
  extend ActiveSupport::Concern
8
5
 
9
- included do
10
- identity_map_on
6
+ def self.enabled=(flag)
7
+ Thread.current[:toystore_identity_map_enabled] = flag
11
8
  end
12
9
 
13
- module ClassMethods
14
- def identity_map
15
- Toy.identity_map
16
- end
10
+ def self.enabled
11
+ Thread.current[:toystore_identity_map_enabled]
12
+ end
17
13
 
18
- def identity_map_on?
19
- @identity_map_on == true
20
- end
14
+ def self.enabled?
15
+ enabled == true
16
+ end
21
17
 
22
- def identity_map_off?
23
- !identity_map_on?
24
- end
18
+ def self.repository
19
+ Thread.current[:toystore_identity_map] ||= {}
20
+ end
25
21
 
26
- def identity_map_on
27
- @identity_map_on = true
28
- end
22
+ def self.clear
23
+ repository.clear
24
+ end
29
25
 
30
- def identity_map_off
31
- @identity_map_on = false
32
- end
26
+ def self.include?(object)
27
+ repository.keys.include?(object.id)
28
+ end
33
29
 
34
- def without_identity_map(&block)
35
- begin
36
- original_identity_map_on = @identity_map_on
37
- identity_map_off
38
- yield
39
- ensure
40
- @identity_map_on = original_identity_map_on
41
- end
42
- end
30
+ def self.use
31
+ old, self.enabled = enabled, true
43
32
 
33
+ yield if block_given?
34
+ ensure
35
+ self.enabled = old
36
+ clear
37
+ end
38
+
39
+ def self.without
40
+ old, self.enabled = enabled, false
41
+
42
+ yield if block_given?
43
+ ensure
44
+ self.enabled = old
45
+ end
46
+
47
+ module ClassMethods
44
48
  def get(id)
45
49
  get_from_identity_map(id) || super
46
50
  end
47
51
 
48
52
  def get_from_identity_map(id)
49
- return nil unless identity_map_on?
50
- if record = identity_map[id]
51
- log_operation(:img, self.name, store, id)
52
- record
53
- end
53
+ IdentityMap.repository[id] if IdentityMap.enabled?
54
54
  end
55
55
 
56
56
  def load(id, attrs)
57
57
  return nil if attrs.nil?
58
58
 
59
- if instance = identity_map[id]
59
+ if IdentityMap.enabled? && instance = IdentityMap.repository[id]
60
60
  instance
61
61
  else
62
62
  super.tap { |doc| doc.add_to_identity_map }
@@ -64,14 +64,8 @@ module Toy
64
64
  end
65
65
  end
66
66
 
67
- def identity_map
68
- Toy.identity_map
69
- end
70
-
71
67
  def save(*)
72
- super.tap do |result|
73
- add_to_identity_map if result
74
- end
68
+ super.tap { |result| add_to_identity_map if result }
75
69
  end
76
70
 
77
71
  def delete(*)
@@ -79,28 +73,11 @@ module Toy
79
73
  end
80
74
 
81
75
  def add_to_identity_map
82
- return unless self.class.identity_map_on?
83
- identity_map[id] = self
84
- log_operation(:ims, self.class.name, store, id)
76
+ IdentityMap.repository[id] = self if IdentityMap.enabled?
85
77
  end
86
78
 
87
79
  def remove_from_identity_map
88
- return unless self.class.identity_map_on?
89
- identity_map.delete(id)
90
- log_operation(:imd, self.class.name, store, id)
80
+ IdentityMap.repository.delete(id) if IdentityMap.enabled?
91
81
  end
92
-
93
- private
94
- def has_embedded_objects?
95
- self.class.embedded_lists.any?
96
- end
97
-
98
- def each_embedded_object(&block)
99
- if has_embedded_objects?
100
- self.class.embedded_lists.keys.inject([]) do |objects, name|
101
- objects.concat(send(name).to_a.compact)
102
- end.each { |object| block.call(object) }
103
- end
104
- end
105
82
  end
106
83
  end
data/lib/toy/list.rb CHANGED
@@ -14,33 +14,34 @@ module Toy
14
14
  end
15
15
 
16
16
  private
17
- def proxy_class
18
- Toy::Proxies::List
19
- end
20
17
 
21
- def list_method
22
- :lists
23
- end
18
+ def proxy_class
19
+ Toy::Proxies::List
20
+ end
24
21
 
25
- def create_accessors
26
- model.class_eval """
27
- def #{name}
28
- #{instance_variable} ||= self.class.#{list_method}[:#{name}].new_proxy(self)
29
- end
22
+ def list_method
23
+ :lists
24
+ end
30
25
 
31
- def #{name}=(records)
32
- #{name}.replace(records)
33
- end
34
- """
26
+ def create_accessors
27
+ model.class_eval """
28
+ def #{name}
29
+ #{instance_variable} ||= self.class.#{list_method}[:#{name}].new_proxy(self)
30
+ end
35
31
 
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
- """
32
+ def #{name}=(records)
33
+ #{name}.replace(records)
43
34
  end
35
+ """
36
+
37
+ if options[:dependent]
38
+ model.class_eval """
39
+ after_destroy :destroy_#{name}
40
+ def destroy_#{name}
41
+ #{name}.each { |o| o.destroy }
42
+ end
43
+ """
44
44
  end
45
+ end
45
46
  end
46
47
  end