virtus 0.3.0 → 0.4.0

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 (62) hide show
  1. data/Changelog.md +10 -2
  2. data/Guardfile +9 -0
  3. data/README.md +12 -8
  4. data/TODO +0 -35
  5. data/config/flay.yml +1 -1
  6. data/config/site.reek +3 -1
  7. data/lib/virtus.rb +1 -0
  8. data/lib/virtus/attribute.rb +1 -1
  9. data/lib/virtus/attribute/collection.rb +2 -2
  10. data/lib/virtus/attribute/default_value/from_symbol.rb +2 -2
  11. data/lib/virtus/attribute_set.rb +4 -17
  12. data/lib/virtus/attributes_accessor.rb +22 -20
  13. data/lib/virtus/class_methods.rb +1 -1
  14. data/lib/virtus/coercion/hash.rb +1 -1
  15. data/lib/virtus/coercion/object.rb +1 -1
  16. data/lib/virtus/coercion/string.rb +20 -7
  17. data/lib/virtus/coercion/time_coercions.rb +5 -1
  18. data/lib/virtus/instance_methods.rb +8 -8
  19. data/lib/virtus/support/type_lookup.rb +24 -8
  20. data/lib/virtus/value_object/equalizer.rb +3 -2
  21. data/lib/virtus/version.rb +1 -1
  22. data/spec/shared/options_class_method.rb +1 -1
  23. data/spec/spec_helper.rb +1 -1
  24. data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +1 -7
  25. data/spec/unit/virtus/attribute/coerce_spec.rb +2 -2
  26. data/spec/unit/virtus/attribute/collection/coerce_and_append_member_spec.rb +16 -0
  27. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +20 -7
  28. data/spec/unit/virtus/attribute/collection/member_coercion/coerce_and_append_member_spec.rb +18 -0
  29. data/spec/unit/virtus/attribute/collection/member_type_spec.rb +17 -0
  30. data/spec/unit/virtus/attribute/collection/new_collection_spec.rb +9 -0
  31. data/spec/unit/virtus/attribute/default_value/class_methods/build_spec.rb +25 -0
  32. data/spec/unit/virtus/attribute/default_value/evaluate_spec.rb +0 -43
  33. data/spec/unit/virtus/attribute/default_value/from_callable/class_methods/handle_spec.rb +20 -0
  34. data/spec/unit/virtus/attribute/default_value/from_callable/evaluate_spec.rb +21 -0
  35. data/spec/unit/virtus/attribute/default_value/from_clonable/class_methods/handle_spec.rb +20 -0
  36. data/spec/unit/virtus/attribute/default_value/from_clonable/evaluate_spec.rb +22 -0
  37. data/spec/unit/virtus/attribute/default_value/from_symbol/class_methods/handle_spec.rb +20 -0
  38. data/spec/unit/virtus/attribute/default_value/from_symbol/evaluate_spec.rb +18 -0
  39. data/spec/unit/virtus/attribute_set/element_set_spec.rb +5 -4
  40. data/spec/unit/virtus/attributes_accessor/define_reader_method_spec.rb +36 -0
  41. data/spec/unit/virtus/attributes_accessor/define_writer_method_spec.rb +36 -0
  42. data/spec/unit/virtus/coercion/array/{to_set_spec.rb → class_methods/to_set_spec.rb} +0 -0
  43. data/spec/unit/virtus/coercion/{class_name_reference_spec.rb → class_methods/element_reference_spec.rb} +0 -0
  44. data/spec/unit/virtus/coercion/class_methods/primitive_spec.rb +13 -0
  45. data/spec/unit/virtus/coercion/decimal/class_methods/to_decimal_spec.rb +10 -0
  46. data/spec/unit/virtus/coercion/float/class_methods/to_float_spec.rb +10 -0
  47. data/spec/unit/virtus/coercion/integer/class_methods/to_integer_spec.rb +10 -0
  48. data/spec/unit/virtus/coercion/numeric/class_methods/to_decimal_spec.rb +0 -0
  49. data/spec/unit/virtus/coercion/numeric/class_methods/to_float_spec.rb +0 -0
  50. data/spec/unit/virtus/coercion/numeric/class_methods/to_integer_spec.rb +0 -0
  51. data/spec/unit/virtus/coercion/numeric/class_methods/to_string_spec.rb +0 -0
  52. data/spec/unit/virtus/coercion/object/class_methods/to_string_spec.rb +1 -1
  53. data/spec/unit/virtus/coercion/string/class_methods/to_symbol_spec.rb +10 -0
  54. data/spec/unit/virtus/coercion/time/class_methods/to_integer_spec.rb +11 -0
  55. data/spec/unit/virtus/instance_methods/attributes_spec.rb +25 -0
  56. data/spec/unit/virtus/instance_methods/initialize_spec.rb +42 -0
  57. data/spec/unit/virtus/type_lookup/class_methods/extended_spec.rb +10 -0
  58. data/spec/unit/virtus/value_object/class_methods/attribute_spec.rb +16 -0
  59. data/spec/unit/virtus/value_object/equalizer/append_spec.rb +61 -0
  60. data/virtus.gemspec +4 -3
  61. metadata +80 -75
  62. data/spec/unit/virtus/attribute_set/parent_spec.rb +0 -11
@@ -24,6 +24,8 @@ module Virtus
24
24
  compile
25
25
  end
26
26
 
27
+ private
28
+
27
29
  # Compile the equalizer methods based on #keys
28
30
  #
29
31
  # @return [self]
@@ -37,8 +39,6 @@ module Virtus
37
39
  self
38
40
  end
39
41
 
40
- private
41
-
42
42
  # Define an inspect method that reports the values of the instance's keys
43
43
  #
44
44
  # @return [undefined]
@@ -122,3 +122,4 @@ module Virtus
122
122
  end # class Equalizer
123
123
  end # module ValueObject
124
124
  end # module Virtus
125
+
@@ -1,3 +1,3 @@
1
1
  module Virtus
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
@@ -13,7 +13,7 @@ shared_examples_for 'an options class method' do
13
13
  it { should equal(object) }
14
14
 
15
15
  it 'sets the default value for the class method' do
16
- expect { subject }.to change { object.send(method) }.from(nil).to(value)
16
+ expect { subject }.to change { object.send(method) }.from(default).to(value)
17
17
  end
18
18
  end
19
19
  end
@@ -13,7 +13,7 @@ require 'virtus'
13
13
  ENV['TZ'] = 'UTC'
14
14
 
15
15
  # require spec support files and shared behavior
16
- Dir[File.expand_path('../**/shared/**/*.rb', __FILE__)].each { |file| require file }
16
+ Dir[File.expand_path('../shared/**/*.rb', __FILE__)].each { |file| require file }
17
17
 
18
18
  RSpec.configure do |config|
19
19
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Virtus::Attribute, '.determine_type' do
4
4
  let(:object) { described_class }
5
5
 
6
- described_class.descendants.each do |attribute_class|
6
+ (described_class.descendants - [ Virtus::Attribute::Collection ]).each do |attribute_class|
7
7
  context "with class #{attribute_class.inspect}" do
8
8
  subject { object.determine_type(attribute_class) }
9
9
 
@@ -16,12 +16,6 @@ describe Virtus::Attribute, '.determine_type' do
16
16
  context "with #{attribute_class.inspect} and primitive #{primitive.inspect}" do
17
17
  subject { object.determine_type(primitive) }
18
18
 
19
- before do
20
- if [Virtus::Attribute::Collection].include? attribute_class
21
- pending
22
- end
23
- end
24
-
25
19
  it 'returns the corresponding attribute class' do
26
20
  should be(attribute_class)
27
21
  end
@@ -9,7 +9,7 @@ describe Virtus::Attribute, '#coerce' do
9
9
  let(:coercion_method) { stub('coercion_method') }
10
10
  let(:value) { mock('value', :class => value_class) }
11
11
  let(:value_class) { stub('value_class') }
12
- let(:coercer) { mock('coercer', :send => coerced) }
12
+ let(:coercer) { mock('coercer', :public_send => coerced) }
13
13
  let(:coerced) { stub('coerced') }
14
14
 
15
15
  before do
@@ -24,7 +24,7 @@ describe Virtus::Attribute, '#coerce' do
24
24
  end
25
25
 
26
26
  it 'asks the coercer to coerce the value' do
27
- coercer.should_receive(:send).with(coercion_method, value)
27
+ coercer.should_receive(:public_send).with(coercion_method, value)
28
28
  subject
29
29
  end
30
30
  end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Collection, '#coerce_and_append_member' do
4
+ subject { object.coerce_and_append_member(collection, entry) }
5
+
6
+ let(:object) { described_class.new('stuff') }
7
+ let(:collection) { mock('collection') }
8
+ let(:entry) { mock('entry') }
9
+
10
+ specify do
11
+ expect { subject }.to raise_error(
12
+ NotImplementedError,
13
+ "Virtus::Attribute::Collection#coerce_and_append_member has not been implemented"
14
+ )
15
+ end
16
+ end
@@ -1,26 +1,39 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::Attribute::Collection, '#coerce' do
4
- subject { attribute.coerce(value) }
4
+ subject { object.coerce(value) }
5
5
 
6
- let(:attribute) { Class.new(described_class).new(:things, :member_type => member_type) }
7
- let(:value) { [ entry ] }
8
- let(:entry) { mock('entry') }
9
- let(:member_type) { Virtus::Attribute::Object }
6
+ let(:described_class) { Class.new(Virtus::Attribute::Collection) }
7
+ let(:object) { described_class.new(:things) }
8
+ let(:value) { [ entry ] }
9
+ let(:entry) { mock('entry') }
10
10
 
11
11
  before do
12
+ described_class.primitive ::Array
12
13
  value.should_receive(:respond_to?).with(:inject).and_return(respond_to_inject)
13
14
  end
14
15
 
15
16
  context 'when coerced value responds to #inject' do
16
17
  let(:respond_to_inject) { true }
17
18
 
18
- specify { expect { subject }.to raise_error(NotImplementedError) }
19
+ context 'when the object has not implemented #coerce_and_append_member' do
20
+ specify { expect { subject }.to raise_error(NotImplementedError, "#{object.class}#coerce_and_append_member has not been implemented") }
21
+ end
22
+
23
+ context 'when the object has implemented #coerce_and_append_member' do
24
+ before do
25
+ def object.coerce_and_append_member(collection, entry)
26
+ collection << entry
27
+ end
28
+ end
29
+
30
+ it { should eql(value) }
31
+ end
19
32
  end
20
33
 
21
34
  context 'when coerced value does not respond to #inject' do
22
35
  let(:respond_to_inject) { false }
23
36
 
24
- specify { should eql(value) }
37
+ it { should eql(value) }
25
38
  end
26
39
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Collection::MemberCoercion, '#coerce_and_append_member' do
4
+ subject { object.coerce_and_append_member(collection, entry) }
5
+
6
+ let(:attribute_class) do
7
+ Class.new(Virtus::Attribute::Collection) do
8
+ include Virtus::Attribute::Collection::MemberCoercion
9
+ self
10
+ end
11
+ end
12
+
13
+ let(:object) { attribute_class.new('stuff', :member_type => Integer) }
14
+ let(:collection) { [] }
15
+ let(:entry) { '1' }
16
+
17
+ it { should eql([ 1 ]) }
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Collection, '#member_type' do
4
+ subject { object.member_type }
5
+
6
+ context 'when specified' do
7
+ let(:object) { Virtus::Attribute::Set.new('stuff', :member_type => Integer) }
8
+
9
+ it { should be(Integer) }
10
+ end
11
+
12
+ context 'when not specified' do
13
+ let(:object) { Virtus::Attribute::Set.new('stuff') }
14
+
15
+ it { should be(Virtus::Attribute::Object) }
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::Collection, '#new_collection' do
4
+ subject { object.new_collection }
5
+
6
+ let(:object) { Virtus::Attribute::Set.new('stuff') }
7
+
8
+ it { should be_instance_of(Set) }
9
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue, '.build' do
4
+ subject { described_class.build(attribute, default) }
5
+
6
+ let(:attribute) { mock('attribute') }
7
+
8
+ context 'when default is a symbol' do
9
+ let(:default) { :symbol }
10
+
11
+ it { should be_instance_of(Virtus::Attribute::DefaultValue::FromSymbol) }
12
+ end
13
+
14
+ context 'when default is a callable' do
15
+ let(:default) { Proc.new {} }
16
+
17
+ it { should be_instance_of(Virtus::Attribute::DefaultValue::FromCallable) }
18
+ end
19
+
20
+ context 'when default is a clonable' do
21
+ let(:default) { "I can be cloned" }
22
+
23
+ it { should be_instance_of(Virtus::Attribute::DefaultValue::FromClonable) }
24
+ end
25
+ end
@@ -9,49 +9,6 @@ describe Virtus::Attribute::DefaultValue, '#evaluate' do
9
9
  let(:instance) { mock('instance') }
10
10
  let(:response) { stub('response') }
11
11
 
12
- context 'when the value is callable' do
13
- before { value.stub(:call => response) }
14
-
15
- specify { object.should be_instance_of(Virtus::Attribute::DefaultValue::FromCallable) }
16
-
17
- it { should be(response) }
18
-
19
- it 'calls the value with the instance and attribute' do
20
- value.should_receive(:call).with(instance, attribute)
21
- subject
22
- end
23
- end
24
-
25
- context 'when the value is cloneable' do
26
- let(:clone) { stub('clone') }
27
-
28
- before { value.stub(:clone => clone) }
29
-
30
- specify { object.should be_instance_of(Virtus::Attribute::DefaultValue::FromClonable) }
31
-
32
- it { should be(clone) }
33
-
34
- it 'clones the value' do
35
- value.should_receive(:clone).with(no_args)
36
- subject
37
- end
38
- end
39
-
40
- context 'when the value is a method name' do
41
- let(:instance) { mock('instance', value => retval) }
42
- let(:value) { :set_default }
43
- let(:retval) { stub('retval') }
44
-
45
- specify { object.should be_instance_of(Virtus::Attribute::DefaultValue::FromSymbol) }
46
-
47
- it { should be(retval) }
48
-
49
- it 'calls the method' do
50
- instance.should_receive(value).with(no_args)
51
- subject
52
- end
53
- end
54
-
55
12
  # smallest number that is Bignum across major ruby impls
56
13
  bignum = 0x7fffffffffffffff + 1
57
14
 
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue::FromCallable, '.handle?' do
4
+ subject { described_class.handle?(attribute, default) }
5
+
6
+ let(:attribute) { mock('attribute') }
7
+
8
+ context 'with a callable' do
9
+ let(:default) { Proc.new {} }
10
+
11
+ it { should be(true) }
12
+ end
13
+
14
+ context 'with a non-callable' do
15
+ let(:default) { '' }
16
+
17
+ it { should be(false) }
18
+ end
19
+ end
20
+
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue::FromCallable, '#evaluate' do
4
+ subject { object.evaluate(instance) }
5
+
6
+ let(:object) { described_class.new(attribute, value) }
7
+ let(:attribute) { mock('attribute') }
8
+ let(:value) { mock('value') }
9
+ let(:instance) { mock('instance') }
10
+ let(:response) { stub('response') }
11
+
12
+ before { value.stub(:call => response) }
13
+
14
+ it { should be(response) }
15
+
16
+ it 'calls the value with the instance and attribute' do
17
+ value.should_receive(:call).with(instance, attribute)
18
+ subject
19
+ end
20
+ end
21
+
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue::FromClonable, '.handle?' do
4
+ subject { described_class.handle?(attribute, default) }
5
+
6
+ let(:attribute) { mock('attribute') }
7
+
8
+ context 'with a clonable' do
9
+ let(:default) { 'clonable' }
10
+
11
+ it { should be(true) }
12
+ end
13
+
14
+ context 'with a non-clonable' do
15
+ let(:default) { 1 }
16
+
17
+ it { should be(false) }
18
+ end
19
+ end
20
+
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue::FromClonable, '#evaluate' do
4
+ subject { object.evaluate(instance) }
5
+
6
+ let(:object) { described_class.new(attribute, value) }
7
+ let(:attribute) { mock('attribute') }
8
+ let(:value) { mock('value') }
9
+ let(:instance) { mock('instance') }
10
+ let(:response) { mock('response') }
11
+ let(:clone) { mock('clone') }
12
+
13
+ before { value.stub(:clone => clone) }
14
+
15
+ it { should be(clone) }
16
+
17
+ it 'clones the value' do
18
+ value.should_receive(:clone).with(no_args)
19
+ subject
20
+ end
21
+ end
22
+
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue::FromSymbol, '.handle?' do
4
+ subject { described_class.handle?(attribute, default) }
5
+
6
+ let(:attribute) { mock('attribute') }
7
+
8
+ context 'with a symbol' do
9
+ let(:default) { :method_name }
10
+
11
+ it { should be(true) }
12
+ end
13
+
14
+ context 'with a non-symbol' do
15
+ let(:default) { 'method_name' }
16
+
17
+ it { should be(false) }
18
+ end
19
+ end
20
+
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::Attribute::DefaultValue::FromSymbol, '#evaluate' do
4
+ subject { object.evaluate(instance) }
5
+
6
+ let(:object) { described_class.new(attribute, value) }
7
+ let(:attribute) { mock('attribute') }
8
+ let(:instance) { mock('instance', value => retval) }
9
+ let(:value) { :set_default }
10
+ let(:retval) { mock('retval') }
11
+
12
+ it { should be(retval) }
13
+
14
+ it 'calls the method' do
15
+ instance.should_receive(value).with(no_args)
16
+ subject
17
+ end
18
+ end
@@ -31,8 +31,9 @@ describe Virtus::AttributeSet, '#[]=' do
31
31
  end
32
32
 
33
33
  context 'with a duplicate attribute' do
34
- let(:attributes) { [ mock('original', :name => name) ] }
35
- let(:attribute) { mock('attribute', :name => name) }
34
+ let(:original) { mock('original', :name => name) }
35
+ let(:attributes) { [ original ] }
36
+ let(:attribute) { mock('attribute', :name => name) }
36
37
 
37
38
  it { should equal(attribute) }
38
39
 
@@ -41,11 +42,11 @@ describe Virtus::AttributeSet, '#[]=' do
41
42
  end
42
43
 
43
44
  it 'allows #[] to access the attribute with a symbol' do
44
- expect { subject }.to change { object['name'] }.from(nil).to(attribute)
45
+ expect { subject }.to change { object['name'] }.from(original).to(attribute)
45
46
  end
46
47
 
47
48
  it 'allows #[] to access the attribute with a string' do
48
- expect { subject }.to change { object[:name] }.from(nil).to(attribute)
49
+ expect { subject }.to change { object[:name] }.from(original).to(attribute)
49
50
  end
50
51
 
51
52
  it 'allows #reset to track overridden attributes' do
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe Virtus::AttributesAccessor, '#define_reader_method' do
4
+ subject { described_class.new('Test') }
5
+
6
+ let(:attribute) { mock('attribute') }
7
+
8
+ if RUBY_VERSION < '1.9'
9
+ let(:method_name) { 'foo_bar' }
10
+ else
11
+ let(:method_name) { :foo_bar }
12
+ end
13
+
14
+ before do
15
+ subject.define_reader_method(attribute, method_name, visibility)
16
+ end
17
+
18
+ context "with public visibility" do
19
+ let(:visibility) { :public }
20
+
21
+ its(:public_instance_methods) { should include(method_name) }
22
+ end
23
+
24
+ context "with private visibility" do
25
+ let(:visibility) { :private }
26
+
27
+ its(:private_instance_methods) { should include(method_name) }
28
+ end
29
+
30
+ context "with protected visibility" do
31
+ let(:visibility) { :protected }
32
+
33
+ its(:protected_instance_methods) { should include(method_name) }
34
+ end
35
+ end
36
+