virtus 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
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