rasti-form 0.1.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 +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +131 -0
- data/Rakefile +24 -0
- data/lib/rasti/form/castable.rb +25 -0
- data/lib/rasti/form/errors.rb +41 -0
- data/lib/rasti/form/formatable.rb +19 -0
- data/lib/rasti/form/types/array.rb +36 -0
- data/lib/rasti/form/types/boolean.rb +42 -0
- data/lib/rasti/form/types/enum.rb +38 -0
- data/lib/rasti/form/types/float.rb +33 -0
- data/lib/rasti/form/types/form.rb +40 -0
- data/lib/rasti/form/types/hash.rb +39 -0
- data/lib/rasti/form/types/integer.rb +21 -0
- data/lib/rasti/form/types/regexp.rb +28 -0
- data/lib/rasti/form/types/string.rb +23 -0
- data/lib/rasti/form/types/symbol.rb +23 -0
- data/lib/rasti/form/types/time.rb +40 -0
- data/lib/rasti/form/types/uuid.rb +19 -0
- data/lib/rasti/form/version.rb +5 -0
- data/lib/rasti/form.rb +173 -0
- data/lib/rasti-form.rb +1 -0
- data/rasti-form.gemspec +37 -0
- data/spec/coverage_helper.rb +7 -0
- data/spec/form_spec.rb +310 -0
- data/spec/minitest_helper.rb +13 -0
- data/spec/types/array_spec.rb +22 -0
- data/spec/types/boolean_spec.rb +24 -0
- data/spec/types/enum_spec.rb +20 -0
- data/spec/types/float_spec.rb +18 -0
- data/spec/types/form_spec.rb +41 -0
- data/spec/types/hash_spec.rb +20 -0
- data/spec/types/integer_spec.rb +18 -0
- data/spec/types/regexp_spec.rb +20 -0
- data/spec/types/string_spec.rb +16 -0
- data/spec/types/symbol_spec.rb +16 -0
- data/spec/types/time_spec.rb +25 -0
- data/spec/types/uuid_spec.rb +18 -0
- metadata +228 -0
data/lib/rasti/form.rb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'multi_require'
|
3
|
+
|
4
|
+
module Rasti
|
5
|
+
class Form
|
6
|
+
|
7
|
+
extend MultiRequire
|
8
|
+
|
9
|
+
require_relative_pattern 'form/*'
|
10
|
+
require_relative_pattern 'form/types/*'
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def [](attributes)
|
15
|
+
Class.new(self) do
|
16
|
+
attributes.each do |name, type, options={}|
|
17
|
+
attribute name, type, options
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.inherited(subclass)
|
21
|
+
subclass.instance_variable_set :@attributes, attributes.dup
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def attribute(name, type, options={})
|
27
|
+
attributes[name.to_sym] = options.merge(type: type)
|
28
|
+
attr_reader name
|
29
|
+
end
|
30
|
+
|
31
|
+
def attributes
|
32
|
+
@attributes ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def attribute_names
|
36
|
+
attributes.keys
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
"#{name || self.superclass.name}[#{attribute_names.map(&:inspect).join(', ')}]"
|
41
|
+
end
|
42
|
+
alias_method :inspect, :to_s
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(attrs={})
|
47
|
+
assign_attributes attrs
|
48
|
+
set_defaults
|
49
|
+
validate!
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
"#<#{self.class.name || self.class.superclass.name}[#{to_h.map { |n,v| "#{n}: #{v.inspect}" }.join(', ')}]>"
|
54
|
+
end
|
55
|
+
alias_method :inspect, :to_s
|
56
|
+
|
57
|
+
def attributes
|
58
|
+
assigned_attribute_names.each_with_object({}) do |name, hash|
|
59
|
+
hash[name] = serialize(read_attribute(name))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
alias_method :to_h, :attributes
|
63
|
+
|
64
|
+
def assigned?(name)
|
65
|
+
assigned_attribute_names.include? name
|
66
|
+
end
|
67
|
+
|
68
|
+
def ==(other)
|
69
|
+
other.kind_of?(self.class) && other.attributes == attributes
|
70
|
+
end
|
71
|
+
|
72
|
+
def eql?(other)
|
73
|
+
other.instance_of?(self.class) && other.attributes == attributes
|
74
|
+
end
|
75
|
+
|
76
|
+
def hash
|
77
|
+
[self.class, attributes].hash
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def assign_attributes(attrs={})
|
83
|
+
self.class.attribute_names.each do |name|
|
84
|
+
begin
|
85
|
+
write_attribute name, attrs[name] if attrs.key? name
|
86
|
+
|
87
|
+
rescue CastError => error
|
88
|
+
errors[name] << error.message
|
89
|
+
|
90
|
+
rescue ValidationError => error
|
91
|
+
error.errors.each do |inner_name, inner_errors|
|
92
|
+
inner_errors.each { |message| errors["#{name}.#{inner_name}"] << message }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_defaults
|
99
|
+
(self.class.attribute_names - attributes.keys).each do |name|
|
100
|
+
if self.class.attributes[name].key? :default
|
101
|
+
value = self.class.attributes[name][:default]
|
102
|
+
write_attribute name, value.is_a?(Proc) ? value.call(self) : value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def assigned_attribute_names
|
108
|
+
self.class.attribute_names & instance_variables.map { |v| v.to_s[1..-1].to_sym }
|
109
|
+
end
|
110
|
+
|
111
|
+
def serialize(value)
|
112
|
+
if value.kind_of? Array
|
113
|
+
value.map { |v| serialize v }
|
114
|
+
elsif value.kind_of? Form
|
115
|
+
value.attributes
|
116
|
+
else
|
117
|
+
value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def read_attribute(name)
|
122
|
+
instance_variable_get "@#{name}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def write_attribute(name, value)
|
126
|
+
typed_value = value.nil? ? nil : self.class.attributes[name][:type].cast(value)
|
127
|
+
instance_variable_set "@#{name}", typed_value
|
128
|
+
end
|
129
|
+
|
130
|
+
def fetch(attribute)
|
131
|
+
attribute.to_s.split('.').inject(self) do |target, attr_name|
|
132
|
+
target.nil? ? nil : target.public_send(attr_name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def validate!
|
137
|
+
validate
|
138
|
+
raise ValidationError.new(errors) unless errors.empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
def validate
|
142
|
+
end
|
143
|
+
|
144
|
+
def errors
|
145
|
+
@errors ||= Hash.new { |hash, key| hash[key] = [] }
|
146
|
+
end
|
147
|
+
|
148
|
+
def assert(key, condition, message)
|
149
|
+
return true if condition
|
150
|
+
|
151
|
+
errors[key] << message
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
def assert_present(attribute)
|
156
|
+
assert attribute, !fetch(attribute).nil?, 'not present'
|
157
|
+
end
|
158
|
+
|
159
|
+
def assert_not_empty(attribute)
|
160
|
+
if assert_present attribute
|
161
|
+
value = fetch attribute
|
162
|
+
assert attribute, value.is_a?(String) ? !value.strip.empty? : !value.empty?, 'is empty'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def assert_included_in(attribute, set)
|
167
|
+
if assert_present attribute
|
168
|
+
assert attribute, set.include?(fetch(attribute)), "not included in #{set.map { |e| e.is_a?(::String) ? "'#{e}'" : e.inspect }.join(', ')}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|
data/lib/rasti-form.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'rasti/form'
|
data/rasti-form.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
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
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'rasti-form'
|
8
|
+
spec.version = Rasti::Form::VERSION
|
9
|
+
spec.authors = ['Gabriel Naiman']
|
10
|
+
spec.email = ['gabynaiman@gmail.com']
|
11
|
+
spec.summary = 'Forms validations and type casting'
|
12
|
+
spec.description = 'Forms validations and type casting'
|
13
|
+
spec.homepage = 'https://github.com/gabynaiman/rasti-form'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'multi_require', '~> 1.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 1.12'
|
24
|
+
spec.add_development_dependency 'rake', '~> 11.0'
|
25
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
26
|
+
spec.add_development_dependency 'minitest-colorin', '~> 0.1'
|
27
|
+
spec.add_development_dependency 'minitest-line', '~> 0.6'
|
28
|
+
spec.add_development_dependency 'simplecov', '~> 0.12'
|
29
|
+
spec.add_development_dependency 'coveralls', '~> 0.8'
|
30
|
+
spec.add_development_dependency 'pry-nav', '~> 0.2'
|
31
|
+
|
32
|
+
if RUBY_VERSION < '2'
|
33
|
+
spec.add_development_dependency 'term-ansicolor', '~> 1.3.0'
|
34
|
+
spec.add_development_dependency 'tins', '~> 1.6.0'
|
35
|
+
spec.add_development_dependency 'json', '~> 1.8'
|
36
|
+
end
|
37
|
+
end
|
data/spec/form_spec.rb
ADDED
@@ -0,0 +1,310 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::Form do
|
4
|
+
|
5
|
+
let(:point_class) { Rasti::Form[x: Rasti::Form::Types::Integer, y: Rasti::Form::Types::Integer] }
|
6
|
+
|
7
|
+
let(:point_subclass) { Class.new point_class }
|
8
|
+
|
9
|
+
def build_form(&block)
|
10
|
+
Class.new(Rasti::Form) do
|
11
|
+
class_eval &block
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'Initialization' do
|
16
|
+
|
17
|
+
it 'All attributes' do
|
18
|
+
point = point_class.new x: 1, y: 2
|
19
|
+
point.x.must_equal 1
|
20
|
+
point.y.must_equal 2
|
21
|
+
point.assigned?(:x).must_equal true
|
22
|
+
point.assigned?(:y).must_equal true
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'Some attributes' do
|
26
|
+
point = point_class.new x: 1
|
27
|
+
point.x.must_equal 1
|
28
|
+
point.y.must_be_nil
|
29
|
+
point.assigned?(:x).must_equal true
|
30
|
+
point.assigned?(:y).must_equal false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'Whitout attributes' do
|
34
|
+
point = point_class.new
|
35
|
+
point.x.must_be_nil
|
36
|
+
point.y.must_be_nil
|
37
|
+
point.assigned?(:x).must_equal false
|
38
|
+
point.assigned?(:y).must_equal false
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'Extra attributes' do
|
42
|
+
point = point_class.new z: 3
|
43
|
+
point.x.must_be_nil
|
44
|
+
point.y.must_be_nil
|
45
|
+
point.assigned?(:x).must_equal false
|
46
|
+
point.assigned?(:y).must_equal false
|
47
|
+
proc { point.z }.must_raise NoMethodError
|
48
|
+
point.attributes.must_be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
describe 'Casting' do
|
52
|
+
|
53
|
+
it 'Attribute' do
|
54
|
+
form = build_form do
|
55
|
+
attribute :text, Rasti::Form::Types::String
|
56
|
+
end
|
57
|
+
|
58
|
+
f = form.new text: 123
|
59
|
+
f.text.must_equal "123"
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'Nested attributes' do
|
63
|
+
form = build_form do
|
64
|
+
attribute :range, Rasti::Form::Types::Form[min: Rasti::Form::Types::Integer, max: Rasti::Form::Types::Integer]
|
65
|
+
end
|
66
|
+
|
67
|
+
f = form.new range: {min: '1', max: '10'}
|
68
|
+
f.range.min.must_equal 1
|
69
|
+
f.range.max.must_equal 10
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'Nested form' do
|
73
|
+
range = build_form do
|
74
|
+
attribute :min, Rasti::Form::Types::Integer
|
75
|
+
attribute :max, Rasti::Form::Types::Integer
|
76
|
+
end
|
77
|
+
|
78
|
+
form = build_form do
|
79
|
+
attribute :range, Rasti::Form::Types::Form[range]
|
80
|
+
end
|
81
|
+
|
82
|
+
f = form.new range: {min: '1', max: '10'}
|
83
|
+
f.range.min.must_equal 1
|
84
|
+
f.range.max.must_equal 10
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'Invalid attributes' do
|
88
|
+
form = build_form do
|
89
|
+
attribute :boolean, Rasti::Form::Types::Boolean
|
90
|
+
attribute :number, Rasti::Form::Types::Integer
|
91
|
+
end
|
92
|
+
|
93
|
+
error = proc { form.new boolean: 'x', number: 'y' }.must_raise Rasti::Form::ValidationError
|
94
|
+
error.message.must_equal 'Validation error: {"boolean":["Invalid cast: \'x\' -> Rasti::Form::Types::Boolean"],"number":["Invalid cast: \'y\' -> Rasti::Form::Types::Integer"]}'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'Invalid nested attributes' do
|
98
|
+
form = build_form do
|
99
|
+
attribute :range, Rasti::Form::Types::Form[min: Rasti::Form::Types::Integer, max: Rasti::Form::Types::Integer]
|
100
|
+
end
|
101
|
+
|
102
|
+
error = proc { form.new range: {min: 'x', max: 'y'} }.must_raise Rasti::Form::ValidationError
|
103
|
+
error.message.must_equal "Validation error: {\"range.min\":[\"Invalid cast: 'x' -> Rasti::Form::Types::Integer\"],\"range.max\":[\"Invalid cast: 'y' -> Rasti::Form::Types::Integer\"]}"
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'Invalid form attributes' do
|
107
|
+
range = build_form do
|
108
|
+
attribute :min, Rasti::Form::Types::Integer
|
109
|
+
attribute :max, Rasti::Form::Types::Integer
|
110
|
+
end
|
111
|
+
|
112
|
+
form = build_form do
|
113
|
+
attribute :range, Rasti::Form::Types::Form[range]
|
114
|
+
end
|
115
|
+
|
116
|
+
error = proc { form.new range: {min: 'x', max: 'y'} }.must_raise Rasti::Form::ValidationError
|
117
|
+
error.message.must_equal "Validation error: {\"range.min\":[\"Invalid cast: 'x' -> Rasti::Form::Types::Integer\"],\"range.max\":[\"Invalid cast: 'y' -> Rasti::Form::Types::Integer\"]}"
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
describe 'Defaults' do
|
125
|
+
|
126
|
+
it 'Value' do
|
127
|
+
form = build_form do
|
128
|
+
attribute :text, Rasti::Form::Types::String, default: 'xyz'
|
129
|
+
end
|
130
|
+
|
131
|
+
f = form.new
|
132
|
+
f.text.must_equal 'xyz'
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'Block' do
|
136
|
+
form = build_form do
|
137
|
+
attribute :time_1, Rasti::Form::Types::Time['%F']
|
138
|
+
attribute :time_2, Rasti::Form::Types::Time['%F'], default: ->(f) { f.time_1 }
|
139
|
+
end
|
140
|
+
|
141
|
+
f = form.new time_1: Time.now
|
142
|
+
f.time_2.must_equal f.time_1
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
describe 'Validations' do
|
148
|
+
|
149
|
+
it 'Required' do
|
150
|
+
form = build_form do
|
151
|
+
attribute :text, Rasti::Form::Types::String
|
152
|
+
|
153
|
+
def validate
|
154
|
+
assert_present :text
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
error = proc { form.new }.must_raise Rasti::Form::ValidationError
|
159
|
+
error.message.must_equal 'Validation error: {"text":["not present"]}'
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'Not empty string' do
|
163
|
+
form = build_form do
|
164
|
+
attribute :text, Rasti::Form::Types::String
|
165
|
+
|
166
|
+
def validate
|
167
|
+
assert_not_empty :text
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
error = proc { form.new text: ' ' }.must_raise Rasti::Form::ValidationError
|
172
|
+
error.message.must_equal 'Validation error: {"text":["is empty"]}'
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'Not empty array' do
|
176
|
+
form = build_form do
|
177
|
+
attribute :array, Rasti::Form::Types::Array[Rasti::Form::Types::String]
|
178
|
+
|
179
|
+
def validate
|
180
|
+
assert_not_empty :array
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
error = proc { form.new array: [] }.must_raise Rasti::Form::ValidationError
|
185
|
+
error.message.must_equal 'Validation error: {"array":["is empty"]}'
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'Included in values list' do
|
189
|
+
form = build_form do
|
190
|
+
attribute :text, Rasti::Form::Types::String
|
191
|
+
|
192
|
+
def validate
|
193
|
+
assert_included_in :text, %w(value_1 value_2)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
error = proc { form.new text: 'xyz' }.must_raise Rasti::Form::ValidationError
|
198
|
+
error.message.must_equal 'Validation error: {"text":["not included in \'value_1\', \'value_2\'"]}'
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'Nested form' do
|
202
|
+
form = build_form do
|
203
|
+
attribute :range, Rasti::Form::Types::Form[min: Rasti::Form::Types::Integer, max: Rasti::Form::Types::Integer]
|
204
|
+
|
205
|
+
def validate
|
206
|
+
assert_present 'range.min'
|
207
|
+
assert_present 'range.max'
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
error = proc { form.new }.must_raise Rasti::Form::ValidationError
|
212
|
+
error.message.must_equal 'Validation error: {"range.min":["not present"],"range.max":["not present"]}'
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'Nested validation' do
|
216
|
+
range = build_form do
|
217
|
+
attribute :min, Rasti::Form::Types::Integer
|
218
|
+
attribute :max, Rasti::Form::Types::Integer
|
219
|
+
|
220
|
+
def validate
|
221
|
+
assert :min, min < max, 'Min must be less than Max' if min && max
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
form = build_form do
|
226
|
+
attribute :range, Rasti::Form::Types::Form[range]
|
227
|
+
end
|
228
|
+
|
229
|
+
error = proc { form.new range: {min: 2, max: 1} }.must_raise Rasti::Form::ValidationError
|
230
|
+
error.message.must_equal 'Validation error: {"range.min":["Min must be less than Max"]}'
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
describe 'Comparable' do
|
236
|
+
|
237
|
+
it 'Equivalency (==)' do
|
238
|
+
point_1 = point_class.new x: 1, y: 2
|
239
|
+
point_2 = point_subclass.new x: 1, y: 2
|
240
|
+
point_3 = point_class.new x: 2, y: 1
|
241
|
+
|
242
|
+
assert point_1 == point_2
|
243
|
+
refute point_1 == point_3
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'Equality (eql?)' do
|
247
|
+
point_1 = point_class.new x: 1, y: 2
|
248
|
+
point_2 = point_class.new x: 1, y: 2
|
249
|
+
point_3 = point_subclass.new x: 1, y: 2
|
250
|
+
point_4 = point_class.new x: 2, y: 1
|
251
|
+
|
252
|
+
assert point_1.eql?(point_2)
|
253
|
+
refute point_1.eql?(point_3)
|
254
|
+
refute point_1.eql?(point_4)
|
255
|
+
end
|
256
|
+
|
257
|
+
it 'hash' do
|
258
|
+
point_1 = point_class.new x: 1, y: 2
|
259
|
+
point_2 = point_class.new x: 1, y: 2
|
260
|
+
point_3 = point_subclass.new x: 1, y: 2
|
261
|
+
point_4 = point_class.new x: 2, y: 1
|
262
|
+
|
263
|
+
point_1.hash.must_equal point_2.hash
|
264
|
+
point_1.hash.wont_equal point_3.hash
|
265
|
+
point_1.hash.wont_equal point_4.hash
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'Attributes (to_h)' do
|
271
|
+
t = Rasti::Form::Types
|
272
|
+
address_class = Rasti::Form[street: t::String, number: t::Integer]
|
273
|
+
contact_class = Rasti::Form[
|
274
|
+
name: t::String,
|
275
|
+
age: t::Integer,
|
276
|
+
phones: t::Hash[t::Symbol, t::Integer],
|
277
|
+
addresses: t::Array[t::Form[address_class]]
|
278
|
+
]
|
279
|
+
|
280
|
+
attributes = {
|
281
|
+
name: 'John',
|
282
|
+
age: 24,
|
283
|
+
phones: {
|
284
|
+
office: 1234567890,
|
285
|
+
house: 456456456
|
286
|
+
},
|
287
|
+
addresses: [
|
288
|
+
{street: 'Lexington Avenue', number: 123},
|
289
|
+
{street: 'Park Avenue', number: 456}
|
290
|
+
]
|
291
|
+
}
|
292
|
+
|
293
|
+
contact = contact_class.new attributes
|
294
|
+
|
295
|
+
contact.attributes.must_equal attributes
|
296
|
+
contact.attributes.must_equal contact.to_h
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'to_s' do
|
300
|
+
point_class.to_s.must_equal 'Rasti::Form[:x, :y]'
|
301
|
+
point_class.new(x: '1', y: '2').to_s.must_equal '#<Rasti::Form[x: 1, y: 2]>'
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'Subclass' do
|
305
|
+
point = point_subclass.new x: 1, y: 2
|
306
|
+
point.x.must_equal 1
|
307
|
+
point.y.must_equal 2
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'coverage_helper'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'minitest/colorin'
|
4
|
+
require 'pry-nav'
|
5
|
+
require 'rasti-form'
|
6
|
+
|
7
|
+
module Minitest
|
8
|
+
class Test
|
9
|
+
def as_string(value)
|
10
|
+
value.is_a?(::String) ? "'#{value}'" : value.inspect
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::Form::Types::Array do
|
4
|
+
|
5
|
+
array = [1, '2', Time.now]
|
6
|
+
|
7
|
+
it "#{array.inspect} -> #{array.map(&:to_i)}" do
|
8
|
+
Rasti::Form::Types::Array[Rasti::Form::Types::Integer].cast(array).must_equal array.map(&:to_i)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "#{array.inspect} -> #{array.map(&:to_s)}" do
|
12
|
+
Rasti::Form::Types::Array[Rasti::Form::Types::String].cast(array).must_equal array.map(&:to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
[[nil], nil, 1, 'text', :symbol, {a: 1, b: 2}, Object.new].each do |value|
|
16
|
+
it "#{value.inspect} -> CastError" do
|
17
|
+
error = proc { Rasti::Form::Types::Array[Rasti::Form::Types::String].cast(value) }.must_raise Rasti::Form::CastError
|
18
|
+
error.message.must_equal "Invalid cast: #{as_string(value)} -> Rasti::Form::Types::Array[Rasti::Form::Types::String]"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::Form::Types::Boolean do
|
4
|
+
|
5
|
+
[true, 'true', 'True', 'TRUE', 'T'].each do |value|
|
6
|
+
it "#{value.inspect} -> true" do
|
7
|
+
Rasti::Form::Types::Boolean.cast(value).must_equal true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
[false, 'false', 'False', 'FALSE', 'F'].each do |value|
|
12
|
+
it "#{value.inspect} -> false" do
|
13
|
+
Rasti::Form::Types::Boolean.cast(value).must_equal false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
[nil, 'text', 123, :false, :true, Time.now, [1,2], {a: 1, b: 2}, Object.new].each do |value|
|
18
|
+
it "#{value.inspect} -> CastError" do
|
19
|
+
error = proc { Rasti::Form::Types::Boolean.cast(value) }.must_raise Rasti::Form::CastError
|
20
|
+
error.message.must_equal "Invalid cast: #{as_string(value)} -> Rasti::Form::Types::Boolean"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::Form::Types::Enum do
|
4
|
+
|
5
|
+
enum = Rasti::Form::Types::Enum[:a,:b,:c]
|
6
|
+
|
7
|
+
[:a, 'b', "c"].each do |value|
|
8
|
+
it "#{value.inspect} -> #{enum}" do
|
9
|
+
Rasti::Form::Types::Enum[:a,:b,:c].cast(value).must_equal value.to_s
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
[nil, 'text', :symbol, '999'.to_sym, [1,2], {a: 1, b: 2}, Object.new].each do |value|
|
14
|
+
it "#{value.inspect} -> CastError" do
|
15
|
+
error = proc { Rasti::Form::Types::Enum[:a,:b,:c].cast(value) }.must_raise Rasti::Form::CastError
|
16
|
+
error.message.must_equal "Invalid cast: #{as_string(value)} -> Rasti::Form::Types::Enum[\"a\", \"b\", \"c\"]"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::Form::Types::Float do
|
4
|
+
|
5
|
+
[100, '200', Time.now,2.0,"12.5"].each do |value|
|
6
|
+
it "#{value.inspect} -> #{value.to_i}" do
|
7
|
+
Rasti::Form::Types::Float.cast(value).must_equal value.to_f
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
[nil, 'text', :symbol, '999'.to_sym, [1,2], {a: 1, b: 2}, Object.new, "1.", ".2","."].each do |value|
|
12
|
+
it "#{value.inspect} -> CastError" do
|
13
|
+
error = proc { Rasti::Form::Types::Float.cast(value) }.must_raise Rasti::Form::CastError
|
14
|
+
error.message.must_equal "Invalid cast: #{as_string(value)} -> Rasti::Form::Types::Float"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|