virtus 0.5.1 → 0.5.2

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