virtus 1.0.3 → 1.0.4

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +8 -5
  3. data/Changelog.md +7 -1
  4. data/Gemfile +14 -5
  5. data/README.md +40 -19
  6. data/Rakefile +13 -3
  7. data/lib/virtus/configuration.rb +10 -5
  8. data/lib/virtus/version.rb +1 -1
  9. data/spec/integration/collection_member_coercion_spec.rb +34 -13
  10. data/spec/integration/hash_attributes_coercion_spec.rb +5 -5
  11. data/spec/shared/freeze_method_behavior.rb +5 -2
  12. data/spec/shared/idempotent_method_behaviour.rb +1 -1
  13. data/spec/shared/options_class_method.rb +3 -3
  14. data/spec/spec_helper.rb +3 -18
  15. data/spec/unit/virtus/attribute/boolean/coerce_spec.rb +3 -3
  16. data/spec/unit/virtus/attribute/boolean/value_coerced_predicate_spec.rb +3 -3
  17. data/spec/unit/virtus/attribute/class_methods/build_spec.rb +48 -24
  18. data/spec/unit/virtus/attribute/class_methods/coerce_spec.rb +2 -2
  19. data/spec/unit/virtus/attribute/coerce_spec.rb +9 -9
  20. data/spec/unit/virtus/attribute/coercible_predicate_spec.rb +2 -2
  21. data/spec/unit/virtus/attribute/collection/class_methods/build_spec.rb +2 -2
  22. data/spec/unit/virtus/attribute/collection/coerce_spec.rb +5 -5
  23. data/spec/unit/virtus/attribute/custom_collection_spec.rb +8 -2
  24. data/spec/unit/virtus/attribute/defined_spec.rb +2 -2
  25. data/spec/unit/virtus/attribute/embedded_value/class_methods/build_spec.rb +30 -15
  26. data/spec/unit/virtus/attribute/embedded_value/coerce_spec.rb +25 -11
  27. data/spec/unit/virtus/attribute/get_spec.rb +2 -2
  28. data/spec/unit/virtus/attribute/hash/class_methods/build_spec.rb +7 -7
  29. data/spec/unit/virtus/attribute/hash/coerce_spec.rb +9 -9
  30. data/spec/unit/virtus/attribute/lazy_predicate_spec.rb +2 -2
  31. data/spec/unit/virtus/attribute/rename_spec.rb +6 -3
  32. data/spec/unit/virtus/attribute/required_predicate_spec.rb +2 -2
  33. data/spec/unit/virtus/attribute/set_default_value_spec.rb +43 -10
  34. data/spec/unit/virtus/attribute/set_spec.rb +1 -1
  35. data/spec/unit/virtus/attribute/value_coerced_predicate_spec.rb +2 -2
  36. data/spec/unit/virtus/attribute_set/append_spec.rb +2 -2
  37. data/spec/unit/virtus/attribute_set/define_reader_method_spec.rb +12 -11
  38. data/spec/unit/virtus/attribute_set/define_writer_method_spec.rb +13 -12
  39. data/spec/unit/virtus/attribute_set/each_spec.rb +3 -3
  40. data/spec/unit/virtus/attribute_set/element_reference_spec.rb +1 -1
  41. data/spec/unit/virtus/attribute_set/element_set_spec.rb +2 -2
  42. data/spec/unit/virtus/attribute_set/merge_spec.rb +2 -2
  43. data/spec/unit/virtus/attribute_set/reset_spec.rb +17 -8
  44. data/spec/unit/virtus/attribute_spec.rb +4 -4
  45. data/spec/unit/virtus/element_reader_spec.rb +1 -1
  46. data/spec/unit/virtus/freeze_spec.rb +10 -3
  47. data/spec/unit/virtus/model_spec.rb +35 -4
  48. data/spec/unit/virtus/set_default_attributes_spec.rb +10 -3
  49. data/spec/unit/virtus/value_object_spec.rb +13 -3
  50. data/virtus.gemspec +6 -4
  51. metadata +16 -8
  52. data/Gemfile.devtools +0 -71
  53. data/config/flay.yml +0 -3
  54. data/config/flog.yml +0 -2
  55. data/config/mutant.yml +0 -15
  56. data/config/reek.yml +0 -146
  57. data/config/yardstick.yml +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9159a933adbe9e8abeda7386590659134150c554
4
- data.tar.gz: cd8c5f3edb39d1dc81d5796ab6a42ba941a1485f
3
+ metadata.gz: 934dc95c7d8fd79268fcf7e2303257cd72378ef9
4
+ data.tar.gz: dd9096d86a6df285b7b97f4cabab540afa72ed0b
5
5
  SHA512:
6
- metadata.gz: b57dce024748303a11bb12631e7133b8ac1ee66711bb08b09cd85080552d815c283596b8a9b7af9a87ae77e413592ece743f8059c8293b808d6e4a653bba7837
7
- data.tar.gz: f1704e07a82c1de070711b117387fcb2bd067e7bfa95a8f46daf2e1bad818f3faa82500636004d0474e0994ab2a3eb7d1e123ad163b0727f015cdf5ac1ef8326
6
+ metadata.gz: 9095639c91403b8193bfe71a9067da701298a5190f65bdef2ac0ea0aaef130cf819529b2a672e071b48857ce614082402048a9125658b4a6f129d432968239f7
7
+ data.tar.gz: ec1343ee430f0e6c6766c6e7a2457d116b7b4de00156f2ffe813989f1f62f96d712218756f750bfb45dc700552ba7999450ae243a3024ed1af3a5671e8a7b817
@@ -1,11 +1,14 @@
1
1
  language: ruby
2
2
  before_install: gem install bundler
3
- bundler_args: --without yard guard benchmarks
4
- script: "bundle exec rake metrics:coverage spec:integration"
3
+ bundler_args: --without tools
4
+ script: "bundle exec rake spec"
5
+ env:
6
+ - CODECLIMATE_REPO_TOKEN=2b66fbb7c7c72503eb7841a479c0ad923f691729f4109b4aa8c9b4def1ebb42d
5
7
  rvm:
6
- - 1.9.3
7
- - 2.0.0
8
- - 2.1.2
8
+ - 1.9
9
+ - 2.0
10
+ - 2.1
11
+ - 2.2
9
12
  - jruby
10
13
  - rbx
11
14
  - ruby-head
@@ -1,3 +1,9 @@
1
+ # v1.0.4 2015-01-03
2
+
3
+ * [feature] Support for :required option when configuring a virtus module (solnic)
4
+
5
+ [Compare v1.0.3..v1.0.4](https://github.com/solnic/virtus/compare/v1.0.3...v1.0.4)
6
+
1
7
  # v1.0.3 2014-07-24
2
8
 
3
9
  * [improvement] Expose attribute name in the exception when in strict mode (ntl)
@@ -9,7 +15,7 @@
9
15
 
10
16
  [Compare v1.0.2..v1.0.3](https://github.com/solnic/virtus/compare/v1.0.2...v1.0.3)
11
17
 
12
- # v1.0.2 2014-12-03
18
+ # v1.0.2 2013-12-03
13
19
 
14
20
  * [improvement] Don’t override already-defined default values when freezing (amarshall)
15
21
  * [improvement] Improved performance of `AttributeSet#each` (Antti)
data/Gemfile CHANGED
@@ -3,10 +3,19 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  gem 'bogus', '~> 0.1'
6
- gem 'devtools', :git => 'https://github.com/rom-rb/devtools', branch: 'master'
6
+ gem 'inflecto', '~> 0.0.2'
7
+ gem 'rspec', '~> 3.1'
7
8
 
8
- group :test do
9
- gem 'inflecto', '~> 0.0.2'
10
- end
9
+ gem "codeclimate-test-reporter", group: :test, require: false
10
+
11
+ group :tools do
12
+ gem 'guard'
13
+ gem 'guard-rspec'
11
14
 
12
- eval_gemfile 'Gemfile.devtools'
15
+ gem 'rubocop'
16
+
17
+ platform :mri do
18
+ gem 'mutant'
19
+ gem 'mutant-rspec'
20
+ end
21
+ end
data/README.md CHANGED
@@ -1,27 +1,35 @@
1
- Virtus
2
- ======
3
-
4
- [![Gem Version](https://badge.fury.io/rb/virtus.png)][gem]
5
- [![Build Status](https://secure.travis-ci.org/solnic/virtus.png?branch=master)][travis]
6
- [![Dependency Status](https://gemnasium.com/solnic/virtus.png)][gemnasium]
7
- [![Code Climate](https://codeclimate.com/github/solnic/virtus.png)][codeclimate]
8
- [![Coverage Status](https://coveralls.io/repos/solnic/virtus/badge.png?branch=master)][coveralls]
9
- [![Inline docs](http://inch-ci.org/github/solnic/virtus.png)][inchpages]
10
-
11
1
  [gem]: https://rubygems.org/gems/virtus
12
2
  [travis]: https://travis-ci.org/solnic/virtus
13
3
  [gemnasium]: https://gemnasium.com/solnic/virtus
14
4
  [codeclimate]: https://codeclimate.com/github/solnic/virtus
15
5
  [coveralls]: https://coveralls.io/r/solnic/virtus
16
- [inchpages]: http://inch-ci.org/github/solnic/virtus
6
+ [inchpages]: http://inch-ci.org/github/solnic/virtus/
7
+
8
+ Virtus
9
+ ======
10
+
11
+ # Ruby Object Mapper
12
+
13
+ [![Gem Version](https://badge.fury.io/rb/rom.svg)][gem]
14
+ [![Build Status](https://travis-ci.org/solnic/virtus.svg?branch=master)][travis]
15
+ [![Dependency Status](https://gemnasium.com/solnic/virtus.png)][gemnasium]
16
+ [![Code Climate](https://codeclimate.com/github/solnic/virtus/badges/gpa.svg)][codeclimate]
17
+ [![Test Coverage](https://codeclimate.com/github/solnic/virtus/badges/coverage.svg)][codeclimate]
18
+ [![Inline docs](http://inch-ci.org/github/solnic/virtus.svg?branch=master)][inchpages]
17
19
 
18
- This is a partial extraction of the DataMapper [Property
19
- API](http://rubydoc.info/github/datamapper/dm-core/master/DataMapper/Property)
20
- with various modifications and improvements. The goal is to provide a common API
21
- for defining attributes on a model so all ORMs/ODMs could use it instead of
22
- reinventing the wheel all over again. It is also suitable for any other
23
- use case where you need to extend your ruby objects with attributes that require
24
- data-type coercions.
20
+ Virtus allows you to define attributes on classes, modules or class instances with
21
+ optional information about types, reader/writer method visibility and coercion
22
+ behavior. It supports a lot of coercions and advanced mapping of embedded objects
23
+ and collections.
24
+
25
+ You can use it in many different contexts like:
26
+
27
+ * Input parameter sanitization and coercion in web applications
28
+ * Mapping JSON to domain objects
29
+ * Encapsulating data-access in Value Objects
30
+ * Domain model prototyping
31
+
32
+ And probably more.
25
33
 
26
34
  Installation
27
35
  ------------
@@ -181,6 +189,19 @@ page.reset_attribute(:views) # => 0
181
189
  page.views # => 0
182
190
  ```
183
191
 
192
+ ### Default values on dynamically extended instances
193
+
194
+ This requires you to set `:lazy` option because default values are set in the
195
+ constructor if it's set to false (which is the default setting):
196
+
197
+ ``` ruby
198
+ User = Class.new
199
+ user = User.new
200
+ user.extend(Virtus.model)
201
+ user.attribute :name, String, default: 'jane', lazy: true
202
+ user.name # => "jane"
203
+ ```
204
+
184
205
  ### Embedded Value
185
206
 
186
207
  ``` ruby
@@ -409,7 +430,7 @@ user.info.class # => Hash
409
430
  # With a custom attribute encapsulating coercion-specific configuration
410
431
  class NoisyString < Virtus::Attribute
411
432
  def coerce(value)
412
- coercer[value.class].to_string.upcase
433
+ value.to_s.upcase
413
434
  end
414
435
  end
415
436
 
data/Rakefile CHANGED
@@ -1,5 +1,15 @@
1
- # encoding: utf-8
1
+ require "rspec/core/rake_task"
2
2
 
3
- require 'devtools'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ task default: [:spec]
4
5
 
5
- Devtools.init_rake_tasks
6
+ begin
7
+ require "rubocop/rake_task"
8
+
9
+ Rake::Task[:default].enhance [:rubocop]
10
+
11
+ RuboCop::RakeTask.new do |task|
12
+ task.options << "--display-cop-names"
13
+ end
14
+ rescue LoadError
15
+ end
@@ -12,6 +12,9 @@ module Virtus
12
12
  # Access the strict setting for this instance
13
13
  attr_accessor :strict
14
14
 
15
+ # Access the required setting for this instance
16
+ attr_accessor :required
17
+
15
18
  # Access the constructor setting for this instance
16
19
  attr_accessor :constructor
17
20
 
@@ -24,11 +27,12 @@ module Virtus
24
27
  #
25
28
  # @api private
26
29
  def initialize(options={})
27
- @finalize = options.fetch(:finalize,true)
28
- @coerce = options.fetch(:coerce,true)
29
- @strict = options.fetch(:strict,false)
30
- @constructor = options.fetch(:constructor,true)
31
- @mass_assignment = options.fetch(:mass_assignment,true)
30
+ @finalize = options.fetch(:finalize, true)
31
+ @coerce = options.fetch(:coerce, true)
32
+ @strict = options.fetch(:strict, false)
33
+ @required = options.fetch(:required, true)
34
+ @constructor = options.fetch(:constructor, true)
35
+ @mass_assignment = options.fetch(:mass_assignment, true)
32
36
  @coercer = Coercible::Coercer.new
33
37
 
34
38
  yield self if block_given?
@@ -55,6 +59,7 @@ module Virtus
55
59
  { :coerce => coerce,
56
60
  :finalize => finalize,
57
61
  :strict => strict,
62
+ :required => required,
58
63
  :configured_coercer => coercer }.freeze
59
64
  end
60
65
 
@@ -1,3 +1,3 @@
1
1
  module Virtus
2
- VERSION = '1.0.3'
2
+ VERSION = '1.0.4'.freeze
3
3
  end
@@ -25,10 +25,10 @@ class User
25
25
  end
26
26
 
27
27
  describe User do
28
- it { should respond_to(:phone_numbers) }
29
- it { should respond_to(:phone_numbers=) }
30
- it { should respond_to(:addresses) }
31
- it { should respond_to(:addresses=) }
28
+ it { is_expected.to respond_to(:phone_numbers) }
29
+ it { is_expected.to respond_to(:phone_numbers=) }
30
+ it { is_expected.to respond_to(:addresses) }
31
+ it { is_expected.to respond_to(:addresses=) }
32
32
 
33
33
  let(:instance) do
34
34
  described_class.new(:phone_numbers => phone_numbers_attributes,
@@ -48,28 +48,49 @@ describe User do
48
48
  describe 'first entry' do
49
49
  subject { instance.phone_numbers.first }
50
50
 
51
- it { should be_instance_of(PhoneNumber) }
51
+ it { is_expected.to be_instance_of(PhoneNumber) }
52
52
 
53
- its(:number) { should eql('212-555-1212') }
53
+ describe '#number' do
54
+ subject { super().number }
55
+ it { is_expected.to eql('212-555-1212') }
56
+ end
54
57
  end
55
58
 
56
59
  describe 'last entry' do
57
60
  subject { instance.phone_numbers.last }
58
61
 
59
- it { should be_instance_of(PhoneNumber) }
62
+ it { is_expected.to be_instance_of(PhoneNumber) }
60
63
 
61
- its(:number) { should eql('919-444-3265') }
64
+ describe '#number' do
65
+ subject { super().number }
66
+ it { is_expected.to eql('919-444-3265') }
67
+ end
62
68
  end
63
69
  end
64
70
 
65
71
  describe '#addresses' do
66
72
  subject { instance.addresses.first }
67
73
 
68
- it { should be_instance_of(Address) }
74
+ it { is_expected.to be_instance_of(Address) }
69
75
 
70
- its(:address) { should eql('1234 Any St.') }
71
- its(:locality) { should eql('Anytown') }
72
- its(:region) { should eql('DC') }
73
- its(:postal_code) { should eql('21234') }
76
+ describe '#address' do
77
+ subject { super().address }
78
+ it { is_expected.to eql('1234 Any St.') }
79
+ end
80
+
81
+ describe '#locality' do
82
+ subject { super().locality }
83
+ it { is_expected.to eql('Anytown') }
84
+ end
85
+
86
+ describe '#region' do
87
+ subject { super().region }
88
+ it { is_expected.to eql('DC') }
89
+ end
90
+
91
+ describe '#postal_code' do
92
+ subject { super().postal_code }
93
+ it { is_expected.to eql('21234') }
94
+ end
74
95
  end
75
96
  end
@@ -26,9 +26,9 @@ describe Package do
26
26
  it 'has 3 keys' do
27
27
  expect(subject.keys.size).to eq(3)
28
28
  end
29
- it { should have_key :width }
30
- it { should have_key :height }
31
- it { should have_key :length }
29
+ it { is_expected.to have_key :width }
30
+ it { is_expected.to have_key :height }
31
+ it { is_expected.to have_key :length }
32
32
 
33
33
  it 'should be coerced to [Symbol => Float] format' do
34
34
  expect(dimensions[:width]).to be_eql(2.2)
@@ -43,8 +43,8 @@ describe Package do
43
43
  it 'has 2 keys' do
44
44
  expect(subject.keys.size).to eq(2)
45
45
  end
46
- it { should have_key 'from' }
47
- it { should have_key 'to' }
46
+ it { is_expected.to have_key 'from' }
47
+ it { is_expected.to have_key 'to' }
48
48
 
49
49
  it 'should be coerced to [String => String] format' do
50
50
  expect(meta_info['from']).to eq('Me')
@@ -20,7 +20,7 @@ shared_examples_for 'a #freeze method' do
20
20
  it_should_behave_like 'an idempotent method'
21
21
 
22
22
  it 'returns object' do
23
- should be(object)
23
+ is_expected.to be(object)
24
24
  end
25
25
 
26
26
  it 'prevents future modifications' do
@@ -29,7 +29,10 @@ shared_examples_for 'a #freeze method' do
29
29
  expect { object.instance_variable_set(:@foo, :bar) }.to(expectation)
30
30
  end
31
31
 
32
- its(:frozen?) { should be(true) }
32
+ describe '#frozen?' do
33
+ subject { super().frozen? }
34
+ it { is_expected.to be(true) }
35
+ end
33
36
 
34
37
  it 'allows to access attribute' do
35
38
  expect(subject.name).to eql('John')
@@ -1,5 +1,5 @@
1
1
  shared_examples_for 'an idempotent method' do
2
2
  it 'is idempotent' do
3
- should equal(subject)
3
+ is_expected.to equal(subject)
4
4
  end
5
5
  end
@@ -2,15 +2,15 @@ shared_examples_for 'an options class method' do
2
2
  context 'with no argument' do
3
3
  subject { object.send(method) }
4
4
 
5
- it { should be(default) }
5
+ it { is_expected.to be(default) }
6
6
  end
7
7
 
8
8
  context 'with a default value' do
9
9
  subject { object.send(method, value) }
10
10
 
11
- let(:value) { double('value') }
11
+ let(:value) { mock('value') }
12
12
 
13
- it { should equal(object) }
13
+ it { is_expected.to equal(object) }
14
14
 
15
15
  it 'sets the default value for the class method' do
16
16
  expect { subject }.to change { object.send(method) }.from(default).to(value)
@@ -1,22 +1,9 @@
1
- if ENV['COVERAGE'] == 'true'
2
- require 'simplecov'
3
- require 'coveralls'
4
-
5
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
- SimpleCov::Formatter::HTMLFormatter,
7
- Coveralls::SimpleCov::Formatter
8
- ]
9
-
10
- SimpleCov.start do
11
- command_name 'spec:unit'
12
- add_filter 'config/'
13
- add_filter 'spec'
14
- add_filter '.bundle'
15
- end
1
+ if RUBY_ENGINE == "rbx"
2
+ require "codeclimate-test-reporter"
3
+ CodeClimate::TestReporter.start
16
4
  end
17
5
 
18
6
  require 'rspec'
19
- require 'rspec/its'
20
7
  require 'bogus/rspec'
21
8
  require 'virtus'
22
9
  require 'inflecto' # for resolving namespaced constant names
@@ -33,7 +20,6 @@ ENV['TZ'] = 'UTC'
33
20
  Dir[File.expand_path('../shared/**/*.rb', __FILE__)].each { |file| require file }
34
21
 
35
22
  RSpec.configure do |config|
36
-
37
23
  # Remove anonymous- and example- Attribute classes from Attribute descendants
38
24
  config.after :all do
39
25
  stack = [ Virtus::Attribute ]
@@ -53,5 +39,4 @@ RSpec.configure do |config|
53
39
  end
54
40
  end
55
41
  end
56
-
57
42
  end
@@ -10,13 +10,13 @@ describe Virtus::Attribute::Boolean, '#coerce' do
10
10
  context 'with a truthy value' do
11
11
  let(:input) { 1 }
12
12
 
13
- it { should be(true) }
13
+ it { is_expected.to be(true) }
14
14
  end
15
15
 
16
16
  context 'with a falsy value' do
17
17
  let(:input) { 0 }
18
18
 
19
- it { should be(false) }
19
+ it { is_expected.to be(false) }
20
20
  end
21
21
  end
22
22
 
@@ -26,7 +26,7 @@ describe Virtus::Attribute::Boolean, '#coerce' do
26
26
  context 'with a coercible input' do
27
27
  let(:input) { 1 }
28
28
 
29
- it { should be(true) }
29
+ it { is_expected.to be(true) }
30
30
  end
31
31
 
32
32
  context 'with a non-coercible input' do
@@ -8,18 +8,18 @@ describe Virtus::Attribute::Boolean, '#value_coerced?' do
8
8
  context 'when input is true' do
9
9
  let(:input) { true }
10
10
 
11
- it { should be(true) }
11
+ it { is_expected.to be(true) }
12
12
  end
13
13
 
14
14
  context 'when input is false' do
15
15
  let(:input) { false }
16
16
 
17
- it { should be(true) }
17
+ it { is_expected.to be(true) }
18
18
  end
19
19
 
20
20
  context 'when input is not coerced' do
21
21
  let(:input) { 1 }
22
22
 
23
- it { should be(false) }
23
+ it { is_expected.to be(false) }
24
24
  end
25
25
  end