virtus 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.travis.yml +0 -1
  2. data/Changelog.md +10 -0
  3. data/README.md +1 -2
  4. data/config/flay.yml +1 -1
  5. data/config/site.reek +2 -1
  6. data/lib/virtus.rb +0 -1
  7. data/lib/virtus/attribute.rb +4 -3
  8. data/lib/virtus/attribute/boolean.rb +1 -1
  9. data/lib/virtus/attribute/embedded_value.rb +1 -1
  10. data/lib/virtus/attribute_set.rb +32 -1
  11. data/lib/virtus/class_inclusions.rb +1 -1
  12. data/lib/virtus/class_methods.rb +15 -23
  13. data/lib/virtus/coercion/string.rb +8 -4
  14. data/lib/virtus/extensions.rb +5 -6
  15. data/lib/virtus/instance_methods.rb +43 -2
  16. data/lib/virtus/module_extensions.rb +10 -2
  17. data/lib/virtus/value_object.rb +4 -3
  18. data/lib/virtus/version.rb +1 -1
  19. data/spec/integration/using_modules_spec.rb +6 -2
  20. data/spec/integration/virtus/value_object_spec.rb +1 -1
  21. data/spec/shared/freeze_method_behavior.rb +37 -0
  22. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +10 -0
  23. data/spec/unit/virtus/attribute_set/append_spec.rb +2 -0
  24. data/spec/unit/virtus/{attributes_accessor → attribute_set}/define_reader_method_spec.rb +2 -2
  25. data/spec/unit/virtus/{attributes_accessor → attribute_set}/define_writer_method_spec.rb +2 -2
  26. data/spec/unit/virtus/{attributes_accessor → attribute_set}/inspect_spec.rb +2 -2
  27. data/spec/unit/virtus/attribute_set/merge_spec.rb +2 -0
  28. data/spec/unit/virtus/attribute_set/reset_spec.rb +4 -0
  29. data/spec/unit/virtus/class_methods/attribute_spec.rb +1 -0
  30. data/spec/unit/virtus/class_methods/attributes_spec.rb +22 -0
  31. data/spec/unit/virtus/class_methods/inherited_spec.rb +3 -3
  32. data/spec/unit/virtus/coercion/string/class_methods/to_integer_spec.rb +6 -0
  33. data/spec/unit/virtus/instance_methods/freeze_spec.rb +64 -0
  34. data/spec/unit/virtus/module_extensions/attribute_spec.rb +31 -0
  35. data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +37 -10
  36. data/spec/unit/virtus/value_object/instance_methods/clone_spec.rb +21 -0
  37. data/tasks/metrics/heckle.rake +4 -3
  38. metadata +13 -10
  39. data/lib/virtus/attributes_accessor.rb +0 -68
  40. data/spec/unit/virtus/value_object/instance_methods/duplicates_spec.rb +0 -22
@@ -10,7 +10,6 @@ rvm:
10
10
  - rbx-18mode
11
11
  - rbx-19mode
12
12
  - ree
13
- - ruby-head
14
13
  - jruby-head
15
14
  notifications:
16
15
  email:
@@ -1,3 +1,13 @@
1
+ # v0.5.2 2012-09-01
2
+
3
+ * [feature] Object is now the default attribute type (dkubb)
4
+ * [fixed] Fix module inclusion problems (dkubb)
5
+ * [fixed] Evaluate default values when freezing an object (mbj)
6
+ * [fixed] String representation of a big integer is now properly coerced to an integer (greyblake)
7
+ * [changed] AttributeSet is now a module responsible for defining attribute methods (emmanuel)
8
+
9
+ [Compare v0.5.1..v0.5.2](https://github.com/solnic/virtus/compare/v0.5.1...v0.5.2)
10
+
1
11
  # v0.5.1 2012-06-11
2
12
 
3
13
  * [fixed] EV properly handle nil as the value (solnic)
data/README.md CHANGED
@@ -3,8 +3,7 @@ virtus
3
3
 
4
4
  [![Build Status](https://secure.travis-ci.org/solnic/virtus.png)](http://travis-ci.org/solnic/virtus)
5
5
  [![Dependency Status](https://gemnasium.com/solnic/virtus.png)](https://gemnasium.com/solnic/virtus)
6
-
7
- [Metrics on CodeClimate](https://codeclimate.com/github/solnic/virtus)
6
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/solnic/virtus)
8
7
 
9
8
  This is a partial extraction of the DataMapper [Property
10
9
  API](http://rubydoc.info/github/datamapper/dm-core/master/DataMapper/Property)
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 19
3
- total_score: 385
3
+ total_score: 425
@@ -31,7 +31,8 @@ FeatureEnvy:
31
31
  Virtus::ClassMethods#build_attribute,
32
32
  Virtus::Coercion::TimeCoercions#to_string,
33
33
  Virtus::Coercion::TimeCoercions#coerce_with_method,
34
- Virtus::TypeLookup#determine_type_from_primitive
34
+ Virtus::TypeLookup#determine_type_from_primitive,
35
+ Virtus::ValueObject::ClassMethods#attribute
35
36
  ]
36
37
  enabled: true
37
38
  ClassVariable:
@@ -56,7 +56,6 @@ require 'virtus/extensions'
56
56
  require 'virtus/class_inclusions'
57
57
  require 'virtus/module_extensions'
58
58
 
59
- require 'virtus/attributes_accessor'
60
59
  require 'virtus/class_methods'
61
60
  require 'virtus/instance_methods'
62
61
 
@@ -7,6 +7,7 @@ module Virtus
7
7
  extend DescendantsTracker
8
8
  extend TypeLookup
9
9
  extend Options
10
+ include Equalizer.new(inspect) << :name << :options
10
11
 
11
12
  accept_options :primitive, :accessor, :reader,
12
13
  :writer, :coercion_method, :default
@@ -50,15 +51,15 @@ module Virtus
50
51
  # the name of an attribute
51
52
  #
52
53
  # @param [Class] type
53
- # the type class of an attribute
54
+ # optional type class of an attribute
54
55
  #
55
56
  # @param [#to_hash] options
56
- # the extra options hash
57
+ # optional extra options hash
57
58
  #
58
59
  # @return [Attribute]
59
60
  #
60
61
  # @api private
61
- def self.build(name, type, options = {})
62
+ def self.build(name, type = Object, options = {})
62
63
  attribute_class = determine_type(type) or
63
64
  raise ArgumentError, "#{type.inspect} does not map to an attribute type"
64
65
  attribute_options = attribute_class.merge_options(type, options)
@@ -1,7 +1,7 @@
1
1
  module Virtus
2
2
  class Attribute
3
3
 
4
- # Bolean attribute allows true or false values to be set
4
+ # Boolean attribute allows true or false values to be set
5
5
  # Additionally it adds boolean reader method, like "admin?"
6
6
  #
7
7
  # @example
@@ -44,7 +44,7 @@ module Virtus
44
44
  #
45
45
  # @return [Virtus::Attribute::EmbeddedValue]
46
46
  #
47
- # @api public
47
+ # @api private
48
48
  def self.determine_type(klass)
49
49
  if klass <= Virtus || klass <= OpenStruct
50
50
  FromOpenStruct
@@ -1,7 +1,7 @@
1
1
  module Virtus
2
2
 
3
3
  # A set of Attribute objects
4
- class AttributeSet
4
+ class AttributeSet < Module
5
5
  include Enumerable
6
6
 
7
7
  # Initialize an AttributeSet
@@ -66,6 +66,7 @@ module Virtus
66
66
  # @api public
67
67
  def <<(attribute)
68
68
  self[attribute.name] = attribute
69
+ attribute.define_accessor_methods(self)
69
70
  self
70
71
  end
71
72
 
@@ -110,6 +111,36 @@ module Virtus
110
111
  self
111
112
  end
112
113
 
114
+ # Defines an attribute reader method
115
+ #
116
+ # @param [Attribute] attribute
117
+ # @param [Symbol] method_name
118
+ # @param [Symbol] visibility
119
+ #
120
+ # @return [self]
121
+ #
122
+ # @api private
123
+ def define_reader_method(attribute, method_name, visibility)
124
+ define_method(method_name) { attribute.get(self) }
125
+ send(visibility, method_name)
126
+ self
127
+ end
128
+
129
+ # Defines an attribute writer method
130
+ #
131
+ # @param [Attribute] attribute
132
+ # @param [Symbol] method_name
133
+ # @param [Symbol] visibility
134
+ #
135
+ # @return [self]
136
+ #
137
+ # @api private
138
+ def define_writer_method(attribute, method_name, visibility)
139
+ define_method(method_name) { |value| attribute.set(self, value) }
140
+ send(visibility, method_name)
141
+ self
142
+ end
143
+
113
144
  private
114
145
 
115
146
  # Merge the attributes into the index
@@ -13,7 +13,7 @@ module Virtus
13
13
  def self.included(descendant)
14
14
  super
15
15
  descendant.extend(ClassMethods)
16
- descendant.send(:include, InstanceMethods)
16
+ descendant.class_eval { include InstanceMethods }
17
17
  end
18
18
  private_class_method :included
19
19
 
@@ -15,7 +15,7 @@ module Virtus
15
15
  super
16
16
  descendant.module_eval do
17
17
  extend DescendantsTracker
18
- virtus_setup_attributes_accessor_module
18
+ include attribute_set
19
19
  end
20
20
  end
21
21
 
@@ -31,7 +31,7 @@ module Virtus
31
31
  # attribute :age, Integer
32
32
  # end
33
33
  #
34
- # User.attributes # =>
34
+ # User.attribute_set # =>
35
35
  #
36
36
  # TODO: implement inspect so the output is not cluttered - solnic
37
37
  #
@@ -56,16 +56,15 @@ module Virtus
56
56
  attribute_set
57
57
  end
58
58
 
59
- protected
60
-
61
- # Set up the anonymous module which will host Attribute accessor methods
59
+ # Hooks into const missing process to determine types of attributes
62
60
  #
63
- # @return [self]
61
+ # @param [String] name
62
+ #
63
+ # @return [Class]
64
64
  #
65
65
  # @api private
66
- def virtus_setup_attributes_accessor_module
67
- @virtus_attributes_accessor_module = AttributesAccessor.new(inspect)
68
- include @virtus_attributes_accessor_module
66
+ def const_missing(name)
67
+ Attribute.determine_type(name) or super
69
68
  end
70
69
 
71
70
  private
@@ -85,18 +84,7 @@ module Virtus
85
84
  # @api private
86
85
  def inherited(descendant)
87
86
  super
88
- descendant.virtus_setup_attributes_accessor_module
89
- end
90
-
91
- # Hooks into const missing process to determine types of attributes
92
- #
93
- # @param [String] name
94
- #
95
- # @return [Class]
96
- #
97
- # @api private
98
- def const_missing(name)
99
- Attribute.determine_type(name) or super
87
+ descendant.module_eval { include attribute_set }
100
88
  end
101
89
 
102
90
  # Add the attribute to the class' and descendants' attributes
@@ -111,9 +99,13 @@ module Virtus
111
99
  descendants.each { |descendant| descendant.attribute_set.reset }
112
100
  end
113
101
 
102
+ # The list of allowed public methods
103
+ #
104
+ # @return [Array<String>]
105
+ #
114
106
  # @api private
115
- def public_method_list
116
- public_instance_methods
107
+ def allowed_methods
108
+ public_instance_methods.map(&:to_s)
117
109
  end
118
110
 
119
111
  end # module ClassMethods
@@ -133,10 +133,14 @@ module Virtus
133
133
  #
134
134
  # @api public
135
135
  def self.to_integer(value)
136
- # coerce to a Float first to evaluate scientific notation (if any)
137
- # that may change the integer part, then convert to an integer
138
- coerced = to_float(value)
139
- ::Float === coerced ? coerced.to_i : coerced
136
+ if value =~ /\A#{INTEGER_REGEXP}\z/
137
+ value.to_i
138
+ else
139
+ # coerce to a Float first to evaluate scientific notation (if any)
140
+ # that may change the integer part, then convert to an integer
141
+ coerced = to_float(value)
142
+ ::Float === coerced ? coerced.to_i : coerced
143
+ end
140
144
  end
141
145
 
142
146
  # Coerce value to float
@@ -13,10 +13,10 @@ module Virtus
13
13
  #
14
14
  # @api private
15
15
  def self.extended(object)
16
- object.extend(InstanceMethods)
16
+ super
17
17
  object.instance_eval do
18
- @virtus_attributes_accessor_module = AttributesAccessor.new(object.class.inspect)
19
- extend @virtus_attributes_accessor_module
18
+ extend InstanceMethods
19
+ extend attribute_set
20
20
  end
21
21
  end
22
22
  private_class_method :extended
@@ -31,6 +31,7 @@ module Virtus
31
31
  # attribute :author, String
32
32
  # attribute :published_at, DateTime
33
33
  # attribute :page_count, Integer
34
+ # attribute :index # defaults to Object
34
35
  # end
35
36
  #
36
37
  # @param [Symbol] name
@@ -49,7 +50,6 @@ module Virtus
49
50
  # @api public
50
51
  def attribute(*args)
51
52
  attribute = Attribute.build(*args)
52
- attribute.define_accessor_methods(@virtus_attributes_accessor_module)
53
53
  virtus_add_attribute(attribute)
54
54
  self
55
55
  end
@@ -62,8 +62,7 @@ module Virtus
62
62
  def allowed_writer_methods
63
63
  @allowed_writer_methods ||=
64
64
  begin
65
- allowed_writer_methods = public_method_list.map(&:to_s)
66
- allowed_writer_methods = allowed_writer_methods.grep(WRITER_METHOD_REGEXP).to_set
65
+ allowed_writer_methods = allowed_methods.grep(WRITER_METHOD_REGEXP).to_set
67
66
  allowed_writer_methods -= INVALID_WRITER_METHODS
68
67
  allowed_writer_methods.freeze
69
68
  end
@@ -132,6 +132,32 @@ module Virtus
132
132
  attributes
133
133
  end
134
134
 
135
+ # Freeze object
136
+ #
137
+ # @return [self]
138
+ #
139
+ # @api public
140
+ #
141
+ # @example
142
+ #
143
+ # class User
144
+ # include Virtus
145
+ #
146
+ # attribute :name, String
147
+ # attribute :age, Integer
148
+ # end
149
+ #
150
+ # user = User.new(:name => 'John', :age => 28)
151
+ # user.frozen? # => false
152
+ # user.freeze
153
+ # user.frozen? # => true
154
+ #
155
+ # @api public
156
+ def freeze
157
+ set_defaults
158
+ super
159
+ end
160
+
135
161
  private
136
162
 
137
163
  # Get values of all attributes defined for this class, ignoring privacy
@@ -146,6 +172,17 @@ module Virtus
146
172
  end
147
173
  end
148
174
 
175
+ # Ensure all defaults are set
176
+ #
177
+ # @return [AttributeSet]
178
+ #
179
+ # @api private
180
+ def set_defaults
181
+ attribute_set.each do |attribute|
182
+ get_attribute(attribute.name)
183
+ end
184
+ end
185
+
149
186
  # Mass-assign attribute values
150
187
  #
151
188
  # @see Virtus::InstanceMethods#attributes=
@@ -181,9 +218,13 @@ module Virtus
181
218
  __send__("#{name}=", value)
182
219
  end
183
220
 
221
+ # The list of allowed public methods
222
+ #
223
+ # @return [Array<String>]
224
+ #
184
225
  # @api private
185
- def public_method_list
186
- public_methods
226
+ def allowed_methods
227
+ public_methods.map(&:to_s)
187
228
  end
188
229
 
189
230
  end # module InstanceMethods
@@ -4,8 +4,16 @@ module Virtus
4
4
  #
5
5
  module ModuleExtensions
6
6
 
7
+ # Define an attribute in the module
8
+ #
9
+ # @see Virtus::Extensions#attribute
10
+ #
11
+ # @return [self]
12
+ #
13
+ # @api private
7
14
  def attribute(*args)
8
15
  attribute_definitions << args
16
+ self
9
17
  end
10
18
 
11
19
  private
@@ -32,7 +40,7 @@ module Virtus
32
40
  # @api private
33
41
  def included(object)
34
42
  super
35
- object.send(:include, ClassInclusions)
43
+ object.module_eval { include Virtus }
36
44
  define_attributes(object)
37
45
  end
38
46
 
@@ -58,5 +66,5 @@ module Virtus
58
66
  end
59
67
  end
60
68
 
61
- end # class ModuleExtensions
69
+ end # module ModuleExtensions
62
70
  end # module Virtus
@@ -35,6 +35,7 @@ module Virtus
35
35
  include ::Virtus
36
36
  include InstanceMethods
37
37
  extend ClassMethods
38
+ private :attributes=
38
39
  end
39
40
  end
40
41
 
@@ -93,10 +94,10 @@ module Virtus
93
94
  # @return [self]
94
95
  #
95
96
  # @api public
96
- def attribute(name, type, options = {})
97
+ def attribute(name, *args)
97
98
  equalizer << name
98
- options[:writer] = :private
99
- super
99
+ options = args.last.kind_of?(Hash) ? args.pop : {}
100
+ super name, *args << options.merge(:writer => :private)
100
101
  end
101
102
 
102
103
  # Define and include a module that provides Value Object semantics
@@ -1,3 +1,3 @@
1
1
  module Virtus
2
- VERSION = '0.5.1'
2
+ VERSION = '0.5.2'
3
3
  end
@@ -3,14 +3,18 @@ require 'spec_helper'
3
3
  describe 'I can define attributes within a module' do
4
4
  before do
5
5
  module Examples
6
- module Name
6
+ module Common
7
7
  include Virtus
8
+ end
9
+
10
+ module Name
11
+ include Common
8
12
 
9
13
  attribute :name, String
10
14
  end
11
15
 
12
16
  module Age
13
- include Virtus
17
+ include Common
14
18
 
15
19
  attribute :age, Integer
16
20
  end
@@ -40,7 +40,7 @@ describe Virtus::ValueObject do
40
40
  it 'writer methods are set to private' do
41
41
  private_methods = class_under_test.private_instance_methods
42
42
  private_methods.map! { |m| m.to_s }
43
- private_methods.should include('latitude=', 'longitude=')
43
+ private_methods.should include('latitude=', 'longitude=', 'attributes=')
44
44
  end
45
45
 
46
46
  it 'attempts to call attribute writer methods raises NameError' do
@@ -0,0 +1,37 @@
1
+ shared_examples_for 'a #freeze method' do
2
+ let(:sample_exception) do
3
+ begin
4
+ object.dup.freeze.instance_variable_set(:@foo,:bar)
5
+ rescue => exception
6
+ exception
7
+ end
8
+ end
9
+
10
+ let(:expected_exception_class) do
11
+ # Ruby 1.8 blows up with TypeError Ruby 1.9 with RuntimeError
12
+ sample_exception.class
13
+ end
14
+
15
+ let(:expected_exception_message) do
16
+ # Ruby 1.8 blows up with a different message than Ruby 1.9
17
+ sample_exception.message
18
+ end
19
+
20
+ it_should_behave_like 'an idempotent method'
21
+
22
+ it 'returns object' do
23
+ should be(object)
24
+ end
25
+
26
+ it 'prevents future modifications' do
27
+ subject
28
+ expectation = raise_error(expected_exception_class,expected_exception_message)
29
+ expect { object.instance_variable_set(:@foo,:bar) }.to(expectation)
30
+ end
31
+
32
+ its(:frozen?) { should be(true) }
33
+
34
+ it 'allows to access attribute' do
35
+ subject.name.should eql('John')
36
+ end
37
+ end
@@ -27,6 +27,16 @@ describe Virtus::Attribute, '.build' do
27
27
  its(:options) { should == Virtus::Attribute::String.options }
28
28
  end
29
29
 
30
+ context 'without a type' do
31
+ subject { object.build(name) }
32
+
33
+ it { should be_instance_of(Virtus::Attribute::Object) }
34
+
35
+ its(:name) { should be(name) }
36
+
37
+ its(:options) { should == Virtus::Attribute::Object.options }
38
+ end
39
+
30
40
  context 'with an invalid type' do
31
41
  subject { object.build(name, type) }
32
42
 
@@ -8,6 +8,8 @@ describe Virtus::AttributeSet, '#<<' do
8
8
  let(:object) { described_class.new(parent, attributes) }
9
9
  let(:name) { :name }
10
10
 
11
+ before { attribute.stub(:define_accessor_methods) }
12
+
11
13
  context 'with a new attribute' do
12
14
  let(:attribute) { mock('Attribute', :name => name) }
13
15
 
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Virtus::AttributesAccessor, '#define_reader_method' do
4
- subject { described_class.new('Test') }
3
+ describe Virtus::AttributeSet, '#define_reader_method' do
4
+ subject { described_class.new }
5
5
 
6
6
  let(:attribute) { mock('attribute') }
7
7
 
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Virtus::AttributesAccessor, '#define_writer_method' do
4
- subject { described_class.new('Test') }
3
+ describe Virtus::AttributeSet, '#define_writer_method' do
4
+ subject { described_class.new }
5
5
 
6
6
  let(:attribute) { mock('attribute') }
7
7
 
@@ -1,9 +1,9 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Virtus::AttributesAccessor, '#inspect' do
3
+ describe Virtus::AttributeSet, '#inspect' do
4
4
  subject { object.inspect }
5
5
 
6
6
  let(:object) { described_class.new('Test') }
7
7
 
8
- it { should eql('Test::AttributesAccessor') }
8
+ it { pending; should eql('Test::AttributeSet') }
9
9
  end
@@ -9,6 +9,8 @@ describe Virtus::AttributeSet, '#merge' do
9
9
  let(:name) { :name }
10
10
  let(:other) { [ attribute ] }
11
11
 
12
+ before { attribute.stub(:define_accessor_methods) }
13
+
12
14
  context 'with a new attribute' do
13
15
  let(:attribute) { mock('Attribute', :name => name) }
14
16
 
@@ -8,6 +8,8 @@ describe Virtus::AttributeSet, '#reset' do
8
8
  let(:attributes) { [ attribute ] }
9
9
  let(:object) { described_class.new(parent, attributes) }
10
10
 
11
+ before { attribute.stub(:define_accessor_methods) }
12
+
11
13
  context 'when the parent has no attributes' do
12
14
  let(:parent) { described_class.new }
13
15
 
@@ -39,6 +41,8 @@ describe Virtus::AttributeSet, '#reset' do
39
41
  let(:parent) { described_class.new([ parent_attribute ]) }
40
42
  let(:new_attribute) { mock('New Attribute', :name => :parent_name) }
41
43
 
44
+ before { new_attribute.stub(:define_accessor_methods) }
45
+
42
46
  it { should equal(object) }
43
47
 
44
48
  it 'includes changes from the parent' do
@@ -13,6 +13,7 @@ describe Virtus::ClassMethods, '#attribute' do
13
13
  attributes[name].should be_nil
14
14
  subject
15
15
  attribute = attributes[name]
16
+ attribute.should_not be_nil
16
17
  attribute.name.should be(name)
17
18
  attribute.class.should be(attribute_class)
18
19
  end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::ClassMethods, '#attributes' do
4
+ subject { object.attributes }
5
+
6
+ before do
7
+ @original_stderr, $stderr = $stderr, StringIO.new
8
+ end
9
+
10
+ after do
11
+ $stderr = @original_stderr
12
+ end
13
+
14
+ let(:object) { Class.new { extend Virtus::ClassMethods } }
15
+
16
+ it { should be_instance_of(Virtus::AttributeSet) }
17
+
18
+ it 'returns a deprecation warning' do
19
+ subject
20
+ $stderr.string.should =~ /\A#{object}.attributes is deprecated. Use #{object}.attribute_set instead: #{__FILE__}:4\b/
21
+ end
22
+ end
@@ -5,15 +5,15 @@ describe Virtus::ClassMethods, '#inherited' do
5
5
 
6
6
  let(:object) { Class.new { extend Virtus::ClassMethods } }
7
7
 
8
- it 'includes an AttributesAccessor module' do
8
+ it 'includes an AttributeSet module' do
9
9
  descendant = subject
10
10
 
11
11
  # return the descendant's attribute accessor modules (superclass + own)
12
- modules = descendant.ancestors.grep(Virtus::AttributesAccessor)
12
+ modules = descendant.ancestors.grep(Virtus::AttributeSet)
13
13
  modules.size.should be(2)
14
14
 
15
15
  # remove the superclass' attribute accessor module
16
- modules -= object.ancestors.grep(Virtus::AttributesAccessor)
16
+ modules -= object.ancestors.grep(Virtus::AttributeSet)
17
17
 
18
18
  # the descendant should have it's own attribute accessor module
19
19
  modules.size.should be(1)
@@ -47,4 +47,10 @@ describe Virtus::Coercion::String, '.to_integer' do
47
47
 
48
48
  it { should equal(string) }
49
49
  end
50
+
51
+ context 'when integer string is big' do
52
+ let(:string) { '334490140000101135' }
53
+
54
+ it { should == 334490140000101135 }
55
+ end
50
56
  end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::InstanceMethods, '#freeze' do
4
+ subject { object.freeze }
5
+
6
+ let(:object) do
7
+ described_class.new(attributes)
8
+ end
9
+
10
+ context 'on class with no defaults' do
11
+ let(:described_class) do
12
+ Class.new do
13
+ include Virtus
14
+ attribute :name, String
15
+ end
16
+ end
17
+
18
+ let(:attributes) { { :name => 'John' } }
19
+
20
+ it_should_behave_like 'a #freeze method'
21
+ end
22
+
23
+ context 'on class with literal default' do
24
+ let(:described_class) do
25
+ Class.new do
26
+ include Virtus
27
+ attribute :name, String, :default => 'John'
28
+ end
29
+ end
30
+
31
+ context 'when value is provided' do
32
+ let(:attributes) { { :name => 'John' } }
33
+
34
+ it_should_behave_like 'a #freeze method'
35
+ end
36
+
37
+ context 'when value is NOT provided' do
38
+ let(:attributes) {}
39
+
40
+ it_should_behave_like 'a #freeze method'
41
+ end
42
+ end
43
+
44
+ context 'on class with computed default' do
45
+ let(:described_class) do
46
+ Class.new do
47
+ include Virtus
48
+ attribute :name, String, :default => proc { 'John' }
49
+ end
50
+ end
51
+
52
+ context 'when value is provided' do
53
+ let(:attributes) { { :name => 'John' } }
54
+
55
+ it_should_behave_like 'a #freeze method'
56
+ end
57
+
58
+ context 'when value is NOT provided' do
59
+ let(:attributes) {}
60
+
61
+ it_should_behave_like 'a #freeze method'
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::ModuleExtensions, '#attribute' do
4
+ subject { object.attribute(name, type, options) }
5
+
6
+ let(:object) { Module.new }
7
+ let(:name) { :name }
8
+ let(:type) { String }
9
+ let(:options) { { :default => default } }
10
+ let(:default) { 'John Doe'.freeze }
11
+
12
+ before do
13
+ object.extend(Virtus::ModuleExtensions)
14
+ end
15
+
16
+ it { should be(object) }
17
+
18
+ it 'tracks the attribute for extension' do
19
+ subject
20
+ instance = Object.new
21
+ instance.extend(object)
22
+ instance.attributes[name].should eql(default)
23
+ end
24
+
25
+ it 'tracks the attribute for inclusion' do
26
+ subject
27
+ klass = Class.new
28
+ klass.send(:include, object)
29
+ klass.attribute_set[name].should eql(Virtus::Attribute::String.new(name, options))
30
+ end
31
+ end
@@ -1,23 +1,50 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::ValueObject, '.attribute' do
4
- subject { object.attribute(name, type) }
5
-
6
4
  let(:object) { Class.new { include Virtus::ValueObject } }
7
5
  let(:name) { :latitude }
8
6
  let(:type) { Float }
9
7
  let(:attribute) { object.attribute_set[name] }
10
8
 
11
- it { should be(object) }
9
+ context 'without options' do
10
+ subject { object.attribute(name, type) }
11
+
12
+ it { should be(object) }
12
13
 
13
- it 'adds the attribute to the equalizer' do
14
- object.new.inspect.should_not match(/\b#{name}=\b/)
15
- subject
16
- object.new.inspect.should match(/\b#{name}=\b/)
14
+ it 'adds the attribute to the equalizer' do
15
+ object.new.inspect.should_not match(/\b#{name}=\b/)
16
+ subject
17
+ object.new.inspect.should match(/\b#{name}=\b/)
18
+ end
19
+
20
+ it 'sets the writer to be private' do
21
+ subject
22
+ attribute.options[:writer].should be(:private)
23
+ end
17
24
  end
18
25
 
19
- it 'sets the writer to be private' do
20
- subject
21
- attribute.options[:writer].should be(:private)
26
+ context 'with options' do
27
+ subject { object.attribute(name, type, options) }
28
+
29
+ let(:options) { { :default => default } }
30
+ let(:default) { 1.0 }
31
+
32
+ it { should be(object) }
33
+
34
+ it 'adds the attribute to the equalizer' do
35
+ object.new.inspect.should_not match(/\b#{name}=\b/)
36
+ subject
37
+ object.new.inspect.should match(/\b#{name}=\b/)
38
+ end
39
+
40
+ it 'sets the writer to be private' do
41
+ subject
42
+ attribute.options[:writer].should be(:private)
43
+ end
44
+
45
+ it 'sets the default' do
46
+ subject
47
+ attribute.default.value.should eql(1.0)
48
+ end
22
49
  end
23
50
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ [ :clone, :dup ].each do |method|
4
+ describe Virtus::ValueObject::InstanceMethods, "##{method}" do
5
+ subject { object.send(method) }
6
+
7
+ let(:object) { described_class.new }
8
+
9
+ let(:described_class) do
10
+ Class.new do
11
+ include Virtus::ValueObject
12
+
13
+ attribute :name, String
14
+ end
15
+ end
16
+
17
+ it 'returns the same instance' do
18
+ should equal(object)
19
+ end
20
+ end
21
+ end
@@ -45,7 +45,10 @@ begin
45
45
  end
46
46
 
47
47
  aliases = Hash.new { |h,mod| h[mod] = Hash.new { |h,method| h[method] = method } }
48
- map = NameMap.new
48
+
49
+ aliases['Virtus::ValueObject::InstanceMethods']['dup'] = 'clone'
50
+
51
+ map = NameMap.new
49
52
 
50
53
  heckle_caught_modules = Hash.new { |hash, key| hash[key] = [] }
51
54
  unhandled_mutations = 0
@@ -111,7 +114,6 @@ begin
111
114
 
112
115
  unless spec_file.file?
113
116
  raise "No spec file #{spec_file} for #{mod}.#{method}"
114
- next
115
117
  end
116
118
 
117
119
  specs << [ ".#{method}", [ spec_file ] ]
@@ -126,7 +128,6 @@ begin
126
128
 
127
129
  unless spec_file.file?
128
130
  raise "No spec file #{spec_file} for #{mod}##{method}"
129
- next
130
131
  end
131
132
 
132
133
  specs << [ "##{method}", [ spec_file ] ]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: virtus
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 5
9
- - 1
10
- version: 0.5.1
9
+ - 2
10
+ version: 0.5.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Piotr Solnica
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-06-11 00:00:00 Z
18
+ date: 2012-09-01 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: backports
@@ -136,7 +136,6 @@ files:
136
136
  - lib/virtus/attribute/symbol.rb
137
137
  - lib/virtus/attribute/time.rb
138
138
  - lib/virtus/attribute_set.rb
139
- - lib/virtus/attributes_accessor.rb
140
139
  - lib/virtus/class_inclusions.rb
141
140
  - lib/virtus/class_methods.rb
142
141
  - lib/virtus/coercion.rb
@@ -179,6 +178,7 @@ files:
179
178
  - spec/integration/virtus/value_object_spec.rb
180
179
  - spec/rcov.opts
181
180
  - spec/shared/constants_helpers.rb
181
+ - spec/shared/freeze_method_behavior.rb
182
182
  - spec/shared/idempotent_method_behaviour.rb
183
183
  - spec/shared/options_class_method.rb
184
184
  - spec/spec_helper.rb
@@ -247,16 +247,17 @@ files:
247
247
  - spec/unit/virtus/attribute/time/coerce_spec.rb
248
248
  - spec/unit/virtus/attribute/value_coerced_spec.rb
249
249
  - spec/unit/virtus/attribute_set/append_spec.rb
250
+ - spec/unit/virtus/attribute_set/define_reader_method_spec.rb
251
+ - spec/unit/virtus/attribute_set/define_writer_method_spec.rb
250
252
  - spec/unit/virtus/attribute_set/each_spec.rb
251
253
  - spec/unit/virtus/attribute_set/element_reference_spec.rb
252
254
  - spec/unit/virtus/attribute_set/element_set_spec.rb
255
+ - spec/unit/virtus/attribute_set/inspect_spec.rb
253
256
  - spec/unit/virtus/attribute_set/merge_spec.rb
254
257
  - spec/unit/virtus/attribute_set/reset_spec.rb
255
- - spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb
256
- - spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb
257
- - spec/unit/virtus/attributes_accessor/inspect_spec.rb
258
258
  - spec/unit/virtus/class_methods/attribute_set_spec.rb
259
259
  - spec/unit/virtus/class_methods/attribute_spec.rb
260
+ - spec/unit/virtus/class_methods/attributes_spec.rb
260
261
  - spec/unit/virtus/class_methods/const_missing_spec.rb
261
262
  - spec/unit/virtus/class_methods/inherited_spec.rb
262
263
  - spec/unit/virtus/coercion/array/class_methods/to_set_spec.rb
@@ -323,8 +324,10 @@ files:
323
324
  - spec/unit/virtus/instance_methods/attributes_spec.rb
324
325
  - spec/unit/virtus/instance_methods/element_reference_spec.rb
325
326
  - spec/unit/virtus/instance_methods/element_set_spec.rb
327
+ - spec/unit/virtus/instance_methods/freeze_spec.rb
326
328
  - spec/unit/virtus/instance_methods/initialize_spec.rb
327
329
  - spec/unit/virtus/instance_methods/to_hash_spec.rb
330
+ - spec/unit/virtus/module_extensions/attribute_spec.rb
328
331
  - spec/unit/virtus/options/accept_options_spec.rb
329
332
  - spec/unit/virtus/options/accepted_options_spec.rb
330
333
  - spec/unit/virtus/options/options_spec.rb
@@ -335,7 +338,7 @@ files:
335
338
  - spec/unit/virtus/value_object/class_methods/attribute_spec.rb
336
339
  - spec/unit/virtus/value_object/class_methods/equalizer_spec.rb
337
340
  - spec/unit/virtus/value_object/initialize_spec.rb
338
- - spec/unit/virtus/value_object/instance_methods/duplicates_spec.rb
341
+ - spec/unit/virtus/value_object/instance_methods/clone_spec.rb
339
342
  - spec/unit/virtus/value_object/instance_methods/with_spec.rb
340
343
  - tasks/metrics/ci.rake
341
344
  - tasks/metrics/flay.rake
@@ -377,7 +380,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
377
380
  requirements: []
378
381
 
379
382
  rubyforge_project:
380
- rubygems_version: 1.8.17
383
+ rubygems_version: 1.8.15
381
384
  signing_key:
382
385
  specification_version: 3
383
386
  summary: Attributes on Steroids for Plain Old Ruby Objects
@@ -1,68 +0,0 @@
1
- module Virtus
2
-
3
- # Host attribute accessor methods
4
- class AttributesAccessor < Module
5
-
6
- # Initialize a module for hosting Attribute access methods
7
- #
8
- # @param [Symbol, String] name
9
- #
10
- # @api private
11
- def initialize(name)
12
- super()
13
- @name = name
14
- end
15
-
16
- # Defines an attribute reader method
17
- #
18
- # @param [Attribute] attribute
19
- # @param [Symbol] method_name
20
- # @param [Symbol] visibility
21
- #
22
- # @return [self]
23
- #
24
- # @api private
25
- def define_reader_method(attribute, method_name, visibility)
26
- define_method(method_name) { attribute.get(self) }
27
- send(visibility, method_name)
28
- self
29
- end
30
-
31
- # Defines an attribute writer method
32
- #
33
- # @param [Attribute] attribute
34
- # @param [Symbol] method_name
35
- # @param [Symbol] visibility
36
- #
37
- # @return [self]
38
- #
39
- # @api private
40
- def define_writer_method(attribute, method_name, visibility)
41
- define_method(method_name) { |value| attribute.set(self, value) }
42
- send(visibility, method_name)
43
- self
44
- end
45
-
46
- # The inspect value of this Module
47
- #
48
- # This provides meaningful output when inspecting the ancestors
49
- # of a class/module that includes this module
50
- #
51
- # @example
52
- #
53
- # class ClassWithAttributes
54
- # include Virtus
55
- # end
56
- #
57
- # mod = ClassWithAttributes.send(:virtus_setup_attributes_accessor_module)
58
- # mod.inspect
59
- #
60
- # @return [String]
61
- #
62
- # @api public
63
- def inspect
64
- "#{@name}::AttributesAccessor"
65
- end
66
-
67
- end
68
- end
@@ -1,22 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Virtus::ValueObject::InstanceMethods, 'duplication' do
4
- let(:described_class) do
5
- Class.new do
6
- include Virtus::ValueObject
7
-
8
- attribute :name, String
9
- end
10
- end
11
-
12
- subject { described_class.new }
13
-
14
- it '#clone returns the same instance' do
15
- subject.should equal(subject.clone)
16
- end
17
-
18
- it '#dup returns the same instance' do
19
- subject.should equal(subject.dup)
20
- end
21
-
22
- end