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
@@ -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