virtus 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +2 -2
  4. data/Changelog.md +11 -0
  5. data/Gemfile +4 -2
  6. data/Gemfile.devtools +25 -19
  7. data/README.md +56 -4
  8. data/lib/virtus.rb +26 -5
  9. data/lib/virtus/attribute/builder.rb +2 -2
  10. data/lib/virtus/attribute/collection.rb +7 -3
  11. data/lib/virtus/attribute/hash.rb +3 -3
  12. data/lib/virtus/attribute/strict.rb +1 -1
  13. data/lib/virtus/builder.rb +1 -1
  14. data/lib/virtus/configuration.rb +7 -36
  15. data/lib/virtus/instance_methods.rb +1 -0
  16. data/lib/virtus/module_extensions.rb +8 -2
  17. data/lib/virtus/support/options.rb +1 -0
  18. data/lib/virtus/support/type_lookup.rb +1 -1
  19. data/lib/virtus/version.rb +1 -1
  20. data/spec/integration/custom_attributes_spec.rb +2 -2
  21. data/spec/integration/custom_collection_attributes_spec.rb +6 -6
  22. data/spec/integration/default_values_spec.rb +8 -8
  23. data/spec/integration/defining_attributes_spec.rb +25 -18
  24. data/spec/integration/embedded_value_spec.rb +5 -5
  25. data/spec/integration/extending_objects_spec.rb +5 -5
  26. data/spec/integration/hash_attributes_coercion_spec.rb +11 -7
  27. data/spec/integration/mass_assignment_with_accessors_spec.rb +5 -5
  28. data/spec/integration/overriding_virtus_spec.rb +4 -4
  29. data/spec/integration/required_attributes_spec.rb +1 -1
  30. data/spec/integration/struct_as_embedded_value_spec.rb +4 -4
  31. data/spec/integration/using_modules_spec.rb +8 -8
  32. data/spec/integration/value_object_with_custom_constructor_spec.rb +4 -4
  33. data/spec/integration/virtus/instance_level_attributes_spec.rb +1 -1
  34. data/spec/integration/virtus/value_object_spec.rb +14 -14
  35. data/spec/shared/freeze_method_behavior.rb +1 -1
  36. data/spec/spec_helper.rb +1 -0
  37. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +9 -1
  38. data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +13 -2
  39. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +21 -0
  40. data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +14 -2
  41. data/spec/unit/virtus/attribute_set/each_spec.rb +21 -16
  42. data/spec/unit/virtus/attribute_set/element_reference_spec.rb +1 -1
  43. data/spec/unit/virtus/attribute_set/reset_spec.rb +5 -3
  44. data/spec/unit/virtus/attribute_spec.rb +4 -3
  45. data/spec/unit/virtus/attributes_reader_spec.rb +1 -1
  46. data/spec/unit/virtus/attributes_writer_spec.rb +1 -1
  47. data/spec/unit/virtus/model_spec.rb +3 -3
  48. data/spec/unit/virtus/module_spec.rb +59 -2
  49. data/spec/unit/virtus/value_object_spec.rb +2 -2
  50. data/virtus.gemspec +2 -2
  51. metadata +34 -32
@@ -32,6 +32,6 @@ shared_examples_for 'a #freeze method' do
32
32
  its(:frozen?) { should be(true) }
33
33
 
34
34
  it 'allows to access attribute' do
35
- subject.name.should eql('John')
35
+ expect(subject.name).to eql('John')
36
36
  end
37
37
  end
@@ -16,6 +16,7 @@ if ENV['COVERAGE'] == 'true'
16
16
  end
17
17
 
18
18
  require 'rspec'
19
+ require 'rspec/its'
19
20
  require 'bogus/rspec'
20
21
  require 'virtus'
21
22
  require 'inflecto' # for resolving namespaced constant names
@@ -7,7 +7,7 @@ describe Virtus::Attribute, '.build' do
7
7
  let(:type) { String }
8
8
  let(:options) { {} }
9
9
 
10
- share_examples_for 'a valid attribute instance' do
10
+ shared_examples_for 'a valid attribute instance' do
11
11
  it { should be_instance_of(Virtus::Attribute) }
12
12
 
13
13
  it { should be_frozen }
@@ -83,6 +83,14 @@ describe Virtus::Attribute, '.build' do
83
83
  its(:type) { should be(Axiom::Types::Integer) }
84
84
  end
85
85
 
86
+ context 'when type is a range' do
87
+ let(:type) { 0..10 }
88
+
89
+ it_behaves_like 'a valid attribute instance'
90
+
91
+ its(:type) { should be(Axiom::Types.infer(Range)) }
92
+ end
93
+
86
94
  context 'when type is a symbol of an existing class constant' do
87
95
  let(:type) { :String }
88
96
 
@@ -1,9 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::Attribute, '.build' do
4
- subject { described_class.build(type) }
4
+ subject { described_class.build(type, options) }
5
5
 
6
- share_examples_for 'a valid collection attribute instance' do
6
+ let(:options) { {} }
7
+
8
+ shared_examples_for 'a valid collection attribute instance' do
7
9
  it { should be_instance_of(Virtus::Attribute::Collection) }
8
10
 
9
11
  it { should be_frozen }
@@ -91,4 +93,13 @@ describe Virtus::Attribute, '.build' do
91
93
  expect(subject.type.member_type).to be(Axiom::Types::String)
92
94
  end
93
95
  end
96
+
97
+ context 'when strict mode is used' do
98
+ let(:type) { Array[String] }
99
+ let(:options) { { strict: true } }
100
+
101
+ it 'sets strict mode for member type' do
102
+ expect(subject.member_type).to be_strict
103
+ end
104
+ end
94
105
  end
@@ -50,4 +50,25 @@ describe Virtus::Attribute::Collection, '#coerce' do
50
50
  end
51
51
  end
52
52
  end
53
+
54
+ context 'when input is nil' do
55
+ let(:input) { nil }
56
+
57
+ fake(:coercer) { Virtus::Attribute::Coercer }
58
+ fake(:member_type) { Virtus::Attribute }
59
+
60
+ let(:member_primitive) { Integer }
61
+
62
+ let(:object) {
63
+ described_class.build(
64
+ Array[member_primitive], coercer: coercer, member_type: member_type
65
+ )
66
+ }
67
+
68
+ it 'returns nil' do
69
+ stub(coercer).call(input) { input }
70
+
71
+ expect(subject).to be(input)
72
+ end
73
+ end
53
74
  end
@@ -1,9 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::Attribute::Hash, '.build' do
4
- subject { described_class.build(type) }
4
+ subject { described_class.build(type, options) }
5
5
 
6
- share_examples_for 'a valid hash attribute instance' do
6
+ let(:options) { {} }
7
+
8
+ shared_examples_for 'a valid hash attribute instance' do
7
9
  it { should be_instance_of(Virtus::Attribute::Hash) }
8
10
 
9
11
  it { should be_frozen }
@@ -91,4 +93,14 @@ describe Virtus::Attribute::Hash, '.build' do
91
93
  )
92
94
  end
93
95
  end
96
+
97
+ context 'when strict mode is used' do
98
+ let(:type) { Hash[String => Integer] }
99
+ let(:options) { { :strict => true } }
100
+
101
+ it 'sets the strict mode for key/value types' do
102
+ expect(subject.key_type).to be_strict
103
+ expect(subject.value_type).to be_strict
104
+ end
105
+ end
94
106
  end
@@ -1,28 +1,31 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::AttributeSet, '#each' do
4
- let(:name) { :name }
4
+ subject(:attribute_set) { described_class.new(parent, attributes) }
5
+
6
+ let(:name) { :name }
5
7
  let(:attribute) { Virtus::Attribute.build(String, :name => :name) }
6
- let(:attributes) { [ attribute ] }
7
- let(:parent) { described_class.new }
8
- let(:object) { described_class.new(parent, attributes) }
9
- let(:yields) { Set[] }
8
+ let(:attributes) { [ attribute ] }
9
+ let(:parent) { described_class.new }
10
+ let(:yields) { Set[] }
10
11
 
11
12
  context 'with no block' do
12
- subject { object.each }
13
-
14
- it { should be_instance_of(to_enum.class) }
13
+ it 'returns an enumerator when block is not provided' do
14
+ expect(attribute_set.each).to be_kind_of(Enumerator)
15
+ end
15
16
 
16
17
  it 'yields the expected attributes' do
17
- subject.to_a.should eql(object.to_a)
18
+ result = []
19
+ attribute_set.each { |attribute| result << attribute }
20
+ expect(result).to eql(attributes)
18
21
  end
19
22
  end
20
23
 
21
24
  context 'with a block' do
22
- subject { object.each { |attribute| yields << attribute } }
25
+ subject { attribute_set.each { |attribute| yields << attribute } }
23
26
 
24
27
  context 'when the parent has no attributes' do
25
- it { should equal(object) }
28
+ it { should equal(attribute_set) }
26
29
 
27
30
  it 'yields the expected attributes' do
28
31
  expect { subject }.to change { yields.dup }.
@@ -35,12 +38,14 @@ describe Virtus::AttributeSet, '#each' do
35
38
  let(:parent_attribute) { Virtus::Attribute.build(String, :name => :parent_name) }
36
39
  let(:parent) { described_class.new([ parent_attribute ]) }
37
40
 
38
- it { should equal(object) }
41
+ it { should equal(attribute_set) }
39
42
 
40
43
  it 'yields the expected attributes' do
41
- expect { subject }.to change { yields.dup }.
42
- from(Set[]).
43
- to(Set[ attribute, parent_attribute ])
44
+ result = []
45
+
46
+ attribute_set.each { |attribute| result << attribute }
47
+
48
+ expect(result).to eql([parent_attribute, attribute])
44
49
  end
45
50
  end
46
51
 
@@ -48,7 +53,7 @@ describe Virtus::AttributeSet, '#each' do
48
53
  let(:parent_attribute) { Virtus::Attribute.build(String, :name => name) }
49
54
  let(:parent) { described_class.new([ parent_attribute ]) }
50
55
 
51
- it { should equal(object) }
56
+ it { should equal(attribute_set) }
52
57
 
53
58
  it 'yields the expected attributes' do
54
59
  expect { subject }.to change { yields.dup }.
@@ -12,6 +12,6 @@ describe Virtus::AttributeSet, '#[]' do
12
12
  it { should equal(attribute) }
13
13
 
14
14
  it 'allows indexed access to attributes by the string representation of their name' do
15
- object[name.to_s].should equal(attribute)
15
+ expect(object[name.to_s]).to equal(attribute)
16
16
  end
17
17
  end
@@ -42,9 +42,11 @@ describe Virtus::AttributeSet, '#reset' do
42
42
  it { should equal(object) }
43
43
 
44
44
  it 'includes changes from the parent' do
45
- expect { parent << new_attribute; subject }.to change { object.to_set }.
46
- from(Set[ attribute, parent_attribute ]).
47
- to(Set[ attribute, new_attribute ])
45
+ expect(object.to_set).to eql(Set[attribute, parent_attribute])
46
+
47
+ parent << new_attribute
48
+
49
+ expect(subject.to_set).to eql(Set[attribute, new_attribute])
48
50
  end
49
51
  end
50
52
 
@@ -4,14 +4,15 @@ describe Virtus, '#attribute' do
4
4
  let(:name) { :test }
5
5
  let(:options) { {} }
6
6
 
7
- share_examples_for 'a class with boolean attribute' do
7
+ shared_examples_for 'a class with boolean attribute' do
8
8
  subject { Test }
9
9
 
10
10
  let(:object) { subject.new }
11
11
 
12
12
  it 'defines reader and writer' do
13
13
  object.test = true
14
- expect(object.test).to be(true)
14
+
15
+ expect(object).to be_test
15
16
  end
16
17
 
17
18
  it 'defines predicate method' do
@@ -21,7 +22,7 @@ describe Virtus, '#attribute' do
21
22
  end
22
23
  end
23
24
 
24
- share_examples_for 'an object with string attribute' do
25
+ shared_examples_for 'an object with string attribute' do
25
26
  it { should respond_to(:test) }
26
27
  it { should respond_to(:test=) }
27
28
 
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Virtus, '#attributes' do
4
4
 
5
- share_examples_for 'attribute hash' do
5
+ shared_examples_for 'attribute hash' do
6
6
  it 'includes all attributes' do
7
7
  subject.attributes = { :test => 'Hello World', :test_priv => 'Yo' }
8
8
 
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Virtus, '#attributes=' do
4
4
 
5
- share_examples_for 'mass-assignment' do
5
+ shared_examples_for 'mass-assignment' do
6
6
  it 'allows writing known attributes' do
7
7
  subject.attributes = { :test => 'Hello World' }
8
8
 
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus, '.model' do
4
- share_examples_for 'a model with constructor' do
4
+ shared_examples_for 'a model with constructor' do
5
5
  it 'accepts attribute hash' do
6
6
  instance = subject.new(:name => 'Jane')
7
7
  expect(instance.name).to eql('Jane')
8
8
  end
9
9
  end
10
10
 
11
- share_examples_for 'a model with mass-assignment' do
11
+ shared_examples_for 'a model with mass-assignment' do
12
12
  let(:attributes) do
13
13
  { :name => 'Jane', :something => nil }
14
14
  end
@@ -22,7 +22,7 @@ describe Virtus, '.model' do
22
22
  end
23
23
  end
24
24
 
25
- share_examples_for 'a model with strict mode turned off' do
25
+ shared_examples_for 'a model with strict mode turned off' do
26
26
  it 'has attributes with strict set to false' do
27
27
  expect(subject.send(:attribute_set)[:name]).to_not be_strict
28
28
  end
@@ -1,14 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus, '.module' do
4
- share_examples_for 'a valid virtus object' do
4
+ shared_examples_for 'a valid virtus object' do
5
5
  it 'reads and writes attribute' do
6
6
  instance.name = 'John'
7
7
  expect(instance.name).to eql('John')
8
8
  end
9
9
  end
10
10
 
11
- share_examples_for 'an object extended with virtus module' do
11
+ shared_examples_for 'an object extended with virtus module' do
12
12
  context 'with default configuration' do
13
13
  subject { Virtus.module }
14
14
 
@@ -114,4 +114,61 @@ describe Virtus, '.module' do
114
114
  end
115
115
  end
116
116
 
117
+ context 'as a peer to another module within a class' do
118
+ subject { Virtus.module }
119
+ let(:other) { Module.new }
120
+
121
+ before do
122
+ other.send(:include, Virtus.module)
123
+ other.attribute :last_name, String, :default => 'Doe'
124
+ other.attribute :something_else
125
+ model.send(:include, mod)
126
+ model.send(:include, other)
127
+ end
128
+
129
+ it 'provides attributes for the model from both modules' do
130
+ expect(model.attribute_set[:name]).to be_kind_of(Virtus::Attribute)
131
+ expect(model.attribute_set[:something]).to be_kind_of(Virtus::Attribute)
132
+ expect(model.attribute_set[:last_name]).to be_kind_of(Virtus::Attribute)
133
+ expect(model.attribute_set[:something_else]).to be_kind_of(Virtus::Attribute)
134
+ end
135
+
136
+ it 'includes the attributes from both modules' do
137
+ expect(model.new.attributes.keys).to eq(
138
+ [:name, :something, :last_name, :something_else]
139
+ )
140
+ end
141
+ end
142
+
143
+ context 'with multiple other modules mixed into it' do
144
+ subject { Virtus.module }
145
+ let(:other) { Module.new }
146
+ let(:yet_another) { Module.new }
147
+
148
+ before do
149
+ other.send(:include, Virtus.module)
150
+ other.attribute :last_name, String, :default => 'Doe'
151
+ other.attribute :something_else
152
+ yet_another.send(:include, Virtus.module)
153
+ yet_another.send(:include, mod)
154
+ yet_another.send(:include, other)
155
+ yet_another.attribute :middle_name, String, :default => 'Foobar'
156
+ model.send(:include, yet_another)
157
+ end
158
+
159
+ it 'provides attributes for the model from all modules' do
160
+ expect(model.attribute_set[:name]).to be_kind_of(Virtus::Attribute)
161
+ expect(model.attribute_set[:something]).to be_kind_of(Virtus::Attribute)
162
+ expect(model.attribute_set[:last_name]).to be_kind_of(Virtus::Attribute)
163
+ expect(model.attribute_set[:something_else]).to be_kind_of(Virtus::Attribute)
164
+ expect(model.attribute_set[:middle_name]).to be_kind_of(Virtus::Attribute)
165
+ end
166
+
167
+ it 'includes the attributes from all modules' do
168
+ expect(model.new.attributes.keys).to eq(
169
+ [:name, :something, :last_name, :something_else, :middle_name]
170
+ )
171
+ end
172
+ end
173
+
117
174
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Virtus::ValueObject do
4
- share_examples_for 'a valid value object' do
4
+ shared_examples_for 'a valid value object' do
5
5
  subject { model.new(attributes) }
6
6
 
7
7
  let(:attributes) { Hash[:id => 1, :name => 'Jane Doe'] }
@@ -43,7 +43,7 @@ describe Virtus::ValueObject do
43
43
  end
44
44
  end
45
45
 
46
- share_examples_for 'a valid value object with mass-assignment turned on' do
46
+ shared_examples_for 'a valid value object with mass-assignment turned on' do
47
47
  subject { model.new }
48
48
 
49
49
  it 'disallows mass-assignment' do
@@ -17,8 +17,8 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = `git ls-files -- {spec}/*`.split("\n")
18
18
  gem.extra_rdoc_files = %w[LICENSE README.md TODO.md]
19
19
 
20
- gem.add_dependency('descendants_tracker', '~> 0.0.3')
21
- gem.add_dependency('equalizer', '~> 0.0.9')
20
+ gem.add_dependency('descendants_tracker', '~> 0.0', '>= 0.0.3')
21
+ gem.add_dependency('equalizer', '~> 0.0', '>= 0.0.9')
22
22
  gem.add_dependency('coercible', '~> 1.0')
23
23
  gem.add_dependency('axiom-types', '~> 0.1')
24
24
  end
metadata CHANGED
@@ -1,78 +1,81 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: virtus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
5
- prerelease:
4
+ version: 1.0.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Piotr Solnica
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-03-12 00:00:00.000000000 Z
11
+ date: 2014-07-24 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: descendants_tracker
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.0'
20
+ - - ">="
20
21
  - !ruby/object:Gem::Version
21
22
  version: 0.0.3
22
23
  type: :runtime
23
24
  prerelease: false
24
25
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
26
  requirements:
27
- - - ~>
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.0'
30
+ - - ">="
28
31
  - !ruby/object:Gem::Version
29
32
  version: 0.0.3
30
33
  - !ruby/object:Gem::Dependency
31
34
  name: equalizer
32
35
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
36
  requirements:
35
- - - ~>
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.0'
40
+ - - ">="
36
41
  - !ruby/object:Gem::Version
37
42
  version: 0.0.9
38
43
  type: :runtime
39
44
  prerelease: false
40
45
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
46
  requirements:
43
- - - ~>
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.0'
50
+ - - ">="
44
51
  - !ruby/object:Gem::Version
45
52
  version: 0.0.9
46
53
  - !ruby/object:Gem::Dependency
47
54
  name: coercible
48
55
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
56
  requirements:
51
- - - ~>
57
+ - - "~>"
52
58
  - !ruby/object:Gem::Version
53
59
  version: '1.0'
54
60
  type: :runtime
55
61
  prerelease: false
56
62
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
63
  requirements:
59
- - - ~>
64
+ - - "~>"
60
65
  - !ruby/object:Gem::Version
61
66
  version: '1.0'
62
67
  - !ruby/object:Gem::Dependency
63
68
  name: axiom-types
64
69
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
70
  requirements:
67
- - - ~>
71
+ - - "~>"
68
72
  - !ruby/object:Gem::Version
69
73
  version: '0.1'
70
74
  type: :runtime
71
75
  prerelease: false
72
76
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
77
  requirements:
75
- - - ~>
78
+ - - "~>"
76
79
  - !ruby/object:Gem::Version
77
80
  version: '0.1'
78
81
  description: Attributes on Steroids for Plain Old Ruby Objects
@@ -85,13 +88,13 @@ extra_rdoc_files:
85
88
  - README.md
86
89
  - TODO.md
87
90
  files:
88
- - .gitignore
89
- - .pelusa.yml
90
- - .rspec
91
- - .ruby-gemset
92
- - .ruby-version
93
- - .travis.yml
94
- - .yardopts
91
+ - ".gitignore"
92
+ - ".pelusa.yml"
93
+ - ".rspec"
94
+ - ".ruby-gemset"
95
+ - ".ruby-version"
96
+ - ".travis.yml"
97
+ - ".yardopts"
95
98
  - CONTRIBUTING.md
96
99
  - Changelog.md
97
100
  - Gemfile
@@ -209,27 +212,26 @@ files:
209
212
  homepage: https://github.com/solnic/virtus
210
213
  licenses:
211
214
  - MIT
215
+ metadata: {}
212
216
  post_install_message:
213
217
  rdoc_options: []
214
218
  require_paths:
215
219
  - lib
216
220
  required_ruby_version: !ruby/object:Gem::Requirement
217
- none: false
218
221
  requirements:
219
- - - ! '>='
222
+ - - ">="
220
223
  - !ruby/object:Gem::Version
221
224
  version: '0'
222
225
  required_rubygems_version: !ruby/object:Gem::Requirement
223
- none: false
224
226
  requirements:
225
- - - ! '>='
227
+ - - ">="
226
228
  - !ruby/object:Gem::Version
227
229
  version: '0'
228
230
  requirements: []
229
231
  rubyforge_project:
230
- rubygems_version: 1.8.23
232
+ rubygems_version: 2.2.2
231
233
  signing_key:
232
- specification_version: 3
234
+ specification_version: 4
233
235
  summary: Attributes on Steroids for Plain Old Ruby Objects
234
236
  test_files: []
235
237
  has_rdoc: