typero 0.5.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/lib/adapters/sequel.rb +11 -33
- data/lib/typero.rb +8 -189
- data/lib/typero/params.rb +126 -0
- data/lib/typero/schema.rb +154 -0
- data/lib/typero/type/type.rb +125 -0
- data/lib/typero/type/types/boolean_type.rb +26 -0
- data/lib/typero/type/types/currency_type.rb +21 -0
- data/lib/typero/type/types/date_type.rb +35 -0
- data/lib/typero/type/types/datetime_type.rb +16 -0
- data/lib/typero/type/types/email_type.rb +20 -0
- data/lib/typero/type/types/float_type.rb +24 -0
- data/lib/typero/type/types/hash_type.rb +31 -0
- data/lib/typero/type/types/image_type.rb +22 -0
- data/lib/typero/type/types/integer_type.rb +16 -0
- data/lib/typero/type/types/label_type.rb +20 -0
- data/lib/typero/type/types/locale_type.rb +13 -0
- data/lib/typero/type/types/model_type.rb +23 -0
- data/lib/typero/type/{oib.rb → types/oib_type.rb} +16 -18
- data/lib/typero/type/types/point_type.rb +25 -0
- data/lib/typero/type/types/string_type.rb +20 -0
- data/lib/typero/type/types/text_type.rb +10 -0
- data/lib/typero/type/types/time_type.rb +5 -0
- data/lib/typero/type/types/timezone_type.rb +15 -0
- data/lib/typero/type/types/url_type.rb +13 -0
- data/lib/typero/typero.rb +98 -0
- metadata +33 -27
- data/lib/typero/type.rb +0 -32
- data/lib/typero/type/array.rb +0 -55
- data/lib/typero/type/boolean.rb +0 -16
- data/lib/typero/type/currency.rb +0 -17
- data/lib/typero/type/date.rb +0 -6
- data/lib/typero/type/datetime.rb +0 -6
- data/lib/typero/type/email.rb +0 -27
- data/lib/typero/type/float.rb +0 -27
- data/lib/typero/type/geography.rb +0 -13
- data/lib/typero/type/hash.rb +0 -18
- data/lib/typero/type/integer.rb +0 -25
- data/lib/typero/type/label.rb +0 -20
- data/lib/typero/type/point.rb +0 -25
- data/lib/typero/type/string.rb +0 -29
- data/lib/typero/type/text.rb +0 -7
- data/lib/typero/type/url.rb +0 -20
@@ -0,0 +1,125 @@
|
|
1
|
+
# Master class
|
2
|
+
|
3
|
+
module Typero
|
4
|
+
class Type
|
5
|
+
ERRORS = {
|
6
|
+
en: {
|
7
|
+
# errors shared between various types
|
8
|
+
min_length_error: 'min lenght is %s, you have %s',
|
9
|
+
max_length_error: 'max lenght is %s, you have %s',
|
10
|
+
min_value_error: 'min is %s, got %s',
|
11
|
+
max_value_error: 'max is %s, got %s',
|
12
|
+
unallowed_characters_error: 'is having unallowed characters',
|
13
|
+
not_in_range: 'Value in not in allowed range (%s)'
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
# default shared allowed opts keys
|
18
|
+
OPTS = {}
|
19
|
+
OPTS_KEYS = [
|
20
|
+
:allow,
|
21
|
+
:allowed,
|
22
|
+
:array,
|
23
|
+
:default,
|
24
|
+
:description,
|
25
|
+
:max_count,
|
26
|
+
:meta,
|
27
|
+
:min_count,
|
28
|
+
:model,
|
29
|
+
:name,
|
30
|
+
:req,
|
31
|
+
:required,
|
32
|
+
:type,
|
33
|
+
:values
|
34
|
+
]
|
35
|
+
|
36
|
+
attr_reader :opts
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def load name
|
40
|
+
klass = 'Typero::%sType' % name.to_s.gsub(/[^\w]/,'').classify
|
41
|
+
|
42
|
+
if const_defined? klass
|
43
|
+
klass.constantize
|
44
|
+
else
|
45
|
+
raise ArgumentError, 'Typero type "%s" is not defined (%s)' % [name, klass]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def error locale, key, message
|
50
|
+
locale = locale.to_sym
|
51
|
+
ERRORS[locale] ||= {}
|
52
|
+
ERRORS[locale][key.to_sym] = message
|
53
|
+
end
|
54
|
+
|
55
|
+
def opts key, desc
|
56
|
+
OPTS_KEYS.push key unless OPTS_KEYS.include?(key)
|
57
|
+
|
58
|
+
OPTS[self] ||= {}
|
59
|
+
OPTS[self][key] = desc
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
###
|
64
|
+
|
65
|
+
def initialize value, opts={}, &block
|
66
|
+
value = value.strip.rstrip if value.is_a?(String)
|
67
|
+
|
68
|
+
@value = value
|
69
|
+
@opts = opts
|
70
|
+
@block = block
|
71
|
+
end
|
72
|
+
|
73
|
+
def value &block
|
74
|
+
if block_given?
|
75
|
+
@value = block.call @value
|
76
|
+
else
|
77
|
+
@value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get
|
82
|
+
if value.nil?
|
83
|
+
opts[:default].nil? ? default : opts[:default]
|
84
|
+
else
|
85
|
+
set
|
86
|
+
error_for(:not_in_range, opts[:values].join(', ')) if opts[:values] && !opts[:values].include?(@value)
|
87
|
+
value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def default
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def db_field
|
96
|
+
out = db_schema
|
97
|
+
out[1] ||= {}
|
98
|
+
out[1][:default] ||= opts[:default] unless opts[:default].nil?
|
99
|
+
out[1][:null] = false if !opts[:array] && opts[:required]
|
100
|
+
out
|
101
|
+
end
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
# get error from option or the default one
|
106
|
+
def error_for name, *args
|
107
|
+
locale =
|
108
|
+
if defined?(Lux)
|
109
|
+
Lux.current.locale.to_s
|
110
|
+
elsif defined?(I18n)
|
111
|
+
I18n.locale
|
112
|
+
end
|
113
|
+
|
114
|
+
locale = :en if locale.to_s == ''
|
115
|
+
pointer = ERRORS[locale.to_sym] || ERRORS[:en]
|
116
|
+
error = @opts.dig(:meta, locale, name) || @opts.dig(:meta, name) || pointer[name]
|
117
|
+
error = error % args if args.first
|
118
|
+
|
119
|
+
raise 'Type error :%s not defined' % name unless error
|
120
|
+
raise TypeError.new(error)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Typero::BooleanType < Typero::Type
|
2
|
+
error :en, :unsupported_boolean, 'Unsupported boolean param value: %s'
|
3
|
+
|
4
|
+
def set
|
5
|
+
value do |_|
|
6
|
+
bool = _.to_s
|
7
|
+
|
8
|
+
if value == ''
|
9
|
+
false
|
10
|
+
elsif %w(true 1 on).include?(bool)
|
11
|
+
true
|
12
|
+
elsif %w(false 0 off).include?(bool)
|
13
|
+
false
|
14
|
+
else
|
15
|
+
error_for :unsupported_boolean, bool
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def db_schema
|
21
|
+
[:boolean, {
|
22
|
+
default: opts[:default] || false
|
23
|
+
}]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# you should not use this filed for currency calculations
|
2
|
+
# use integer and covert in code
|
3
|
+
# Example: use cents and divide by 100 for $
|
4
|
+
|
5
|
+
require_relative './float_type'
|
6
|
+
|
7
|
+
class Typero::CurrencyType < Typero::FloatType
|
8
|
+
|
9
|
+
def set
|
10
|
+
value { |data| data.to_f.round(2) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def db_schema
|
14
|
+
[:decimal, {
|
15
|
+
precision: 8,
|
16
|
+
scale: 2
|
17
|
+
}]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Typero::DateType < Typero::Type
|
2
|
+
error :en, :min_date, 'Minimal allowed date is %s'
|
3
|
+
error :en, :max_date, 'Maximal allowed date is %s'
|
4
|
+
|
5
|
+
def set
|
6
|
+
unless [Date].include?(value.class)
|
7
|
+
value { |data| DateTime.parse(data) }
|
8
|
+
end
|
9
|
+
|
10
|
+
value { |data| DateTime.new(data.year, data.month, data.day) }
|
11
|
+
|
12
|
+
check_date_min_max
|
13
|
+
end
|
14
|
+
|
15
|
+
def db_schema
|
16
|
+
[:date, {}]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def check_date_min_max
|
22
|
+
if min = opts[:min]
|
23
|
+
min = DateTime.parse(min)
|
24
|
+
error_for(:min_date, min) % min if min > value
|
25
|
+
end
|
26
|
+
|
27
|
+
if max = opts[:max]
|
28
|
+
max = DateTime.parse(max)
|
29
|
+
error_for(:max_date, max) % max if value > max
|
30
|
+
end
|
31
|
+
|
32
|
+
value
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'date_type'
|
2
|
+
|
3
|
+
class Typero::DatetimeType < Typero::DateType
|
4
|
+
def set
|
5
|
+
unless [Time, DateTime].include?(value.class)
|
6
|
+
value { |data| DateTime.parse(data) }
|
7
|
+
end
|
8
|
+
|
9
|
+
check_date_min_max
|
10
|
+
end
|
11
|
+
|
12
|
+
def db_schema
|
13
|
+
[:datetime]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Typero::EmailType < Typero::Type
|
2
|
+
error :en, :not_8_chars_error, 'is not having at least 8 characters'
|
3
|
+
error :en, :missing_monkey_error, 'is missing @'
|
4
|
+
|
5
|
+
def set
|
6
|
+
value do |email|
|
7
|
+
email.downcase.gsub(/\s+/,'+')
|
8
|
+
end
|
9
|
+
|
10
|
+
error_for(:not_8_chars_error) unless value.to_s.length > 7
|
11
|
+
error_for(:missing_monkey_error) unless value.include?('@')
|
12
|
+
end
|
13
|
+
|
14
|
+
def db_schema
|
15
|
+
[:string, {
|
16
|
+
limit: @opts[:max] || 120
|
17
|
+
}]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Typero::FloatType < Typero::Type
|
2
|
+
opts :min, 'Minimum value'
|
3
|
+
opts :max, 'Maximun value'
|
4
|
+
opts :round, 'Round to (decimal spaces)'
|
5
|
+
|
6
|
+
def set
|
7
|
+
@value =
|
8
|
+
if opts[:round]
|
9
|
+
value.to_f.round(opts[:round])
|
10
|
+
else
|
11
|
+
value.to_f
|
12
|
+
end
|
13
|
+
|
14
|
+
error_for(:min_length_error, opts[:min], value) if opts[:min] && value < opts[:min]
|
15
|
+
error_for(:max_length_error, opts[:max], value) if opts[:max] && value > opts[:max]
|
16
|
+
end
|
17
|
+
|
18
|
+
def db_schema
|
19
|
+
opts = {}
|
20
|
+
opts[:null] = false if opts[:required]
|
21
|
+
[:float, opts]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Typero::HashType < Typero::Type
|
2
|
+
error :en, :not_hash_type_error, 'value is not hash type'
|
3
|
+
|
4
|
+
def set
|
5
|
+
if value.is_a?(String) && value[0,1] == '{'
|
6
|
+
@value = JSON.load(value)
|
7
|
+
end
|
8
|
+
|
9
|
+
@value ||= {}
|
10
|
+
|
11
|
+
error_for(:not_hash_type_error) unless @value.respond_to?(:keys) && @value.respond_to?(:values)
|
12
|
+
|
13
|
+
if opts[:allow]
|
14
|
+
for key in @value.keys
|
15
|
+
@value.delete(key) unless opts[:allow].include?(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def default
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
def db_schema
|
25
|
+
[:jsonb, {
|
26
|
+
null: false,
|
27
|
+
default: '{}'
|
28
|
+
}]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Typero::ImageType < Typero::Type
|
2
|
+
FORMATS = %w[jpg jpeg gif png svg webp]
|
3
|
+
|
4
|
+
error :en, :image_not_starting_error, 'URL is not starting with http'
|
5
|
+
error :en, :image_not_image_format, 'URL is not ending with %s' % FORMATS.join(', ')
|
6
|
+
|
7
|
+
opts :strict, 'Force image to have known extension (%s)' % FORMATS.join(', ')
|
8
|
+
|
9
|
+
def set
|
10
|
+
error_for(:image_not_starting_error) unless value =~ /^https?:\/\/./
|
11
|
+
|
12
|
+
if opts[:strict]
|
13
|
+
ext = value.split('.').last.downcase
|
14
|
+
error_for(:image_not_image_format) unless FORMATS.include?(ext)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def db_schema
|
19
|
+
[:string]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class Typero::IntegerType < Typero::Type
|
2
|
+
opts :min, 'Minimum value'
|
3
|
+
opts :max, 'Maximun value'
|
4
|
+
|
5
|
+
def set
|
6
|
+
value(&:to_i)
|
7
|
+
|
8
|
+
error_for(:min_value_error, opts[:min], value) if opts[:min] && value < opts[:min]
|
9
|
+
error_for(:max_value_error, opts[:max], value) if opts[:max] && value > opts[:max]
|
10
|
+
end
|
11
|
+
|
12
|
+
def db_schema
|
13
|
+
[:integer, {}]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Typero::LabelType < Typero::Type
|
2
|
+
def set
|
3
|
+
value do |data|
|
4
|
+
data
|
5
|
+
.to_s
|
6
|
+
.gsub(/\s+/,'-')
|
7
|
+
.gsub(/[^\w\-]/,'')
|
8
|
+
.gsub(/\-+/, '-')[0,30]
|
9
|
+
.downcase
|
10
|
+
end
|
11
|
+
|
12
|
+
error_for(:unallowed_characters_error) unless value =~ /^[\w\-]+$/
|
13
|
+
end
|
14
|
+
|
15
|
+
def db_schema
|
16
|
+
[:string, {
|
17
|
+
limit: 30
|
18
|
+
}]
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Typero::LocaleType < Typero::Type
|
2
|
+
error :en, :locale_bad_format, 'Locale "%s" is in bad format (should be xx or xx-xx)'
|
3
|
+
|
4
|
+
def set
|
5
|
+
error_for(:locale_bad_format, value) unless value =~ /^[\w\-]{2,5}$/
|
6
|
+
end
|
7
|
+
|
8
|
+
def db_schema
|
9
|
+
[:string, { limit: 5 }]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Typero::ModelType < Typero::Type
|
2
|
+
def set
|
3
|
+
value(&:to_h)
|
4
|
+
|
5
|
+
errors = {}
|
6
|
+
|
7
|
+
schema = opts[:model].is_a?(Typero::Schema) ? opts[:model] : Typero.schema(opts[:model])
|
8
|
+
|
9
|
+
# by default models in schems are strict true (remove undefined keys)
|
10
|
+
schema.validate value, strict: true do |field, error|
|
11
|
+
errors[field] = error
|
12
|
+
end
|
13
|
+
|
14
|
+
raise TypeError.new errors.to_json if errors.keys.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def db_schema
|
18
|
+
[:jsonb, {
|
19
|
+
null: false
|
20
|
+
}]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -1,4 +1,20 @@
|
|
1
1
|
class Typero::OibType < Typero::Type
|
2
|
+
error :en, :not_an_oib_error, 'not in an OIB format'
|
3
|
+
|
4
|
+
def set
|
5
|
+
value do |data|
|
6
|
+
check?(data) ? data.to_i : error_for(:not_an_oib_error)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def db_schema
|
11
|
+
[:string, {
|
12
|
+
limit: 11
|
13
|
+
}]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
2
18
|
# http://domagoj.eu/oib/
|
3
19
|
def check? oib
|
4
20
|
oib = oib.to_s
|
@@ -19,23 +35,5 @@ class Typero::OibType < Typero::Type
|
|
19
35
|
return control_sum == oib.at(10).to_i
|
20
36
|
end
|
21
37
|
|
22
|
-
def set
|
23
|
-
@value = check?(@value) ? @value.to_i : nil
|
24
|
-
end
|
25
|
-
|
26
|
-
def validate
|
27
|
-
raise TypeError.new(error_for(:not_an_oib_error)) unless check?(@value)
|
28
|
-
end
|
29
|
-
|
30
|
-
def not_an_oib_error
|
31
|
-
'not in an OIB format'
|
32
|
-
end
|
33
|
-
|
34
|
-
def db_field
|
35
|
-
opts = {}
|
36
|
-
opts[:null] = false if @opts[:req]
|
37
|
-
opts[:limit] = 11
|
38
|
-
[:string, opts]
|
39
|
-
end
|
40
38
|
end
|
41
39
|
|