typero 0.4.0 → 0.9.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/.version +1 -1
- data/lib/adapters/sequel.rb +69 -0
- data/lib/typero.rb +12 -170
- data/lib/typero/exporter.rb +93 -0
- data/lib/typero/params.rb +99 -0
- data/lib/typero/schema.rb +132 -0
- data/lib/typero/type/type.rb +110 -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 +32 -0
- data/lib/typero/type/types/datetime_type.rb +14 -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 +25 -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/model_type.rb +21 -0
- data/lib/typero/type/{oib.rb → types/oib_type.rb} +16 -11
- 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/url_type.rb +13 -0
- data/lib/typero/typero.rb +90 -0
- metadata +29 -19
- data/lib/typero/type.rb +0 -39
- data/lib/typero/type/array.rb +0 -44
- data/lib/typero/type/boolean.rb +0 -10
- data/lib/typero/type/email.rb +0 -21
- data/lib/typero/type/float.rb +0 -21
- data/lib/typero/type/hash.rb +0 -14
- data/lib/typero/type/integer.rb +0 -19
- data/lib/typero/type/label.rb +0 -13
- data/lib/typero/type/string.rb +0 -22
- data/lib/typero/type/url.rb +0 -14
@@ -0,0 +1,132 @@
|
|
1
|
+
module Typero
|
2
|
+
class Schema
|
3
|
+
SCHEMAS = {}
|
4
|
+
|
5
|
+
# accepts dsl block to
|
6
|
+
def initialize &block
|
7
|
+
raise "Params not defined" unless block_given?
|
8
|
+
@schema = Params.new &block
|
9
|
+
end
|
10
|
+
|
11
|
+
# validates any instance object with hash variable interface
|
12
|
+
# it also coarces values
|
13
|
+
def validate object
|
14
|
+
@object = object
|
15
|
+
@errors = {}
|
16
|
+
|
17
|
+
@schema.rules.each do |field, opts|
|
18
|
+
# set value to default if value is blank and default given
|
19
|
+
@object[field] = opts[:default] if opts[:default] && @object[field].blank?
|
20
|
+
|
21
|
+
# get field value
|
22
|
+
value = @object[field]
|
23
|
+
|
24
|
+
if opts[:array]
|
25
|
+
unless value.is_a?(Array)
|
26
|
+
opts[:delimiter] ||= /\s*,\s*/
|
27
|
+
value = value.to_s.split(opts[:delimiter])
|
28
|
+
end
|
29
|
+
|
30
|
+
value = value
|
31
|
+
.map { |el| check_filed_value field, el, opts }
|
32
|
+
.map { |el| el.to_s == '' ? nil : el }
|
33
|
+
.compact
|
34
|
+
|
35
|
+
value = Set.new(value).to_a unless opts[:duplicates]
|
36
|
+
|
37
|
+
opts[:max_count] ||= 100
|
38
|
+
add_error(field, 'Max number of array elements is %d, you have %d' % [opts[:max_count], value.length], opts) if value.length > opts[:max_count]
|
39
|
+
|
40
|
+
add_required_error field, value.first, opts
|
41
|
+
else
|
42
|
+
value = check_filed_value field, value, opts
|
43
|
+
add_required_error field, value, opts
|
44
|
+
end
|
45
|
+
|
46
|
+
# if value is not list of allowed values, raise error
|
47
|
+
if opts[:allowed] && !opts[:values].include?(value)
|
48
|
+
add_error field, 'Value "%s" is not allowed' % value, opts
|
49
|
+
end
|
50
|
+
|
51
|
+
# present empty string values as nil
|
52
|
+
@object[field] = value.to_s.sub(/\s+/, '') == '' ? nil : value
|
53
|
+
end
|
54
|
+
|
55
|
+
if @errors.keys.length > 0 && block_given?
|
56
|
+
@errors.each { |k, v| yield(k, v) }
|
57
|
+
end
|
58
|
+
|
59
|
+
@errors
|
60
|
+
end
|
61
|
+
|
62
|
+
def valid? object
|
63
|
+
errors = validate object
|
64
|
+
errors.keys.length == 0
|
65
|
+
end
|
66
|
+
|
67
|
+
# returns field, db_type, db_opts
|
68
|
+
def db_schema
|
69
|
+
out = @schema.rules.inject([]) do |total, (field, opts)|
|
70
|
+
# get db filed schema
|
71
|
+
type, opts = Typero::Type.load(opts[:type]).new(nil, opts).db_field
|
72
|
+
|
73
|
+
# add array true to field it ont defined in schema
|
74
|
+
schema_opts = @schema.rules[field]
|
75
|
+
opts[:array] = true if schema_opts[:array]
|
76
|
+
|
77
|
+
total << [type, field, opts]
|
78
|
+
end
|
79
|
+
|
80
|
+
out += @schema.db_rules
|
81
|
+
|
82
|
+
out
|
83
|
+
end
|
84
|
+
|
85
|
+
# iterate trough all the ruels via block interface
|
86
|
+
# schema.rules do |field, opts|
|
87
|
+
# schema.rules(:url) do |field, opts|
|
88
|
+
def rules filter = nil, &block
|
89
|
+
return @schema.rules unless filter
|
90
|
+
out = @schema.rules
|
91
|
+
out = out.select { |k, v| v[:type].to_s == filter.to_s || v[:array_type].to_s == filter.to_s } if filter
|
92
|
+
return out unless block_given?
|
93
|
+
|
94
|
+
out.each { |k, v| yield k, v }
|
95
|
+
end
|
96
|
+
alias :to_h :rules
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# adds error to array or prefixes with field name
|
101
|
+
def add_error field, msg, opts
|
102
|
+
if @errors[field]
|
103
|
+
@errors[field] += ", %s" % msg
|
104
|
+
else
|
105
|
+
if msg && msg[0, 1].downcase == msg[0, 1]
|
106
|
+
field_name = opts[:name] || field.to_s.sub(/_id$/, "").capitalize
|
107
|
+
msg = "%s %s" % [field_name, msg]
|
108
|
+
end
|
109
|
+
|
110
|
+
@errors[field] = msg
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def safe_type type
|
115
|
+
type.to_s.gsub(/[^\w]/, "").classify
|
116
|
+
end
|
117
|
+
|
118
|
+
def add_required_error field, value, opts
|
119
|
+
return unless opts[:required] && value.nil?
|
120
|
+
msg = opts[:required].class == TrueClass ? "is required" : opts[:required]
|
121
|
+
add_error field, msg, opts
|
122
|
+
end
|
123
|
+
|
124
|
+
def check_filed_value field, value, opts
|
125
|
+
klass = "Typero::%sType" % safe_type(opts[:type])
|
126
|
+
check = klass.constantize.new value, opts
|
127
|
+
check.get
|
128
|
+
rescue TypeError => e
|
129
|
+
add_error field, e.message, opts
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,110 @@
|
|
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_KEYS = [:type, :required, :array, :max_count, :default, :name, :meta, :model]
|
19
|
+
OPTS = {}
|
20
|
+
|
21
|
+
attr_reader :opts
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def load name
|
25
|
+
klass = 'Typero::%sType' % name.to_s.gsub(/[^\w]/,'').classify
|
26
|
+
|
27
|
+
if const_defined? klass
|
28
|
+
klass.constantize
|
29
|
+
else
|
30
|
+
raise ArgumentError, 'Typero type "%s" is not defined (%s)' % [name, klass]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def error locale, key, message
|
35
|
+
locale = locale.to_sym
|
36
|
+
ERRORS[locale] ||= {}
|
37
|
+
ERRORS[locale][key.to_sym] = message
|
38
|
+
end
|
39
|
+
|
40
|
+
def opts key, desc
|
41
|
+
OPTS_KEYS.push key unless OPTS_KEYS.include?(key)
|
42
|
+
|
43
|
+
OPTS[self] ||= {}
|
44
|
+
OPTS[self][key] = desc
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
###
|
49
|
+
|
50
|
+
def initialize value, opts={}, &block
|
51
|
+
value = value.sub(/^\s+/, '').sub(/\s+$/, '') if value.is_a?(String)
|
52
|
+
|
53
|
+
@value = value
|
54
|
+
@opts = opts
|
55
|
+
@block = block
|
56
|
+
end
|
57
|
+
|
58
|
+
def value &block
|
59
|
+
if block_given?
|
60
|
+
@value = block.call @value
|
61
|
+
else
|
62
|
+
@value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def get
|
67
|
+
if value.nil?
|
68
|
+
opts[:default].nil? ? default : opts[:default]
|
69
|
+
else
|
70
|
+
set
|
71
|
+
error_for(:not_in_range, opts[:values].join(', ')) if opts[:values] && !opts[:values].include?(@value)
|
72
|
+
value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def default
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def db_field
|
81
|
+
out = db_schema
|
82
|
+
out[1] ||= {}
|
83
|
+
out[1][:default] ||= opts[:default] unless opts[:default].nil?
|
84
|
+
out[1][:null] = false if !opts[:array] && opts[:required]
|
85
|
+
out
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# get error from option or the default one
|
91
|
+
def error_for name, *args
|
92
|
+
locale =
|
93
|
+
if defined?(Lux)
|
94
|
+
Lux.current.locale.to_s
|
95
|
+
elsif defined?(I18n)
|
96
|
+
I18n.locale
|
97
|
+
end
|
98
|
+
|
99
|
+
locale = :en if locale.to_s == ''
|
100
|
+
pointer = ERRORS[locale.to_sym] || ERRORS[:en]
|
101
|
+
error = @opts.dig(:meta, locale, name) || @opts.dig(:meta, name) || pointer[name]
|
102
|
+
error = error % args if args.first
|
103
|
+
|
104
|
+
raise 'Type error :%s not defined' % name unless error
|
105
|
+
raise TypeError.new(error)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
@@ -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,32 @@
|
|
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
|
+
value { |data| DateTime.parse(data) }
|
7
|
+
value { |data| DateTime.new(data.year, data.month, data.day) }
|
8
|
+
|
9
|
+
check_date_min_max
|
10
|
+
end
|
11
|
+
|
12
|
+
def db_schema
|
13
|
+
[:date, {}]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def check_date_min_max
|
19
|
+
if min = opts[:min]
|
20
|
+
min = DateTime.parse(min)
|
21
|
+
error_for(:min_date, min) % min if min > value
|
22
|
+
end
|
23
|
+
|
24
|
+
if max = opts[:max]
|
25
|
+
max = DateTime.parse(max)
|
26
|
+
error_for(:max_date, max) % max if value > max
|
27
|
+
end
|
28
|
+
|
29
|
+
value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -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,25 @@
|
|
1
|
+
class Typero::HashType < Typero::Type
|
2
|
+
error :en, :not_hash_type_error, 'value is not hash type'
|
3
|
+
|
4
|
+
def set
|
5
|
+
error_for(:not_hash_type_error) unless value.is_a?(Hash)
|
6
|
+
|
7
|
+
if opts[:allow]
|
8
|
+
for key in value.keys
|
9
|
+
value.delete(key) unless opts[:allow].include?(key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def default
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def db_schema
|
19
|
+
[:jsonb, {
|
20
|
+
null: false,
|
21
|
+
default: '{}'
|
22
|
+
}]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -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
|
+
|