rasti-form 3.1.2 → 6.0.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +36 -0
- data/README.md +6 -61
- data/lib/rasti/form/errors.rb +6 -52
- data/lib/rasti/form/validable.rb +10 -7
- data/lib/rasti/form.rb +40 -131
- data/rasti-form.gemspec +2 -12
- data/spec/coverage_helper.rb +1 -3
- data/spec/minitest_helper.rb +1 -7
- data/spec/validations_spec.rb +333 -0
- metadata +19 -48
- data/.travis.yml +0 -20
- data/lib/rasti/form/castable.rb +0 -25
- data/lib/rasti/form/types/array.rb +0 -54
- data/lib/rasti/form/types/boolean.rb +0 -42
- data/lib/rasti/form/types/enum.rb +0 -48
- data/lib/rasti/form/types/float.rb +0 -33
- data/lib/rasti/form/types/form.rb +0 -40
- data/lib/rasti/form/types/hash.rb +0 -39
- data/lib/rasti/form/types/integer.rb +0 -21
- data/lib/rasti/form/types/io.rb +0 -23
- data/lib/rasti/form/types/regexp.rb +0 -23
- data/lib/rasti/form/types/string.rb +0 -40
- data/lib/rasti/form/types/symbol.rb +0 -23
- data/lib/rasti/form/types/time.rb +0 -40
- data/lib/rasti/form/types/uuid.rb +0 -7
- data/lib/rasti/form/version.rb +0 -5
- data/spec/form_spec.rb +0 -411
- data/spec/types/array_spec.rb +0 -54
- data/spec/types/boolean_spec.rb +0 -24
- data/spec/types/enum_spec.rb +0 -28
- data/spec/types/float_spec.rb +0 -18
- data/spec/types/form_spec.rb +0 -41
- data/spec/types/hash_spec.rb +0 -20
- data/spec/types/integer_spec.rb +0 -18
- data/spec/types/io_spec.rb +0 -18
- data/spec/types/regexp_spec.rb +0 -18
- data/spec/types/string_formatted_spec.rb +0 -20
- data/spec/types/string_spec.rb +0 -16
- data/spec/types/symbol_spec.rb +0 -16
- data/spec/types/time_spec.rb +0 -25
- data/spec/types/uuid_spec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efb9cfdea85fcce07f977a6bf54a183aface5cce17bfa3f9720f0b60684f48c3
|
4
|
+
data.tar.gz: f1aa1973dad9c2f363bd8f9f030ca48fd158a9897821439ab9e0d93c334a79de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38d34d94241efe87628538d8f6e59baf91e4f46ecf4afe72aebaddf69136de15e2cde9713329cb20d3fb80d8bbb9cbcb52c9041972f949051b925b89b3ce42fb
|
7
|
+
data.tar.gz: cd250e2f305c5836e6b70b6d1e3d0a0d964311af5ac68d5b033461d74b9461d8a4b1cc1948cc6c7d3e7b1bd399a980d5c84765aeced72cadee7d51408dc35d45
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: CI
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ master ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ master ]
|
15
|
+
|
16
|
+
permissions:
|
17
|
+
contents: read
|
18
|
+
|
19
|
+
jobs:
|
20
|
+
test:
|
21
|
+
|
22
|
+
runs-on: ubuntu-latest
|
23
|
+
name: Tests
|
24
|
+
strategy:
|
25
|
+
matrix:
|
26
|
+
ruby-version: ['2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', 'jruby-9.2.9.0']
|
27
|
+
|
28
|
+
steps:
|
29
|
+
- uses: actions/checkout@v3
|
30
|
+
- name: Set up Ruby
|
31
|
+
uses: ruby/setup-ruby@v1
|
32
|
+
with:
|
33
|
+
ruby-version: ${{ matrix.ruby-version }}
|
34
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
35
|
+
- name: Run tests
|
36
|
+
run: bundle exec rake
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# Rasti::Form
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/rasti-form)
|
4
|
-
[](https://github.com/gabynaiman/rasti-form/actions/workflows/ci.yml)
|
5
5
|
[](https://coveralls.io/github/gabynaiman/rasti-form?branch=master)
|
6
6
|
[](https://codeclimate.com/github/gabynaiman/rasti-form)
|
7
7
|
|
8
|
-
Forms validations
|
8
|
+
Forms validations
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
@@ -25,37 +25,18 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
## Usage
|
27
27
|
|
28
|
-
```ruby
|
29
|
-
T = Rasti::Form::Types
|
30
|
-
```
|
31
|
-
|
32
|
-
### Type casting
|
33
|
-
|
34
|
-
```ruby
|
35
|
-
T::Integer.cast '10' # => 10
|
36
|
-
T::Integer.cast '10.5' # => 10
|
37
|
-
T::Integer.cast 'text' # => Rasti::Types::CastError: Invalid cast: 'text' -> Rasti::Types::Integer
|
38
|
-
|
39
|
-
T::Boolean.cast 'true' # => true
|
40
|
-
T::Boolean.cast 'FALSE' # => false
|
41
|
-
T::Boolean.cast 'text' # => Rasti::Types::CastError: Invalid cast: 'text' -> Rasti::Types::Boolean
|
42
|
-
|
43
|
-
T::Time['%Y-%m-%d'].cast '2016-10-22' # => 2016-10-22 00:00:00 -0300
|
44
|
-
T::Time['%Y-%m-%d'].cast '2016-10' # => Rasti::Types::CastError: Invalid cast: '2016-10' -> Rasti::Types::Time['%Y-%m-%d']
|
45
|
-
|
46
|
-
T::Array[T::Symbol].cast [1, 'test', :sym] # => [:"1", :test, :sym]
|
47
|
-
```
|
48
|
-
|
49
28
|
### Form type coercion
|
50
29
|
|
51
30
|
```ruby
|
31
|
+
T = Rasti::Types
|
32
|
+
|
52
33
|
PointForm = Rasti::Form[x: T::Integer, y: T::Integer] # => PointForm[:x, :y]
|
53
34
|
form = PointForm.new x: '1', y: 2 # => #<PointForm[x: 1, y: 2]>
|
54
35
|
form.x # => 1
|
55
36
|
form.y # => 2
|
56
|
-
form.
|
37
|
+
form.to_h # => {x: 1, y: 2}
|
57
38
|
|
58
|
-
PointForm.new x: true # => Validation error: {"x":["Invalid cast: true -> Rasti::
|
39
|
+
PointForm.new x: true # => Validation error: {"x":["Invalid cast: true -> Rasti::Types::Integer"]}
|
59
40
|
```
|
60
41
|
|
61
42
|
### Form validations
|
@@ -84,42 +65,6 @@ form.from # => 2016-10-20 00:00:00 -0300
|
|
84
65
|
form.to # => 2016-10-28 00:00:00 -0300
|
85
66
|
```
|
86
67
|
|
87
|
-
### Built-in types
|
88
|
-
|
89
|
-
- Array
|
90
|
-
- Boolean
|
91
|
-
- Enum
|
92
|
-
- Float
|
93
|
-
- Form
|
94
|
-
- Hash
|
95
|
-
- Integer
|
96
|
-
- IO
|
97
|
-
- Regexp
|
98
|
-
- String
|
99
|
-
- Symbol
|
100
|
-
- Time
|
101
|
-
- UUID
|
102
|
-
|
103
|
-
### Plugable types
|
104
|
-
|
105
|
-
```ruby
|
106
|
-
class CustomType
|
107
|
-
class << self
|
108
|
-
extend Castable
|
109
|
-
|
110
|
-
private
|
111
|
-
|
112
|
-
def valid?(value)
|
113
|
-
valid.is_a?(String)
|
114
|
-
end
|
115
|
-
|
116
|
-
def transform(value)
|
117
|
-
value.upcase
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
```
|
122
|
-
|
123
68
|
## Contributing
|
124
69
|
|
125
70
|
Bug reports and pull requests are welcome on GitHub at https://github.com/gabynaiman/rasti-form.
|
data/lib/rasti/form/errors.rb
CHANGED
@@ -1,65 +1,19 @@
|
|
1
1
|
module Rasti
|
2
2
|
class Form
|
3
|
-
|
4
|
-
class CastError < StandardError
|
5
3
|
|
6
|
-
|
4
|
+
class ValidationError < Rasti::Types::CompoundError
|
7
5
|
|
8
|
-
|
9
|
-
@type = type
|
10
|
-
@value = value
|
11
|
-
end
|
12
|
-
|
13
|
-
def message
|
14
|
-
"Invalid cast: #{display_value} -> #{type}"
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def display_value
|
20
|
-
value.is_a?(::String) ? "'#{value}'" : value.inspect
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
class MultiCastError < StandardError
|
6
|
+
attr_reader :scope
|
27
7
|
|
28
|
-
attr_reader :type, :value, :errors
|
29
|
-
|
30
|
-
def initialize(type, value, errors)
|
31
|
-
@type = type
|
32
|
-
@value = value
|
33
|
-
@errors = errors
|
34
|
-
end
|
35
|
-
|
36
|
-
def message
|
37
|
-
"Invalid cast: #{display_value} -> #{type} - #{JSON.dump(errors)}"
|
38
|
-
end
|
39
|
-
|
40
|
-
def display_value
|
41
|
-
value.is_a?(::String) ? "'#{value}'" : value.inspect
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
class ValidationError < StandardError
|
47
|
-
|
48
|
-
attr_reader :scope, :errors
|
49
|
-
|
50
8
|
def initialize(scope, errors)
|
51
9
|
@scope = scope
|
52
|
-
|
10
|
+
super errors
|
53
11
|
end
|
54
12
|
|
55
|
-
|
56
|
-
lines = ['Validation errors:']
|
57
|
-
|
58
|
-
errors.each do |key, value|
|
59
|
-
lines << "- #{key}: #{value}"
|
60
|
-
end
|
13
|
+
private
|
61
14
|
|
62
|
-
|
15
|
+
def message_title
|
16
|
+
'Validation errors:'
|
63
17
|
end
|
64
18
|
|
65
19
|
end
|
data/lib/rasti/form/validable.rb
CHANGED
@@ -10,24 +10,27 @@ module Rasti
|
|
10
10
|
|
11
11
|
def validate!
|
12
12
|
validate
|
13
|
-
|
13
|
+
raise_if_errors!
|
14
14
|
end
|
15
15
|
|
16
16
|
def validate
|
17
17
|
end
|
18
18
|
|
19
|
+
def raise_if_errors!
|
20
|
+
raise ValidationError.new(self, errors) unless errors.empty?
|
21
|
+
end
|
22
|
+
|
19
23
|
def assert(key, condition, message)
|
20
|
-
|
21
|
-
|
22
|
-
errors[key] << message
|
23
|
-
false
|
24
|
+
errors[key] << message unless condition
|
25
|
+
condition
|
24
26
|
end
|
25
27
|
|
26
28
|
def assert_not_error(key)
|
27
29
|
yield
|
28
30
|
true
|
29
|
-
rescue =>
|
30
|
-
|
31
|
+
rescue => ex
|
32
|
+
errors[key] << ex.message
|
33
|
+
false
|
31
34
|
end
|
32
35
|
|
33
36
|
end
|
data/lib/rasti/form.rb
CHANGED
@@ -1,171 +1,80 @@
|
|
1
|
-
require '
|
1
|
+
require 'rasti-model'
|
2
2
|
require 'multi_require'
|
3
3
|
|
4
4
|
module Rasti
|
5
|
-
class Form
|
5
|
+
class Form < Rasti::Model
|
6
6
|
|
7
7
|
extend MultiRequire
|
8
8
|
|
9
9
|
require_relative_pattern 'form/*'
|
10
|
-
require_relative_pattern 'form/types/*'
|
11
10
|
|
12
11
|
include Validable
|
13
12
|
|
14
|
-
|
13
|
+
alias_method :__initialize__, :initialize
|
14
|
+
private :__initialize__
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
attribute name, type, options
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def inherited(subclass)
|
25
|
-
subclass.instance_variable_set :@attributes, attributes.dup
|
26
|
-
end
|
27
|
-
|
28
|
-
def attribute(name, type, options={})
|
29
|
-
attributes[name.to_sym] = options.merge(type: type)
|
30
|
-
attr_reader name
|
31
|
-
end
|
32
|
-
|
33
|
-
def attributes
|
34
|
-
@attributes ||= {}
|
35
|
-
end
|
36
|
-
|
37
|
-
def attribute_names
|
38
|
-
attributes.keys
|
39
|
-
end
|
40
|
-
|
41
|
-
def to_s
|
42
|
-
"#{name || self.superclass.name}[#{attribute_names.map(&:inspect).join(', ')}]"
|
43
|
-
end
|
44
|
-
alias_method :inspect, :to_s
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
def initialize(attrs={})
|
49
|
-
assign_attributes attrs
|
50
|
-
set_defaults
|
16
|
+
def initialize(attributes={})
|
17
|
+
assign_attributes! attributes
|
18
|
+
validate_type_casting!
|
51
19
|
validate!
|
52
20
|
end
|
53
21
|
|
54
|
-
def
|
55
|
-
|
56
|
-
end
|
57
|
-
alias_method :inspect, :to_s
|
58
|
-
|
59
|
-
def attributes(options={})
|
60
|
-
attributes_filter = {only: assigned_attribute_names, except: []}.merge(options)
|
61
|
-
(attributes_filter[:only] - attributes_filter[:except]).each_with_object({}) do |name, hash|
|
62
|
-
hash[name] = serialize(read_attribute(name))
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_h
|
67
|
-
attributes
|
68
|
-
end
|
69
|
-
|
70
|
-
def assigned?(name)
|
71
|
-
assigned_attribute_names.include? name
|
72
|
-
end
|
73
|
-
|
74
|
-
def ==(other)
|
75
|
-
other.kind_of?(self.class) && other.attributes == attributes
|
76
|
-
end
|
77
|
-
|
78
|
-
def eql?(other)
|
79
|
-
other.instance_of?(self.class) && other.attributes == attributes
|
80
|
-
end
|
81
|
-
|
82
|
-
def hash
|
83
|
-
[self.class, attributes].hash
|
22
|
+
def assigned?(attr_name)
|
23
|
+
assigned_attribute? attr_name.to_sym
|
84
24
|
end
|
85
25
|
|
86
26
|
private
|
87
27
|
|
88
|
-
def assign_attributes(
|
89
|
-
|
90
|
-
attr_name = name.to_sym
|
91
|
-
begin
|
92
|
-
if self.class.attributes.key? attr_name
|
93
|
-
write_attribute attr_name, value
|
94
|
-
else
|
95
|
-
errors[attr_name] << 'unexpected attribute'
|
96
|
-
end
|
97
|
-
|
98
|
-
rescue CastError => error
|
99
|
-
errors[attr_name] << error.message
|
100
|
-
|
101
|
-
rescue MultiCastError, ValidationError => error
|
102
|
-
error.errors.each do |inner_name, inner_errors|
|
103
|
-
inner_errors.each { |message| errors["#{attr_name}.#{inner_name}"] << message }
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
28
|
+
def assign_attributes!(attributes)
|
29
|
+
__initialize__ attributes
|
108
30
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
value = self.class.attributes[name][:default]
|
113
|
-
write_attribute name, value.is_a?(Proc) ? value.call(self) : value
|
114
|
-
end
|
31
|
+
rescue Rasti::Model::UnexpectedAttributesError => ex
|
32
|
+
ex.attributes.each do |attr_name|
|
33
|
+
errors[attr_name] << 'unexpected attribute'
|
115
34
|
end
|
116
|
-
end
|
117
35
|
|
118
|
-
|
119
|
-
|
36
|
+
ensure
|
37
|
+
raise_if_errors!
|
120
38
|
end
|
121
39
|
|
122
|
-
def
|
123
|
-
|
124
|
-
value.map { |v| serialize v }
|
125
|
-
elsif value.kind_of? Form
|
126
|
-
value.attributes
|
127
|
-
else
|
128
|
-
value
|
129
|
-
end
|
130
|
-
end
|
40
|
+
def validate_type_casting!
|
41
|
+
cast_attributes!
|
131
42
|
|
132
|
-
|
133
|
-
|
134
|
-
|
43
|
+
rescue Rasti::Types::CompoundError => ex
|
44
|
+
ex.errors.each do |key, messages|
|
45
|
+
errors[key] += messages
|
46
|
+
end
|
135
47
|
|
136
|
-
|
137
|
-
|
138
|
-
instance_variable_set "@#{name}", typed_value
|
48
|
+
ensure
|
49
|
+
raise_if_errors!
|
139
50
|
end
|
140
51
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
52
|
+
def assert_present(attr_name)
|
53
|
+
if !errors.key?(attr_name)
|
54
|
+
assert attr_name, assigned?(attr_name) && !public_send(attr_name).nil?, 'not present'
|
144
55
|
end
|
145
56
|
end
|
146
57
|
|
147
|
-
def
|
148
|
-
assert
|
149
|
-
end
|
150
|
-
|
151
|
-
def assert_not_present(attribute)
|
152
|
-
assert attribute, fetch(attribute).nil?, 'is present'
|
58
|
+
def assert_not_present(attr_name)
|
59
|
+
assert attr_name, !assigned?(attr_name) || public_send(attr_name).nil?, 'is present'
|
153
60
|
end
|
154
61
|
|
155
|
-
def assert_not_empty(
|
156
|
-
if assert_present
|
157
|
-
value =
|
158
|
-
assert
|
62
|
+
def assert_not_empty(attr_name)
|
63
|
+
if assert_present attr_name
|
64
|
+
value = public_send attr_name
|
65
|
+
assert attr_name, value.is_a?(String) ? !value.strip.empty? : !value.empty?, 'is empty'
|
159
66
|
end
|
160
67
|
end
|
161
68
|
|
162
|
-
def
|
163
|
-
|
69
|
+
def assert_included_in(attr_name, set)
|
70
|
+
if assert_present attr_name
|
71
|
+
assert attr_name, set.include?(public_send(attr_name)), "not included in #{set.map(&:inspect).join(', ')}"
|
72
|
+
end
|
164
73
|
end
|
165
74
|
|
166
|
-
def
|
167
|
-
if assert_present
|
168
|
-
assert
|
75
|
+
def assert_range(attr_name_from, attr_name_to)
|
76
|
+
if assert_present(attr_name_from) && assert_present(attr_name_to)
|
77
|
+
assert attr_name_from, public_send(attr_name_from) <= public_send(attr_name_to), 'invalid range'
|
169
78
|
end
|
170
79
|
end
|
171
80
|
|
data/rasti-form.gemspec
CHANGED
@@ -1,11 +1,6 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'rasti/form/version'
|
5
|
-
|
6
1
|
Gem::Specification.new do |spec|
|
7
2
|
spec.name = 'rasti-form'
|
8
|
-
spec.version =
|
3
|
+
spec.version = '6.0.0'
|
9
4
|
spec.authors = ['Gabriel Naiman']
|
10
5
|
spec.email = ['gabynaiman@gmail.com']
|
11
6
|
spec.summary = 'Forms validations and type casting'
|
@@ -19,6 +14,7 @@ Gem::Specification.new do |spec|
|
|
19
14
|
spec.require_paths = ['lib']
|
20
15
|
|
21
16
|
spec.add_runtime_dependency 'multi_require', '~> 1.0'
|
17
|
+
spec.add_runtime_dependency 'rasti-model', '~> 2.0'
|
22
18
|
|
23
19
|
spec.add_development_dependency 'rake', '~> 12.0'
|
24
20
|
spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
|
@@ -27,10 +23,4 @@ Gem::Specification.new do |spec|
|
|
27
23
|
spec.add_development_dependency 'simplecov', '~> 0.12'
|
28
24
|
spec.add_development_dependency 'coveralls', '~> 0.8'
|
29
25
|
spec.add_development_dependency 'pry-nav', '~> 0.2'
|
30
|
-
|
31
|
-
if RUBY_VERSION < '2'
|
32
|
-
spec.add_development_dependency 'term-ansicolor', '~> 1.3.0'
|
33
|
-
spec.add_development_dependency 'tins', '~> 1.6.0'
|
34
|
-
spec.add_development_dependency 'json', '~> 1.8'
|
35
|
-
end
|
36
26
|
end
|
data/spec/coverage_helper.rb
CHANGED