Permalink
Browse files

Merge pull request #105 from mbj/fix-read-defaults-after-freeze

Fix read defaults after freeze
  • Loading branch information...
dkubb committed Jul 21, 2012
2 parents f3af067 + 315d3bf commit 0aa0649b834fe98b7d07b4206f03b95838084ddd
@@ -132,6 +132,32 @@ def to_hash
attributes
end
+ # Freeze object
+ #
+ # @return [self]
+ #
+ # @api public
+ #
+ # @example
+ #
+ # class User
+ # include Virtus
+ #
+ # attribute :name, String
+ # attribute :age, Integer
+ # end
+ #
+ # user = User.new(:name => 'John', :age => 28)
+ # user.frozen? # => false
+ # user.freeze
+ # user.frozen? # => true
+ #
+ # @api public
+ def freeze
+ set_defaults
+ super
+ end
+
private
# Get values of all attributes defined for this class, ignoring privacy
@@ -146,6 +172,17 @@ def get_attributes
end
end
+ # Ensure all defaults are set
+ #
+ # @return [AttributeSet]
+ #
+ # @api private
+ def set_defaults
+ attribute_set.each do |attribute|
+ get_attribute(attribute.name)
+ end
+ end
+
# Mass-assign attribute values
#
# @see Virtus::InstanceMethods#attributes=
@@ -0,0 +1,37 @@
+shared_examples_for 'a #freeze method' do
+ let(:sample_exception) do
+ begin
+ object.dup.freeze.instance_variable_set(:@foo,:bar)
+ rescue => exception
+ exception
+ end
+ end
+
+ let(:expected_exception_class) do
+ # Ruby 1.8 blows up with TypeError Ruby 1.9 with RuntimeError
+ sample_exception.class
+ end
+
+ let(:expected_exception_message) do
+ # Ruby 1.8 blows up with a different message than Ruby 1.9
+ sample_exception.message
+ end
+
+ it_should_behave_like 'an idempotent method'
+
+ it 'returns object' do
+ should be(object)
+ end
+
+ it 'prevents future modifications' do
+ subject
+ expectation = raise_error(expected_exception_class,expected_exception_message)
+ expect { object.instance_variable_set(:@foo,:bar) }.to(expectation)
+ end
+
+ its(:frozen?) { should be(true) }
+
+ it 'allows to access attribute' do
+ subject.name.should eql('John')
+ end
+end
@@ -0,0 +1,64 @@
+require 'spec_helper'
+
+describe Virtus::InstanceMethods, '#freeze' do
+ subject { object.freeze }
+
+ let(:object) do
+ described_class.new(attributes)
+ end
+
+ context 'on class with no defaults' do
+ let(:described_class) do
+ Class.new do
+ include Virtus
+ attribute :name, String
+ end
+ end
+
+ let(:attributes) { { :name => 'John' } }
+
+ it_should_behave_like 'a #freeze method'
+ end
+
+ context 'on class with literal default' do
+ let(:described_class) do
+ Class.new do
+ include Virtus
+ attribute :name, String, :default => 'John'
+ end
+ end
+
+ context 'when value is provided' do
+ let(:attributes) { { :name => 'John' } }
+
+ it_should_behave_like 'a #freeze method'
+ end
+
+ context 'when value is NOT provided' do
+ let(:attributes) {}
+
+ it_should_behave_like 'a #freeze method'
+ end
+ end
+
+ context 'on class with computed default' do
+ let(:described_class) do
+ Class.new do
+ include Virtus
+ attribute :name, String, :default => proc { 'John' }
+ end
+ end
+
+ context 'when value is provided' do
+ let(:attributes) { { :name => 'John' } }
+
+ it_should_behave_like 'a #freeze method'
+ end
+
+ context 'when value is NOT provided' do
+ let(:attributes) {}
+
+ it_should_behave_like 'a #freeze method'
+ end
+ end
+end

0 comments on commit 0aa0649

Please sign in to comment.