validated_object 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: beeec5e7c798a88c65b4f5a492ce3e59505dacf6a0a1f8de4da90ca59c73f873
4
- data.tar.gz: 3c18ec02b076c71a020eaf7825fa2014321e09ee43a5490373698bfd8deb3c59
3
+ metadata.gz: cbb448bc227821b65819e57ae210f715ea5295bf4c7f2e14925e560340a56a38
4
+ data.tar.gz: 7e78815f3603aedfe4e4eda9cf5e626a64d0ef91f3f237b78a13e110697ce999
5
5
  SHA512:
6
- metadata.gz: 279331de46a2f201ee2eafcb1633ab0ad3b428fa7651276e7754a4e06b6a740f1c473fd14e6296d428d74e3ee39c962bdbcc0e0ebaf6f7bbc7a7fdd393bc56c1
7
- data.tar.gz: afe9a3252da50c7bd028ebbfec81729e0ecf71e0c8c5ff1272d31e616593d41e031a87b11602cfb0337dac3c21d0abad1af024007757c3cab462df66c79fa163
6
+ metadata.gz: 75745c4db0c873e4a4a5f63ca792a8646abcf5402408d0824e9a704e11112d91c5c303de4a4cf6addffd61ed9532f7327f602442c10ab4969b9332a6e3b6e161
7
+ data.tar.gz: 4fa2b7800b2675469d85d5ce9a69f5bf2683fc56376f2bfa0fb623fd0491ad2d1c0833d44f55e124ebf067cc04066a5afd3f7d9ad6ea9d76d87ae00f917b770e
data/.travis.yml CHANGED
@@ -1,6 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.2
4
- - 2.3.1
3
+ - 2.6.6
5
4
  - 2.7.1
6
5
  before_install: gem install bundler
data/README.md CHANGED
@@ -3,6 +3,19 @@
3
3
  # ValidatedObject
4
4
 
5
5
  Plain Old Ruby Objects + Rails Validations = **self-checking Ruby objects**.
6
+ Here's a quick example of a common case: a class with an immutable,
7
+ required, type-checked attribute:
8
+
9
+ ```ruby
10
+ class Person < ValidatedObject::Base
11
+ validated_attr :name, type: String, presence: true
12
+ end
13
+
14
+ # Using it
15
+ me = Person.new(name: 'Robb')
16
+ ```
17
+
18
+ I use classes like these as Data Transfer Objects at my system boundaries.
6
19
 
7
20
 
8
21
  ## Goals
@@ -11,7 +24,7 @@ Plain Old Ruby Objects + Rails Validations = **self-checking Ruby objects**.
11
24
  * Clean, minimal syntax
12
25
 
13
26
  This is a small layer around
14
- [ActiveModel::Validations](http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates). (About 18 lines of code.) So if you know how to use Rails Validations, you're good to go. I wrote this to help with CSV data imports and [website microdata generation](https://github.com/dogweather/schema-dot-org).
27
+ [ActiveModel::Validations](http://api.rubyonrails.org/classes/ActiveModel/Validations/ClassMethods.html#method-i-validates). (About 25 lines of code.) So if you know how to use Rails Validations, you're good to go. I wrote this to help with CSV data imports and [website microdata generation](https://github.com/dogweather/schema-dot-org).
15
28
 
16
29
 
17
30
  ## Usage
@@ -24,7 +37,7 @@ All of the [ActiveModel::Validations](http://api.rubyonrails.org/classes/ActiveM
24
37
  ```ruby
25
38
  class Dog < ValidatedObject::Base
26
39
  # Plain old Ruby
27
- attr_accessor :name, :birthday # attr_reader is supported as well for read-only attributes
40
+ attr_accessor :name, :birthday
28
41
 
29
42
  # Plain old Rails
30
43
  validates :name, presence: true
@@ -34,6 +47,31 @@ class Dog < ValidatedObject::Base
34
47
  end
35
48
  ```
36
49
 
50
+ We can make it immutable with `attr_reader`:
51
+
52
+ ```ruby
53
+ class ImmutableDog < ValidatedObject::Base
54
+ attr_reader :name, :birthday
55
+
56
+ validates :name, presence: true
57
+ validates :birthday, type: Date, allow_nil: true
58
+ end
59
+ ```
60
+
61
+ > `attr_reader` followed by `validates` is such a common pattern that
62
+ > there's a second DSL which wraps them up into one call: `validates_attr`.
63
+
64
+ Here's the immutable version of `Dog` re-written with the simplified DSL:
65
+
66
+ ```ruby
67
+ class ImmutableDog < ValidatedObject::Base
68
+ validates_attr :name, presence: true
69
+ validates_attr :birthday, type: Date, allow_nil: true
70
+ end
71
+ ```
72
+
73
+ ### About that `type:` check
74
+
37
75
  The included `TypeValidator` is what enables `type: Date`, above. All classes can be checked, as well as a pseudo-class `Boolean`. E.g.:
38
76
 
39
77
  ```ruby
@@ -0,0 +1,18 @@
1
+ require "active_support/concern"
2
+
3
+ # Enable a simplified API for the common case of
4
+ # read-only ValidatedObjects.
5
+ module ValidatedObject
6
+ module SimplifiedApi
7
+ extend ActiveSupport::Concern
8
+
9
+ class_methods do
10
+ # Simply delegate to `attr_reader` and `validates`.
11
+ def validated_attr(attribute, *options)
12
+ attr_reader attribute
13
+ validates attribute, *options
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ValidatedObject
5
- VERSION = '2.1.0'
5
+ VERSION = '2.3.0'
6
6
  end
@@ -4,6 +4,8 @@
4
4
  require 'active_model'
5
5
  require 'sorbet-runtime'
6
6
  require 'validated_object/version'
7
+ require 'validated_object/simplified_api'
8
+
7
9
 
8
10
  module ValidatedObject
9
11
  # @abstract Subclass and add `attr_accessor` and validations
@@ -43,6 +45,7 @@ module ValidatedObject
43
45
  # @see http://www.rubyinside.com/rails-3-0s-activemodel-how-to-give-ruby-classes-some-activerecord-magic-2937.html Rails 3.0′s ActiveModel: How To Give Ruby Classes Some ActiveRecord Magic, Peter Cooper
44
46
  class Base
45
47
  include ActiveModel::Validations
48
+ include SimplifiedApi
46
49
  extend T::Sig
47
50
 
48
51
  SymbolHash = T.type_alias { T::Hash[Symbol, T.untyped] }
@@ -63,18 +66,23 @@ module ValidatedObject
63
66
  #
64
67
  # @raise [ArgumentError] if the object is not valid at the
65
68
  # end of initialization or `attributes` is not a Hash.
66
- sig { params(attributes: SymbolHash).returns(ValidatedObject::Base)}
69
+ sig { params(attributes: SymbolHash).void }
67
70
  def initialize(attributes = EMPTY_HASH)
68
71
  set_instance_variables from_hash: attributes
69
72
  check_validations!
70
- self
73
+ nil
74
+ end
75
+
76
+ def validated_attr(attribute_name, **validation_options)
77
+ attr_reader attribute_name
78
+ validates attribute_name, validation_options
71
79
  end
72
80
 
73
81
  # Run any validations and raise an error if invalid.
74
82
  #
75
83
  # @raise [ArgumentError] if any validations fail.
76
84
  # @return [ValidatedObject::Base] the receiver
77
- sig {returns(ValidatedObject::Base)}
85
+ sig { returns(ValidatedObject::Base) }
78
86
  def check_validations!
79
87
  raise ArgumentError, errors.full_messages.join('; ') if invalid?
80
88
 
@@ -118,17 +126,17 @@ module ValidatedObject
118
126
 
119
127
  private
120
128
 
121
- sig {params(expected_class: T.untyped, value: T.untyped).returns(T.untyped)}
129
+ sig { params(expected_class: T.untyped, value: T.untyped).returns(T.untyped) }
122
130
  def pseudo_boolean?(expected_class, value)
123
131
  expected_class == Boolean && boolean?(value)
124
132
  end
125
133
 
126
- sig {params(expected_class: T.untyped, value: T.untyped).returns(T.untyped)}
134
+ sig { params(expected_class: T.untyped, value: T.untyped).returns(T.untyped) }
127
135
  def expected_class?(expected_class, value)
128
136
  value.is_a?(expected_class)
129
137
  end
130
138
 
131
- sig {params(value: T.untyped).returns(T.untyped)}
139
+ sig { params(value: T.untyped).returns(T.untyped) }
132
140
  def boolean?(value)
133
141
  value.is_a?(TrueClass) || value.is_a?(FalseClass)
134
142
  end
@@ -150,7 +158,7 @@ module ValidatedObject
150
158
 
151
159
  private
152
160
 
153
- sig {params(from_hash: SymbolHash).void}
161
+ sig { params(from_hash: SymbolHash).void }
154
162
  def set_instance_variables(from_hash:)
155
163
  from_hash.each do |variable_name, variable_value|
156
164
  # Test for the attribute reader
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validated_object
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robb Shecter
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-04 00:00:00.000000000 Z
11
+ date: 2023-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -98,6 +98,7 @@ files:
98
98
  - bin/console
99
99
  - bin/setup
100
100
  - lib/validated_object.rb
101
+ - lib/validated_object/simplified_api.rb
101
102
  - lib/validated_object/version.rb
102
103
  - script/demo.rb
103
104
  - sorbet/config
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  - !ruby/object:Gem::Version
140
141
  version: '0'
141
142
  requirements: []
142
- rubygems_version: 3.1.4
143
+ rubygems_version: 3.4.20
143
144
  signing_key:
144
145
  specification_version: 4
145
146
  summary: Self-validating plain Ruby objects.