Browse files

Ensuring that all hash values get set.

Previously if you set one value, it would not try to set the others as
id? would return true, since the hash was not blank once it had a
single key. This overrides id? to ensure that it returns false if any
of the values are blank.

Really need to introduce a new type here...
  • Loading branch information...
1 parent 5c0555e commit 147496475cfd70030f89364765b9c2f8054093da @jnunemaker committed Nov 19, 2012
Showing with 72 additions and 60 deletions.
  1. +22 −8 lib/toy/identity/hash_key_factory.rb
  2. +50 −52 spec/toy/identity/hash_key_factory_spec.rb
View
30 lib/toy/identity/hash_key_factory.rb
@@ -1,17 +1,24 @@
module Toy
module Identity
class HashKeyFactory < AbstractKeyFactory
+ # Internal
+ attr_reader :attributes
+
+ # Internal
+ attr_reader :attribute_keys
+
def initialize(args = {})
@model = args.fetch(:model)
@attributes = args.fetch(:attributes)
+ @attribute_keys = @attributes.keys
@to_key = args.fetch(:to_key) { default_to_key }
@accessors_module = Module.new
@model.send :include, @accessors_module
override_id_writer_to_typecast_values
- define_virtual_attributes
- override_virtual_writers_to_update_id
+ override_id_presence_check_to_confirm_all_values
+ define_and_override_virtual_attributes
end
def key_type
@@ -38,6 +45,9 @@ def to_key(object)
# Private
def override_id_writer_to_typecast_values
+ # Forces id updates to change both id and virtual attributes.
+ # I hate this. I should probably make a new type that wraps Hash and
+ # does this type of stuff.
@accessors_module.module_eval <<-EOM
def id=(value)
hash = {}
@@ -50,16 +60,20 @@ def id=(value)
EOM
end
- # Private
- def define_virtual_attributes
- @attributes.each do |name, type|
- @model.attribute name, type, :virtual => true
- end
+ def override_id_presence_check_to_confirm_all_values
+ @accessors_module.module_eval <<-EOM
+ def id?
+ return false if key_factory.attribute_keys.any? { |key| id[key].blank? }
+ super
+ end
+ EOM
end
# Private
- def override_virtual_writers_to_update_id
+ def define_and_override_virtual_attributes
@attributes.each do |name, type|
+ @model.attribute name, type, :virtual => true
+
@accessors_module.module_eval <<-EOM
def #{name}=(value)
id_will_change!
View
102 spec/toy/identity/hash_key_factory_spec.rb
@@ -3,30 +3,15 @@
describe Toy::Identity::HashKeyFactory do
uses_objects('User')
- let(:no_default_type) {
- Class.new do
- def self.to_store(value, *)
- value
- end
-
- def self.from_store(value, *)
- value
- end
- end
- }
-
let(:bucket_type) {
Class.new do
- def self.store_default
- new('2012')
- end
-
def self.to_store(value, *)
value
end
def self.from_store(value, *)
return value if value.is_a?(self)
+ return value if value.nil?
new(value)
end
@@ -97,48 +82,30 @@ def self.from_store(value, *)
end
describe "#next_key" do
- context "with all types having store defaults" do
- it "generates key based on defaults" do
- bucket = bucket_type.new('2012')
- uuid = uuid_type.new
-
- bucket_type.should_receive(:store_default).and_return(bucket)
- uuid_type.should_receive(:store_default).and_return(uuid)
+ context "when record has no values for keys" do
+ before do
+ @key = subject.next_key(User.new)
+ end
- key = subject.next_key(User.new)
+ it "sets keys without store_default to nil" do
+ @key[:bucket].should be_nil
+ end
- key[:bucket].should eq(bucket)
- key[:uuid].should eq(uuid)
+ it "sets keys with store_default to default" do
+ @key[:uuid].should be_instance_of(SimpleUUID::UUID)
end
end
context "when record has value for keys already" do
- it "generates key based on set values" do
- bucket = bucket_type.new('2012')
- uuid = uuid_type.new
- key = subject.next_key(User.new(bucket: bucket, uuid: uuid))
-
- key[:bucket].should be(bucket)
- key[:uuid].should be(uuid)
+ before do
+ @bucket = bucket_type.new('2012')
+ @uuid = uuid_type.new
+ @key = subject.next_key(User.new(bucket: @bucket, uuid: @uuid))
end
- end
-
- context "when record has type without default and no value for that key" do
- subject {
- described_class.new(required_arguments.merge({
- attributes: {
- bucket: no_default_type,
- uuid: uuid_type,
- }
- }))
- }
- it "sets key to nil" do
- uuid = uuid_type.new
- key = subject.next_key(User.new(uuid: uuid))
-
- key[:bucket].should be_nil
- key[:uuid].should be(uuid)
+ it "sets keys to values" do
+ @key[:bucket].should be(@bucket)
+ @key[:uuid].should be(@uuid)
end
end
end
@@ -188,6 +155,37 @@ def self.from_store(value, *)
User.attributes['id'].type.should be(Hash)
end
+ describe "id?" do
+ it "returns false if any value is blank" do
+ user = User.new
+ user.bucket = nil
+ user.id?.should be_false
+ end
+
+ it "returns true if all values are present" do
+ user = User.new
+ user.bucket = bucket_type.new('2011')
+ user.uuid = uuid_type.new
+ user.id?.should be_true
+ end
+ end
+
+ context "initializing with only one virtual attribute" do
+ before do
+ @bucket = bucket_type.new('2011')
+ @user = User.new(bucket: @bucket)
+ end
+
+ it "sets virtual attribute" do
+ @user.bucket.should be(@bucket)
+ end
+
+ it "defaults unassigned virtual attributes" do
+ @user.id[:uuid].should be_instance_of(SimpleUUID::UUID)
+ @user.uuid.should be_instance_of(SimpleUUID::UUID)
+ end
+ end
+
context "assigning one of the Hash's virtual attributes" do
before do
@user = User.new
@@ -212,7 +210,7 @@ def self.from_store(value, *)
end
end
- context "updating id" do
+ context "assigning id" do
before do
@user = User.new
@bucket = bucket_type.new('2011')
@@ -230,7 +228,7 @@ def self.from_store(value, *)
end
end
- context "updating id with not type that gets typecast" do
+ context "assigning id with value that needs to be typecast" do
before do
@user = User.new
@bucket = bucket_type.new('2011')

0 comments on commit 1474964

Please sign in to comment.