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
@@ -14,8 +14,7 @@ module Toy
14
14
  options[:only] = Array.wrap(options[:only]).map(&:to_sym)
15
15
  options[:except] = Array.wrap(options[:except]).map(&:to_sym)
16
16
 
17
- serializable_stuff = serializable_attributes.map(&:to_sym) +
18
- self.class.embedded_lists.keys.map(&:to_sym)
17
+ serializable_stuff = serializable_attributes.map(&:to_sym)
19
18
 
20
19
  if options[:only].any?
21
20
  serializable_stuff &= options[:only]
@@ -48,35 +47,36 @@ module Toy
48
47
  end
49
48
 
50
49
  private
51
- # Add associations specified via the <tt>:includes</tt> option.
52
- # Expects a block that takes as arguments:
53
- # +association+ - name of the association
54
- # +records+ - the association record(s) to be serialized
55
- # +opts+ - options for the association records
56
- def serializable_add_includes(options = {})
57
- return unless include_associations = options.delete(:include)
58
50
 
59
- base_only_or_except = { :except => options[:except],
60
- :only => options[:only] }
51
+ # Add associations specified via the <tt>:includes</tt> option.
52
+ # Expects a block that takes as arguments:
53
+ # +association+ - name of the association
54
+ # +records+ - the association record(s) to be serialized
55
+ # +opts+ - options for the association records
56
+ def serializable_add_includes(options = {})
57
+ return unless include_associations = options.delete(:include)
61
58
 
62
- include_has_options = include_associations.is_a?(Hash)
63
- associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
59
+ base_only_or_except = { :except => options[:except],
60
+ :only => options[:only] }
64
61
 
65
- for association in associations
66
- records = if self.class.list?(association)
67
- send(association).to_a
68
- elsif self.class.reference?(association) || self.class.parent_reference?(association)
69
- send(association)
70
- end
62
+ include_has_options = include_associations.is_a?(Hash)
63
+ associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
71
64
 
72
- unless records.nil?
73
- association_options = include_has_options ? include_associations[association] : base_only_or_except
74
- opts = options.merge(association_options)
75
- yield(association, records, opts)
76
- end
65
+ for association in associations
66
+ records = if self.class.list?(association)
67
+ send(association).to_a
68
+ elsif self.class.reference?(association) || self.class.parent_reference?(association)
69
+ send(association)
77
70
  end
78
71
 
79
- options[:include] = include_associations
72
+ unless records.nil?
73
+ association_options = include_has_options ? include_associations[association] : base_only_or_except
74
+ opts = options.merge(association_options)
75
+ yield(association, records, opts)
76
+ end
80
77
  end
78
+
79
+ options[:include] = include_associations
80
+ end
81
81
  end
82
82
  end
data/lib/toy/store.rb CHANGED
@@ -4,17 +4,12 @@ module Toy
4
4
  extend Plugins
5
5
 
6
6
  included do
7
- extend ActiveModel::Naming
8
- include ActiveModel::Conversion
9
- include Attributes
10
- include Identity
7
+ include Toy::Object
11
8
  include Persistence
12
9
  include MassAssignmentSecurity
13
- include Dolly
14
- include Dirty
15
- include Equality
16
- include Inspect
10
+ include DirtyStore
17
11
  include Querying
12
+ include Reloadable
18
13
 
19
14
  include Callbacks
20
15
  include Validations
@@ -22,10 +17,7 @@ module Toy
22
17
  include Timestamps
23
18
 
24
19
  include Lists
25
- include EmbeddedLists
26
20
  include References
27
- include Indices
28
- include Logger
29
21
 
30
22
  include IdentityMap
31
23
  include Caching
@@ -9,41 +9,22 @@ module Toy
9
9
  end
10
10
 
11
11
  module ClassMethods
12
- def validates_embedded(*names)
13
- validates_each(*names) do |record, name, value|
14
- invalid = value.compact.select { |obj| !obj.valid? }
15
- if invalid.any?
16
- record.errors.add(name, 'is invalid')
17
-
18
- if logger && logger.debug?
19
- invalid_messages = []
20
- invalid.each do |obj|
21
- invalid_messages << [obj.attributes, obj.errors.full_messages]
22
- end
23
- log_operation(:iem, self.name, store, record.id, invalid_messages)
24
- end
25
- end
26
- end
27
- end
28
-
29
12
  def create!(attrs={})
30
13
  new(attrs).tap { |doc| doc.save! }
31
14
  end
32
15
  end
33
16
 
34
- module InstanceMethods
35
- def valid?
36
- run_callbacks(:validation) { super }
37
- end
17
+ def valid?
18
+ run_callbacks(:validation) { super }
19
+ end
38
20
 
39
- def save(options={})
40
- options.assert_valid_keys(:validate)
41
- !options.fetch(:validate, true) || valid? ? super : false
42
- end
21
+ def save(options={})
22
+ options.assert_valid_keys(:validate)
23
+ !options.fetch(:validate, true) || valid? ? super : false
24
+ end
43
25
 
44
- def save!
45
- save || raise(RecordInvalid.new(self))
46
- end
26
+ def save!
27
+ save || raise(RecordInvalid.new(self))
47
28
  end
48
29
  end
49
30
  end
data/lib/toy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Toy
2
- VERSION = "0.8.3"
2
+ VERSION = "0.9.0"
3
3
  end
data/perf/reads.rb CHANGED
@@ -12,8 +12,6 @@ Toy.logger = ::Logger.new(STDOUT).tap { |log| log.level = ::Logger::INFO }
12
12
 
13
13
  class User
14
14
  include Toy::Store
15
- identity_map_off
16
- store(:memory, {})
17
15
  attribute :name, String
18
16
  end
19
17
 
@@ -21,20 +19,20 @@ user = User.create(:name => 'John')
21
19
  id = user.id
22
20
  times = 10_000
23
21
 
24
- client_result = Benchmark.realtime {
25
- times.times { User.store.decode(User.store.client[User.store.key_for(id)]) }
22
+ adapter_result = Benchmark.realtime {
23
+ times.times { User.adapter.decode(User.adapter.client[User.adapter.key_for(id)]) }
26
24
  }
27
25
 
28
- store_result = Benchmark.realtime {
26
+ toystore_result = Benchmark.realtime {
29
27
  times.times { User.get(id) }
30
28
  }
31
29
 
32
- puts 'Client', client_result
33
- puts 'Toystore', store_result
34
- puts 'Ratio', store_result / client_result
30
+ puts 'Client', adapter_result
31
+ puts 'Toystore', toystore_result
32
+ puts 'Ratio', toystore_result / adapter_result
35
33
 
36
34
  # PerfTools::CpuProfiler.start('prof_client') do
37
- # times.times{ User.store.decode(User.store.client[User.store.key_for(id)]) }
35
+ # times.times{ User.adapter.decode(User.adapter.client[User.adapter.key_for(id)]) }
38
36
  # end
39
37
 
40
38
  # PerfTools::CpuProfiler.start('prof_reads') do
data/perf/writes.rb CHANGED
@@ -12,8 +12,6 @@ Toy.logger = ::Logger.new(STDOUT).tap { |log| log.level = ::Logger::INFO }
12
12
 
13
13
  class User
14
14
  include Toy::Store
15
- identity_map_off
16
- store(:memory, {})
17
15
  end
18
16
 
19
17
  times = 10_000
@@ -21,13 +19,13 @@ user = User.new
21
19
  id = user.id
22
20
  attrs = user.persisted_attributes
23
21
 
24
- client_result = Benchmark.realtime {
25
- times.times { User.store.write(id, attrs) }
22
+ adapter_result = Benchmark.realtime {
23
+ times.times { User.adapter.write(id, attrs) }
26
24
  }
27
- store_result = Benchmark.realtime {
25
+ toystore_result = Benchmark.realtime {
28
26
  times.times { User.create }
29
27
  }
30
28
 
31
- puts 'Client', client_result
32
- puts 'Toystore', store_result
33
- puts 'Ratio', store_result / client_result
29
+ puts 'Client', adapter_result
30
+ puts 'Toystore', toystore_result
31
+ puts 'Ratio', toystore_result / adapter_result
data/spec/helper.rb CHANGED
@@ -14,8 +14,8 @@ require 'bundler'
14
14
  Bundler.require(:default, :development)
15
15
 
16
16
  require 'toy'
17
- require 'adapter/memory'
18
17
  require 'support/constants'
18
+ require 'support/objects'
19
19
  require 'support/identity_map_matcher'
20
20
  require 'support/name_and_number_key_factory'
21
21
 
@@ -26,9 +26,11 @@ end
26
26
 
27
27
  RSpec.configure do |c|
28
28
  c.include(Support::Constants)
29
+ c.include(Support::Objects)
29
30
  c.include(IdentityMapMatcher)
30
31
 
31
32
  c.before(:each) do
33
+ Toy::IdentityMap.enabled = false
32
34
  Toy.clear
33
35
  Toy.reset
34
36
  Toy.key_factory = nil
@@ -1,8 +1,6 @@
1
1
  module Support
2
2
  module Constants
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- end
3
+ extend ActiveSupport::Concern
6
4
 
7
5
  module ClassMethods
8
6
  def uses_constants(*constants)
@@ -34,7 +32,6 @@ module Support
34
32
  def self.to_s; '#{name}' end
35
33
  """ if name
36
34
  model.send(:include, Toy::Store)
37
- model.store(:memory, {})
38
35
  end
39
36
  end
40
37
  end
@@ -1,16 +1,16 @@
1
1
  module IdentityMapMatcher
2
2
  class BeInIdentityMap
3
- def matches?(obj)
4
- @obj = obj
5
- @obj.identity_map[@obj.id] == @obj
3
+ def matches?(object)
4
+ @object = object
5
+ Toy::IdentityMap.include?(@object)
6
6
  end
7
7
 
8
8
  def failure_message
9
- "expected #{@obj} to be in identity map, but it was not"
9
+ "expected #{@object} to be in identity map, but it was not"
10
10
  end
11
11
 
12
12
  def negative_failure_message
13
- "expected #{@obj} to not be in identity map, but it was"
13
+ "expected #{@object} to not be in identity map, but it was"
14
14
  end
15
15
  end
16
16
 
@@ -0,0 +1,38 @@
1
+ module Support
2
+ module Objects
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def uses_objects(*objects)
7
+ before { create_objects(*objects) }
8
+ end
9
+ end
10
+
11
+ def create_objects(*objects)
12
+ objects.each { |object| create_object(object) }
13
+ end
14
+
15
+ def remove_objects(*objects)
16
+ objects.each { |object| remove_object(object) }
17
+ end
18
+
19
+ def create_object(object)
20
+ remove_object(object)
21
+ Kernel.const_set(object, Object(object))
22
+ end
23
+
24
+ def remove_object(object)
25
+ Kernel.send(:remove_const, object) if Kernel.const_defined?(object)
26
+ end
27
+
28
+ def Object(name=nil)
29
+ Class.new.tap do |object|
30
+ object.class_eval """
31
+ def self.name; '#{name}' end
32
+ def self.to_s; '#{name}' end
33
+ """ if name
34
+ object.send(:include, Toy::Object)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,7 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Toy::Attribute do
4
- uses_constants('User')
4
+ uses_objects('User')
5
5
 
6
6
  before do
7
7
  @attribute = Toy::Attribute.new(User, :age, String)
@@ -1,12 +1,7 @@
1
1
  require 'helper'
2
2
 
3
3
  describe Toy::Attributes do
4
- uses_constants('User', 'Game', 'Move', 'Tile')
5
-
6
- before do
7
- Game.embedded_list(:moves)
8
- Move.embedded_list(:tiles)
9
- end
4
+ uses_objects('User', 'Game')
10
5
 
11
6
  describe "including" do
12
7
  it "adds id attribute" do
@@ -22,7 +17,6 @@ describe Toy::Attributes do
22
17
 
23
18
  describe "#persisted_attributes" do
24
19
  before do
25
- Game.embedded_list(:moves)
26
20
  @over = Game.attribute(:over, Boolean)
27
21
  @score = Game.attribute(:creator_score, Integer, :virtual => true)
28
22
  @abbr = Game.attribute(:super_secret_hash, String, :abbr => :ssh)
@@ -32,7 +26,6 @@ describe Toy::Attributes do
32
26
  :creator_score => 20,
33
27
  :rewards => %w(twigs berries).to_set,
34
28
  :ssh => 'h4x',
35
- :moves => [Move.new, Move.new],
36
29
  })
37
30
  end
38
31
 
@@ -48,14 +41,6 @@ describe Toy::Attributes do
48
41
  @game.persisted_attributes.should_not have_key('super_secret_hash')
49
42
  end
50
43
 
51
- it "includes embedded" do
52
- @game.persisted_attributes.should have_key('moves')
53
- end
54
-
55
- it "includes ids of embedded" do
56
- @game.persisted_attributes['moves'][0].should have_key('id')
57
- end
58
-
59
44
  it "does not include virtual attributes" do
60
45
  @game.persisted_attributes.should_not have_key(:creator_score)
61
46
  end
@@ -130,48 +115,6 @@ describe Toy::Attributes do
130
115
  it "does not fail with nil" do
131
116
  User.new(nil).should be_instance_of(User)
132
117
  end
133
-
134
- it "does guard attributes=" do
135
- attrs = {'age' => 21}
136
- user = User.allocate
137
- user.should_receive(:attributes=).with(attrs, true)
138
- user.send(:initialize, attrs)
139
- end
140
- end
141
-
142
- describe "#initialize_from_database" do
143
- before do
144
- User.attribute(:age, Integer, :default => 20)
145
- @user = User.allocate
146
- end
147
-
148
- it "sets new record to false" do
149
- @user.initialize_from_database
150
- @user.should_not be_new_record
151
- end
152
-
153
- it "sets attributes" do
154
- @user.initialize_from_database('age' => 21)
155
- end
156
-
157
- it "sets defaults" do
158
- @user.initialize_from_database
159
- @user.age.should == 20
160
- end
161
-
162
- it "does not fail with nil" do
163
- @user.initialize_from_database(nil).should == @user
164
- end
165
-
166
- it "returns self" do
167
- @user.initialize_from_database.should == @user
168
- end
169
-
170
- it "does not guard attributes=" do
171
- attrs = {'age' => 21}
172
- @user.should_receive(:attributes=).with(attrs, false)
173
- @user.initialize_from_database(attrs)
174
- end
175
118
  end
176
119
 
177
120
  describe "#attributes" do
@@ -189,11 +132,6 @@ describe Toy::Attributes do
189
132
  'active' => true,
190
133
  }
191
134
  end
192
-
193
- it "does not include embedded documents" do
194
- game = Game.new(:moves => [Move.new(:tiles => [Tile.new])])
195
- game.attributes.should_not have_key('moves')
196
- end
197
135
  end
198
136
 
199
137
  describe "#attributes=" do
@@ -322,96 +260,6 @@ describe Toy::Attributes do
322
260
  user.tat = '1234'
323
261
  user.twitter_access_token.should == '1234'
324
262
  end
325
-
326
- it "persists to store using abbreviation" do
327
- user = User.create(:twitter_access_token => '1234')
328
- raw = user.store.read(user.id)
329
- raw['tat'].should == '1234'
330
- raw.should_not have_key('twitter_access_token')
331
- end
332
-
333
- it "loads from store correctly" do
334
- user = User.create(:twitter_access_token => '1234')
335
- user = User.get(user.id)
336
- user.twitter_access_token.should == '1234'
337
- user.tat.should == '1234'
338
- end
339
- end
340
-
341
- describe "#reload" do
342
- before do
343
- User.attribute(:name, String)
344
- @user = User.create(:name => 'John')
345
- end
346
- let(:user) { @user }
347
-
348
- it "reloads id from database" do
349
- id = user.id
350
- user.reload
351
- user.id.should == id
352
- end
353
-
354
- it "reloads record from the database" do
355
- user.name = 'Steve'
356
- user.reload
357
- user.name.should == 'John'
358
- end
359
-
360
- it "is still persisted" do
361
- user.should be_persisted
362
- user.reload
363
- user.should be_persisted
364
- end
365
-
366
- it "returns the record" do
367
- user.name = 'Steve'
368
- user.reload.should equal(user)
369
- end
370
-
371
- it "resets instance variables" do
372
- user.instance_variable_set("@foo", true)
373
- user.reload
374
- user.instance_variable_get("@foo").should be_nil
375
- end
376
-
377
- it "resets lists" do
378
- User.list(:games)
379
- game = Game.create
380
- user.update_attributes(:games => [game])
381
- user.games = []
382
- user.games.should == []
383
- user.reload
384
- user.games.should == [game]
385
- end
386
-
387
- it "resets references" do
388
- Game.reference(:user)
389
- game = Game.create(:user => user)
390
- game.user = nil
391
- game.user.should be_nil
392
- game.reload
393
- game.user.should == user
394
- end
395
-
396
- it "raises NotFound if does not exist" do
397
- user.destroy
398
- lambda { user.reload }.should raise_error(Toy::NotFound)
399
- end
400
-
401
- it "reloads defaults" do
402
- User.attribute(:skills, Array)
403
- @user.reload
404
- @user.skills.should == []
405
- end
406
-
407
- it "reloads attributes protected from mass assignment" do
408
- User.attribute(:admin, Boolean)
409
- User.attr_accessible(:name)
410
- user = User.new(:name => 'John')
411
- user.admin = true
412
- user.save
413
- user.reload.admin.should be_true
414
- end
415
263
  end
416
264
 
417
265
  describe "Initialization of array attributes" do