typero 0.7.0 → 0.8.0
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 +6 -2
- data/lib/typero.rb +3 -3
- data/lib/typero/params.rb +84 -0
- data/lib/typero/type/type.rb +82 -0
- data/lib/typero/type/{boolean.rb → types/boolean.rb} +0 -0
- data/lib/typero/type/{currency.rb → types/currency.rb} +2 -0
- data/lib/typero/type/{date.rb → types/date.rb} +0 -0
- data/lib/typero/type/{datetime.rb → types/datetime.rb} +0 -0
- data/lib/typero/type/types/email.rb +21 -0
- data/lib/typero/type/types/float.rb +21 -0
- data/lib/typero/type/{hash.rb → types/hash.rb} +3 -5
- data/lib/typero/type/types/image.rb +28 -0
- data/lib/typero/type/types/integer.rb +21 -0
- data/lib/typero/type/{label.rb → types/label.rb} +1 -5
- data/lib/typero/type/{oib.rb → types/oib.rb} +19 -18
- data/lib/typero/type/{point.rb → types/point.rb} +1 -3
- data/lib/typero/type/types/string.rb +23 -0
- data/lib/typero/type/{text.rb → types/text.rb} +3 -0
- data/lib/typero/type/{url.rb → types/url.rb} +3 -5
- data/lib/typero/typero.rb +87 -101
- metadata +23 -24
- data/lib/typero/schema.rb +0 -84
- data/lib/typero/type.rb +0 -43
- data/lib/typero/type/array.rb +0 -55
- data/lib/typero/type/email.rb +0 -27
- data/lib/typero/type/float.rb +0 -27
- data/lib/typero/type/image.rb +0 -28
- data/lib/typero/type/integer.rb +0 -26
- data/lib/typero/type/string.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba656a56b8d2e5d3d977a2973f6de881eefd3257397a4a4ebe908a7abaed70a7
|
4
|
+
data.tar.gz: 8374f340b6af235c274ceaa9aace33f0c462b5a203d667a199fa519c1f56f53e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba14e29595a5d17c3c3d102f49d1234a82e7ea23a7062a4a61c6dc56770d9d4922f1be7ae01663c41382efb6c6c51ed3fb5dfd4c188315ec1a5bf3f43eb699fc
|
7
|
+
data.tar.gz: c2d8b322fac4048067b809bf7944c8ca27bb787d4b590a68eec62169a768a65ddbb089359da73f550a32b59a0405b95629ae4932bf7a3dfb9d5426e2e26d3e7d
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/lib/adapters/sequel.rb
CHANGED
@@ -2,13 +2,17 @@
|
|
2
2
|
|
3
3
|
module Sequel::Plugins::TyperoAttributes
|
4
4
|
module ClassMethods
|
5
|
-
|
5
|
+
def typero
|
6
|
+
Typero.new self
|
7
|
+
end
|
6
8
|
end
|
7
9
|
|
8
10
|
module InstanceMethods
|
9
11
|
# calling typero! on any object will validate all fields
|
10
12
|
def typero! field_name=nil
|
11
|
-
|
13
|
+
return unless Typero.defined?(self.class)
|
14
|
+
|
15
|
+
typero = self.class.typero
|
12
16
|
|
13
17
|
typero.validate(self) do |name, err|
|
14
18
|
errors.add(name, err) unless (errors.on(name) || []).include?(err)
|
data/lib/typero.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# base libs
|
2
2
|
require_relative 'typero/typero'
|
3
|
-
require_relative 'typero/
|
4
|
-
require_relative 'typero/type'
|
3
|
+
require_relative 'typero/params'
|
4
|
+
require_relative 'typero/type/type'
|
5
5
|
|
6
6
|
# checker types
|
7
|
-
Dir['%s/typero/type/*.rb' % __dir__].each do |file|
|
7
|
+
Dir['%s/typero/type/types/*.rb' % __dir__].each do |file|
|
8
8
|
require file
|
9
9
|
end
|
10
10
|
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Base class for schema validation
|
2
|
+
|
3
|
+
class Typero
|
4
|
+
class Params
|
5
|
+
ALLOWED = %i(name min max default allowed delimiter max_count req required type array meta desc description duplicates unique)
|
6
|
+
|
7
|
+
attr_reader :rules, :db_rules
|
8
|
+
|
9
|
+
def initialize &block
|
10
|
+
@db_rules = []
|
11
|
+
@rules = {}
|
12
|
+
instance_exec &block
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# used in dsl to define schema field options
|
18
|
+
def set field, *args
|
19
|
+
raise "Field name not given (Typero)" unless field
|
20
|
+
|
21
|
+
if args.first.is_a?(Hash)
|
22
|
+
opts = args.first || {}
|
23
|
+
else
|
24
|
+
opts = args[1] || {}
|
25
|
+
opts[:type] ||= args[0]
|
26
|
+
end
|
27
|
+
|
28
|
+
opts[:type] ||= :string
|
29
|
+
opts[:required] = true unless opts[:required].is_a?(FalseClass) || opts[:req].is_a?(FalseClass)
|
30
|
+
|
31
|
+
field = field.to_s
|
32
|
+
|
33
|
+
# name? - opional name
|
34
|
+
if field.include?('?')
|
35
|
+
field = field.sub('?', '')
|
36
|
+
opts[:required] = false
|
37
|
+
end
|
38
|
+
|
39
|
+
# array that allows duplicates
|
40
|
+
if opts[:type].is_a?(Array)
|
41
|
+
opts[:type] = opts[:type].first
|
42
|
+
opts[:array] = true
|
43
|
+
end
|
44
|
+
|
45
|
+
# no duplicates array
|
46
|
+
if opts[:type].is_a?(Set)
|
47
|
+
opts[:type] = opts[:type].to_a.first
|
48
|
+
opts[:array] = true
|
49
|
+
end
|
50
|
+
|
51
|
+
opts[:type] ||= 'string'
|
52
|
+
opts[:type] = opts[:type].to_s.downcase
|
53
|
+
|
54
|
+
opts[:description] = opts.delete(:desc) unless opts[:desc].nil?
|
55
|
+
|
56
|
+
# chek alloed params, all optional should go in meta
|
57
|
+
result = opts.keys - ALLOWED
|
58
|
+
raise ArgumentError.new('Unallowed Type params found: %s, allowed: %s' % [result.join(', '), ALLOWED]) if result.length > 0
|
59
|
+
|
60
|
+
field = field.to_sym
|
61
|
+
|
62
|
+
db :add_index, field if opts.delete(:index)
|
63
|
+
|
64
|
+
klass = Typero::Type.load opts[:type]
|
65
|
+
@rules[field] = opts
|
66
|
+
end
|
67
|
+
|
68
|
+
# pass values for db_schema only
|
69
|
+
# db :timestamps
|
70
|
+
# db :add_index, :code -> t.add_index :code
|
71
|
+
def db *args
|
72
|
+
@db_rules.push args
|
73
|
+
end
|
74
|
+
|
75
|
+
# set :age, type: :integer -> integer :age
|
76
|
+
# email :email
|
77
|
+
#
|
78
|
+
# set :emails, Array[:email]
|
79
|
+
# email Array[:emails]
|
80
|
+
def method_missing field, *args, &block
|
81
|
+
set field, *args
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Master class
|
2
|
+
|
3
|
+
class 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
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
OPTS = {}
|
17
|
+
|
18
|
+
attr_accessor :opts
|
19
|
+
attr_accessor :value
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def load name
|
23
|
+
klass = 'Typero::%sType' % name.to_s.gsub(/[^\w]/,'').classify
|
24
|
+
|
25
|
+
if const_defined? klass
|
26
|
+
klass.constantize
|
27
|
+
else
|
28
|
+
raise ArgumentError, 'Typero type "%s" is not defined (%s)' % [name, klass]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def error locale, key, message
|
33
|
+
locale = locale.to_sym
|
34
|
+
ERRORS[locale] ||= {}
|
35
|
+
ERRORS[locale][key.to_sym] = message
|
36
|
+
end
|
37
|
+
|
38
|
+
def opts key, desc
|
39
|
+
OPTS[self] ||= {}
|
40
|
+
OPTS[self][key] = desc
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
###
|
45
|
+
|
46
|
+
def initialize value, opts={}
|
47
|
+
@value = value
|
48
|
+
@opts = opts
|
49
|
+
end
|
50
|
+
|
51
|
+
# default validation for any type
|
52
|
+
def validate
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def default
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# get error from option or the default one
|
63
|
+
def error_for name, *args
|
64
|
+
locale =
|
65
|
+
if defined?(Lux)
|
66
|
+
Lux.current.locale.to_s
|
67
|
+
elsif defined?(I18n)
|
68
|
+
I18n.locale
|
69
|
+
end
|
70
|
+
|
71
|
+
locale = :en if locale.to_s == ''
|
72
|
+
pointer = ERRORS[locale.to_sym] || ERRORS[:en]
|
73
|
+
error = @opts.dig(:meta, locale, name) || @opts.dig(:meta, name) || pointer[name]
|
74
|
+
error = error % args if args.first
|
75
|
+
|
76
|
+
raise 'Type error :%s not defined' % name unless error
|
77
|
+
raise TypeError.new(error)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
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 = @value.downcase.gsub(/\s+/,'+')
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate
|
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_field
|
15
|
+
opts = {}
|
16
|
+
opts[:limit] = @opts[:max] || 120
|
17
|
+
opts[:null] = false if @opts[:required]
|
18
|
+
[:string, opts]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Typero::FloatType < Typero::Type
|
2
|
+
opts :min, 'Minimum value'
|
3
|
+
opts :max, 'Maximun value'
|
4
|
+
|
5
|
+
def set
|
6
|
+
@value = @value.to_f
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate
|
10
|
+
error_for(:min_length_error, @opts[:min], @value) if @opts[:min] && value < @opts[:min]
|
11
|
+
error_for(:max_length_error, @opts[:max], @value) if @opts[:max] && value > @opts[:max]
|
12
|
+
end
|
13
|
+
|
14
|
+
def db_field
|
15
|
+
opts = {}
|
16
|
+
opts[:null] = false if @opts[:required]
|
17
|
+
[:float, opts]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class Typero::HashType < Typero::Type
|
2
|
+
error :en, :not_hash_type_error, 'value is not hash type'
|
3
|
+
|
2
4
|
def default
|
3
5
|
{}
|
4
6
|
end
|
@@ -8,11 +10,7 @@ class Typero::HashType < Typero::Type
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def validate
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def not_hash_type_error
|
15
|
-
'value is not hash type'
|
13
|
+
error_for(:not_hash_type_error) unless @value.is_a?(Hash)
|
16
14
|
end
|
17
15
|
|
18
16
|
def db_field
|
@@ -0,0 +1,28 @@
|
|
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
|
+
@value = 'https://%s' % @value unless @value.include?('://')
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate
|
14
|
+
error_for(:image_not_starting_error) unless @value =~ /^https?:\/\/./
|
15
|
+
|
16
|
+
if opts[:strict]
|
17
|
+
ext = @value.split('.').last.downcase
|
18
|
+
error_for(:image_not_image_format) unless FORMATS.include?(ext)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def db_field
|
23
|
+
opts = {}
|
24
|
+
opts[:null] = false if @opts[:required]
|
25
|
+
[:string, opts]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Typero::IntegerType < Typero::Type
|
2
|
+
opts :min, 'Minimum value'
|
3
|
+
opts :max, 'Maximun value'
|
4
|
+
|
5
|
+
def set
|
6
|
+
@value = @value.to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
def validate
|
10
|
+
error_for(:min_value_error, @opts[:min], @value) if @opts[:min] && @value < @opts[:min]
|
11
|
+
error_for(:max_value_error, @opts[:max], @value) if @opts[:max] && @value > @opts[:max]
|
12
|
+
end
|
13
|
+
|
14
|
+
def db_field
|
15
|
+
opts = {}
|
16
|
+
opts[:null] = false if @opts[:required]
|
17
|
+
opts[:default] = @opts[:default]
|
18
|
+
[:integer, opts]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -4,11 +4,7 @@ class Typero::LabelType < Typero::Type
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def validate
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
def unallowed_characters_error
|
11
|
-
'label is having unallowed characters'
|
7
|
+
error_for(:unallowed_characters_error) unless @value =~ /^[\w\-]+$/
|
12
8
|
end
|
13
9
|
|
14
10
|
def db_field
|
@@ -1,4 +1,23 @@
|
|
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 = check?(@value) ? @value.to_i : nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate
|
9
|
+
error_for(:not_an_oib_error) unless check?(@value)
|
10
|
+
end
|
11
|
+
|
12
|
+
def db_field
|
13
|
+
opts = {}
|
14
|
+
opts[:null] = false if @opts[:required]
|
15
|
+
opts[:limit] = 11
|
16
|
+
[:string, opts]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
2
21
|
# http://domagoj.eu/oib/
|
3
22
|
def check? oib
|
4
23
|
oib = oib.to_s
|
@@ -19,23 +38,5 @@ class Typero::OibType < Typero::Type
|
|
19
38
|
return control_sum == oib.at(10).to_i
|
20
39
|
end
|
21
40
|
|
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[:required]
|
37
|
-
opts[:limit] = 11
|
38
|
-
[:string, opts]
|
39
|
-
end
|
40
41
|
end
|
41
42
|
|
@@ -3,7 +3,6 @@
|
|
3
3
|
|
4
4
|
class Typero::PointType < Typero::Type
|
5
5
|
def set
|
6
|
-
ap @value
|
7
6
|
if @value.present?
|
8
7
|
if @value.include?('/@')
|
9
8
|
point = @value.split('/@', 2).last.split(',')
|
@@ -19,13 +18,12 @@ class Typero::PointType < Typero::Type
|
|
19
18
|
|
20
19
|
def validate
|
21
20
|
if @value && @value.include?(',') && !@value =~ /^SRID=4326;POINT\(/
|
22
|
-
|
21
|
+
error_for(:unallowed_characters_error)
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
25
|
def db_field
|
27
26
|
[:geography, {}]
|
28
27
|
end
|
29
|
-
|
30
28
|
end
|
31
29
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Typero::StringType < Typero::Type
|
2
|
+
opts :min, 'Minimun string length'
|
3
|
+
opts :max, 'Maximun string length'
|
4
|
+
|
5
|
+
def set
|
6
|
+
@value = @value.to_s unless @value.is_a?(String)
|
7
|
+
@value = @value.downcase if @opts[:downcase]
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate
|
11
|
+
error_for(:min_length_error, @opts[:min], @value.length) if @opts[:min] && @value.length < @opts[:min]
|
12
|
+
error_for(:max_length_error, @opts[:max], @value.length) if @opts[:max] && @value.length > @opts[:max]
|
13
|
+
end
|
14
|
+
|
15
|
+
def db_field
|
16
|
+
opts = {}
|
17
|
+
opts[:limit] = @opts[:max] || 255
|
18
|
+
opts[:null] = false if @opts[:required]
|
19
|
+
opts[:default] = @opts[:default]
|
20
|
+
[:string, opts]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -1,14 +1,12 @@
|
|
1
1
|
class Typero::UrlType < Typero::Type
|
2
|
+
error :en, :url_not_starting_error, 'URL is not starting with http'
|
3
|
+
|
2
4
|
def set
|
3
5
|
@value = 'http://%s' % @value unless @value.include?('://')
|
4
6
|
end
|
5
7
|
|
6
8
|
def validate
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
def not_starting_error
|
11
|
-
'URL is not starting with http'
|
9
|
+
error_for(:url_not_starting_error) unless @value =~ /^https?:\/\/./
|
12
10
|
end
|
13
11
|
|
14
12
|
def db_field
|
data/lib/typero/typero.rb
CHANGED
@@ -17,127 +17,100 @@
|
|
17
17
|
# rules.validate(@object) { |errors| ... }
|
18
18
|
|
19
19
|
class Typero
|
20
|
-
|
21
|
-
VERSION = File.read File.expand_path '../../.version', File.dirname(__FILE__)
|
20
|
+
VERSION = File.read File.expand_path "../../.version", File.dirname(__FILE__)
|
22
21
|
|
23
22
|
class << self
|
24
|
-
# validate single value in type
|
25
|
-
def validate type, value, opts={}
|
26
|
-
field = type.to_s.tableize.singularize.to_sym
|
27
|
-
|
28
|
-
# we need to have pointer to hash, so value can be changed (coerced) if needed
|
29
|
-
h = { field => value }
|
30
|
-
|
31
|
-
rule = new do
|
32
|
-
set field, type, opts
|
33
|
-
end
|
34
|
-
|
35
|
-
if error = rule.validate(h)[field]
|
36
|
-
block_given? ? yield(error) : raise(TypeError.new(error))
|
37
|
-
end
|
38
|
-
|
39
|
-
h[field]
|
40
|
-
end
|
41
|
-
|
42
23
|
# check and coerce value
|
43
24
|
# Typero.set(:label, 'Foo bar') -> "foo-bar"
|
44
|
-
def set type, value, opts={}
|
25
|
+
def set type, value, opts = {}, &block
|
45
26
|
check = Typero::Type.load(type).new value, opts
|
27
|
+
check.set
|
28
|
+
check.validate
|
46
29
|
check.value
|
30
|
+
rescue TypeError => error
|
31
|
+
if block
|
32
|
+
block.call error
|
33
|
+
false
|
34
|
+
else
|
35
|
+
raise error
|
36
|
+
end
|
47
37
|
end
|
48
|
-
|
49
|
-
# list loaded classes
|
50
|
-
def list type=nil
|
51
|
-
SCHEMAS
|
52
|
-
.select { |k, v| type ? v[1] == type : true; }
|
53
|
-
.keys
|
54
|
-
end
|
55
|
-
|
56
|
-
# def schema table_name, &block
|
57
|
-
# schema = Typero.new(&block)
|
58
|
-
|
59
|
-
# if Lux.config.migrate
|
60
|
-
# AutoMigrate.typero table_name, schema
|
61
|
-
# else
|
62
|
-
# klass = table_name.to_s.classify.constantize
|
63
|
-
# klass.typero = schema
|
64
|
-
# end
|
65
|
-
# end
|
66
38
|
end
|
67
39
|
|
68
40
|
###
|
69
41
|
|
70
42
|
# accepts dsl block to
|
71
|
-
def initialize
|
72
|
-
|
73
|
-
|
74
|
-
if name.is_a?(Hash)
|
75
|
-
type = name.keys.first
|
76
|
-
name = name.values.first
|
77
|
-
end
|
78
|
-
|
79
|
-
if block_given?
|
80
|
-
@schema = Schema.new &block
|
81
|
-
|
82
|
-
if name
|
83
|
-
SCHEMAS[name_fix(name)] = [@schema, type]
|
84
|
-
end
|
85
|
-
elsif name
|
86
|
-
schema_name = name_fix(name)
|
87
|
-
@schema = SCHEMAS[schema_name][0] || raise(ArgumentError.new('Schema nemed "%s" not found (%s)' % [schema_name, name]))
|
88
|
-
else
|
89
|
-
raise ArgumentError, 'No block or schema name given'
|
90
|
-
end
|
43
|
+
def initialize &block
|
44
|
+
raise "Params not defined" unless block_given?
|
45
|
+
@schema = Params.new &block
|
91
46
|
end
|
92
47
|
|
93
|
-
# validates any instance object
|
48
|
+
# validates any instance object with hash variable interface
|
94
49
|
# it also coarces values
|
95
|
-
def validate
|
50
|
+
def validate object
|
51
|
+
@object = object
|
96
52
|
@errors = {}
|
97
53
|
|
98
54
|
@schema.rules.each do |field, opts|
|
99
55
|
# set value to default if value is blank and default given
|
100
|
-
|
56
|
+
@object[field] = opts[:default] if opts[:default] && @object[field].blank?
|
101
57
|
|
102
58
|
# get field value
|
103
|
-
value =
|
104
|
-
|
105
|
-
if
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
unless check.value.nil?
|
111
|
-
begin
|
112
|
-
check.set
|
113
|
-
check.validate
|
114
|
-
instance[field] = check.value
|
115
|
-
rescue TypeError => e
|
116
|
-
add_error field, e.message
|
117
|
-
end
|
59
|
+
value = @object[field]
|
60
|
+
|
61
|
+
if opts[:array]
|
62
|
+
unless value.is_a?(Array)
|
63
|
+
opts[:delimiter] ||= /\s*,\s*/
|
64
|
+
value = value.to_s.split(opts[:delimiter])
|
118
65
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
66
|
+
|
67
|
+
value = value
|
68
|
+
.map { |el| check_filed_value field, el, opts }
|
69
|
+
.map { |el| el.to_s == '' ? nil : el }
|
70
|
+
.compact
|
71
|
+
|
72
|
+
value = Set.new(value).to_a unless opts[:duplicates]
|
73
|
+
|
74
|
+
opts[:max_count] ||= 100
|
75
|
+
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]
|
76
|
+
|
77
|
+
check_required field, value.first, opts
|
78
|
+
else
|
79
|
+
value = check_filed_value field, value, opts
|
80
|
+
check_required field, value, opts
|
81
|
+
end
|
82
|
+
|
83
|
+
# if value is not list of allowed values, raise error
|
84
|
+
if opts[:allowed] && !opts[:values].include?(value)
|
85
|
+
add_error field, 'Value "%s" is not allowed' % value, opts
|
122
86
|
end
|
87
|
+
|
88
|
+
# present empty string values as nil
|
89
|
+
@object[field] = value.to_s.sub(/\s+/, '') == '' ? nil : value
|
123
90
|
end
|
124
91
|
|
125
92
|
if @errors.keys.length > 0 && block_given?
|
126
|
-
@errors.each { |k,v| yield(k, v) }
|
93
|
+
@errors.each { |k, v| yield(k, v) }
|
127
94
|
end
|
128
95
|
|
129
96
|
@errors
|
130
97
|
end
|
131
98
|
|
132
|
-
def valid?
|
133
|
-
errors = validate
|
99
|
+
def valid? object
|
100
|
+
errors = validate object
|
134
101
|
errors.keys.length == 0
|
135
102
|
end
|
136
103
|
|
137
104
|
# returns field, db_type, db_opts
|
138
105
|
def db_schema
|
139
106
|
out = @schema.rules.inject([]) do |total, (field, opts)|
|
140
|
-
|
107
|
+
# get db filed schema
|
108
|
+
type, opts = Typero::Type.load(opts[:type]).new(nil, opts).db_field
|
109
|
+
|
110
|
+
# add array true to field it ont defined in schema
|
111
|
+
schema_opts = @schema.rules[field]
|
112
|
+
opts[:array] = true if schema_opts[:array]
|
113
|
+
|
141
114
|
total << [type, field, opts]
|
142
115
|
end
|
143
116
|
|
@@ -149,27 +122,25 @@ class Typero
|
|
149
122
|
# iterate trough all the ruels via block interface
|
150
123
|
# schema.rules do |field, opts|
|
151
124
|
# schema.rules(:url) do |field, opts|
|
152
|
-
def rules
|
125
|
+
def rules(filter = nil, &block)
|
153
126
|
return @schema.rules unless filter
|
154
127
|
out = @schema.rules
|
155
|
-
out = out.select { |k,v| v[:type].to_s == filter.to_s || v[:array_type].to_s == filter.to_s } if filter
|
128
|
+
out = out.select { |k, v| v[:type].to_s == filter.to_s || v[:array_type].to_s == filter.to_s } if filter
|
156
129
|
return out unless block_given?
|
157
130
|
|
158
|
-
|
159
|
-
yield k, v
|
160
|
-
end
|
131
|
+
out.each { |k, v| yield k, v }
|
161
132
|
end
|
162
133
|
|
163
134
|
private
|
164
135
|
|
165
136
|
# adds error to array or prefixes with field name
|
166
|
-
def add_error field, msg
|
137
|
+
def add_error field, msg, opts
|
167
138
|
if @errors[field]
|
168
|
-
@errors[field] +=
|
139
|
+
@errors[field] += ", %s" % msg
|
169
140
|
else
|
170
|
-
if msg && msg[0,1].downcase == msg[0,1]
|
171
|
-
field_name = field.to_s.sub(/_id$/,
|
172
|
-
msg =
|
141
|
+
if msg && msg[0, 1].downcase == msg[0, 1]
|
142
|
+
field_name = opts[:name] || field.to_s.sub(/_id$/, "").humanize
|
143
|
+
msg = "%s %s" % [field_name, msg]
|
173
144
|
end
|
174
145
|
|
175
146
|
@errors[field] = msg
|
@@ -177,15 +148,30 @@ class Typero
|
|
177
148
|
end
|
178
149
|
|
179
150
|
def safe_type type
|
180
|
-
type.to_s.gsub(/[^\w]/,
|
151
|
+
type.to_s.gsub(/[^\w]/, "").classify
|
181
152
|
end
|
182
153
|
|
183
|
-
def
|
184
|
-
if
|
185
|
-
|
186
|
-
|
187
|
-
|
154
|
+
def check_required field, value, opts
|
155
|
+
return if !opts[:required] || value
|
156
|
+
msg = opts[:required].class == TrueClass ? "is required" : opts[:required]
|
157
|
+
add_error field, msg, opts
|
158
|
+
end
|
159
|
+
|
160
|
+
def check_filed_value field, value, opts
|
161
|
+
return unless value
|
162
|
+
|
163
|
+
klass = "Typero::%sType" % safe_type(opts[:type])
|
164
|
+
check = klass.constantize.new value, opts
|
165
|
+
check.value = check.default if check.value.nil?
|
166
|
+
|
167
|
+
unless check.value.nil?
|
168
|
+
begin
|
169
|
+
check.set
|
170
|
+
check.validate
|
171
|
+
check.value
|
172
|
+
rescue TypeError => e
|
173
|
+
add_error field, e.message, opts
|
174
|
+
end
|
188
175
|
end
|
189
176
|
end
|
190
177
|
end
|
191
|
-
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dino Reic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fast_blank
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
description: Simple and fast ruby type system. Enforce types as Array, Email, Boolean
|
28
28
|
for ruby class instances
|
29
29
|
email: reic.dino@gmail.com
|
@@ -34,24 +34,23 @@ files:
|
|
34
34
|
- "./.version"
|
35
35
|
- "./lib/adapters/sequel.rb"
|
36
36
|
- "./lib/typero.rb"
|
37
|
-
- "./lib/typero/
|
38
|
-
- "./lib/typero/type.rb"
|
39
|
-
- "./lib/typero/type/
|
40
|
-
- "./lib/typero/type/
|
41
|
-
- "./lib/typero/type/
|
42
|
-
- "./lib/typero/type/
|
43
|
-
- "./lib/typero/type/
|
44
|
-
- "./lib/typero/type/
|
45
|
-
- "./lib/typero/type/
|
46
|
-
- "./lib/typero/type/
|
47
|
-
- "./lib/typero/type/
|
48
|
-
- "./lib/typero/type/
|
49
|
-
- "./lib/typero/type/
|
50
|
-
- "./lib/typero/type/
|
51
|
-
- "./lib/typero/type/
|
52
|
-
- "./lib/typero/type/
|
53
|
-
- "./lib/typero/type/
|
54
|
-
- "./lib/typero/type/url.rb"
|
37
|
+
- "./lib/typero/params.rb"
|
38
|
+
- "./lib/typero/type/type.rb"
|
39
|
+
- "./lib/typero/type/types/boolean.rb"
|
40
|
+
- "./lib/typero/type/types/currency.rb"
|
41
|
+
- "./lib/typero/type/types/date.rb"
|
42
|
+
- "./lib/typero/type/types/datetime.rb"
|
43
|
+
- "./lib/typero/type/types/email.rb"
|
44
|
+
- "./lib/typero/type/types/float.rb"
|
45
|
+
- "./lib/typero/type/types/hash.rb"
|
46
|
+
- "./lib/typero/type/types/image.rb"
|
47
|
+
- "./lib/typero/type/types/integer.rb"
|
48
|
+
- "./lib/typero/type/types/label.rb"
|
49
|
+
- "./lib/typero/type/types/oib.rb"
|
50
|
+
- "./lib/typero/type/types/point.rb"
|
51
|
+
- "./lib/typero/type/types/string.rb"
|
52
|
+
- "./lib/typero/type/types/text.rb"
|
53
|
+
- "./lib/typero/type/types/url.rb"
|
55
54
|
- "./lib/typero/typero.rb"
|
56
55
|
homepage: https://github.com/dux/typero
|
57
56
|
licenses:
|
data/lib/typero/schema.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
# Base class for schema validation
|
2
|
-
|
3
|
-
class Typero
|
4
|
-
class Schema
|
5
|
-
attr_reader :rules, :db_rules
|
6
|
-
|
7
|
-
def initialize &block
|
8
|
-
@db_rules = []
|
9
|
-
@rules = {}
|
10
|
-
instance_exec &block
|
11
|
-
end
|
12
|
-
|
13
|
-
private
|
14
|
-
|
15
|
-
# used in dsl to define value
|
16
|
-
def set field, type=String, opts={}
|
17
|
-
raise "Field name not given (Typero)" unless field
|
18
|
-
|
19
|
-
field = field.to_sym
|
20
|
-
opts = type.is_a?(Hash) ? type : opts.merge(type: type)
|
21
|
-
opts[:type] ||= :string
|
22
|
-
opts[:required] = true if opts[:null].class == FalseClass
|
23
|
-
|
24
|
-
db :add_index, field if opts.delete(:index)
|
25
|
-
|
26
|
-
klass = Typero::Type.load opts[:type]
|
27
|
-
@rules[field] = parse_option opts
|
28
|
-
end
|
29
|
-
|
30
|
-
# coerce opts values
|
31
|
-
def parse_option opts
|
32
|
-
opts[:type] ||= 'string'
|
33
|
-
|
34
|
-
if opts[:type].is_a?(Array)
|
35
|
-
opts[:array_type] = opts[:type][0] if opts[:type][0]
|
36
|
-
opts[:type] = 'array'
|
37
|
-
end
|
38
|
-
|
39
|
-
opts[:type] = opts[:type].to_s.downcase
|
40
|
-
|
41
|
-
opts[:required] = opts.delete(:req) unless opts[:req].nil?
|
42
|
-
opts[:unique] = opts.delete(:uniq) unless opts[:uniq].nil?
|
43
|
-
opts[:description] = opts.delete(:desc) unless opts[:desc].nil?
|
44
|
-
|
45
|
-
opts
|
46
|
-
end
|
47
|
-
|
48
|
-
# pass values for db_schema only
|
49
|
-
# db :timestamps
|
50
|
-
# db :add_index, :code -> t.add_index :code
|
51
|
-
def db *args
|
52
|
-
@db_rules.push args
|
53
|
-
end
|
54
|
-
|
55
|
-
def link klass, opts={}
|
56
|
-
klass.is! Class
|
57
|
-
|
58
|
-
# TODO: Add can? update check before save
|
59
|
-
integer '%s_id' % klass.to_s.tableize.singularize, opts
|
60
|
-
end
|
61
|
-
|
62
|
-
def hash name
|
63
|
-
set name, :hash
|
64
|
-
end
|
65
|
-
|
66
|
-
# set :age, type: :integer -> integer :age
|
67
|
-
# email :email
|
68
|
-
# set :email, [:emails]
|
69
|
-
# email [:emails]
|
70
|
-
def method_missing name, *args, &block
|
71
|
-
field = args.shift
|
72
|
-
|
73
|
-
if field.class == Array
|
74
|
-
field = field.first
|
75
|
-
name = [name]
|
76
|
-
end
|
77
|
-
|
78
|
-
name = args.shift if name == :set
|
79
|
-
|
80
|
-
set field, type=name, *args
|
81
|
-
end
|
82
|
-
|
83
|
-
end
|
84
|
-
end
|
data/lib/typero/type.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# Master class
|
2
|
-
|
3
|
-
class Typero
|
4
|
-
class Type
|
5
|
-
attr_accessor :opts
|
6
|
-
attr_accessor :value
|
7
|
-
|
8
|
-
class << self
|
9
|
-
def load name
|
10
|
-
klass = 'Typero::%sType' % name.to_s.gsub(/[^\w]/,'').classify
|
11
|
-
|
12
|
-
if const_defined? klass
|
13
|
-
klass.constantize
|
14
|
-
else
|
15
|
-
raise ArgumentError, 'Typero type "%s" is not defined (%s)' % [name, klass]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
###
|
21
|
-
|
22
|
-
def initialize value, opts={}
|
23
|
-
@value = value
|
24
|
-
@opts = opts
|
25
|
-
end
|
26
|
-
|
27
|
-
# default validation for any type
|
28
|
-
def validate
|
29
|
-
true
|
30
|
-
end
|
31
|
-
|
32
|
-
# get error from option or the default one
|
33
|
-
def error_for name
|
34
|
-
@opts[name] || send(name)
|
35
|
-
end
|
36
|
-
|
37
|
-
def default
|
38
|
-
nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
|
data/lib/typero/type/array.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
class Typero::ArrayType < Typero::Type
|
2
|
-
def default
|
3
|
-
[]
|
4
|
-
end
|
5
|
-
|
6
|
-
def set
|
7
|
-
unless @value.class.to_s.index('Array')
|
8
|
-
@value = @value.to_s.sub(/^\{/,'').sub(/\}$/,'')
|
9
|
-
|
10
|
-
# split on new line and comma by default
|
11
|
-
@value = @value.split(/\s*[,\n]\s*/)
|
12
|
-
end
|
13
|
-
|
14
|
-
@value.uniq!
|
15
|
-
@value.compact!
|
16
|
-
|
17
|
-
if type = @opts[:array_type]
|
18
|
-
@value.map! { |el|
|
19
|
-
Typero.validate(type, el) { |msg|
|
20
|
-
raise TypeError.new "'%s' %s (%s)" % [el, msg, value_in_list_error]
|
21
|
-
}
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
# this converts Sequel::Postgres::PGArray to Array and fixes many problems
|
26
|
-
@value = @value.to_a if @value.class != Array
|
27
|
-
end
|
28
|
-
|
29
|
-
def validate
|
30
|
-
raise TypeError, error_for(:min_error) % @opts[:min] if @opts[:min] && @value.length < @opts[:min]
|
31
|
-
raise TypeError, error_for(:max_error) % @opts[:max] if @opts[:max] && @value.length > @opts[:max]
|
32
|
-
true
|
33
|
-
end
|
34
|
-
|
35
|
-
def min_error
|
36
|
-
'min array lenght is %s elements'
|
37
|
-
end
|
38
|
-
|
39
|
-
def max_error
|
40
|
-
'max array lenght is %s elements'
|
41
|
-
end
|
42
|
-
|
43
|
-
def value_in_list_error
|
44
|
-
'value in list'
|
45
|
-
end
|
46
|
-
|
47
|
-
def db_field
|
48
|
-
check = Typero::Type.load(@opts[:array_type]).new nil, {}
|
49
|
-
schema = check.db_field
|
50
|
-
schema[1] ||= {}
|
51
|
-
schema[1].merge! array: true
|
52
|
-
schema
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
data/lib/typero/type/email.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
class Typero::EmailType < Typero::Type
|
2
|
-
|
3
|
-
def set
|
4
|
-
@value = @value.downcase.gsub(/\s+/,'+')
|
5
|
-
end
|
6
|
-
|
7
|
-
def validate
|
8
|
-
raise TypeError, error_for(:not_8_chars_error) unless @value.to_s.length > 7
|
9
|
-
raise TypeError, error_for(:missing_monkey_error) unless @value.include?('@')
|
10
|
-
end
|
11
|
-
|
12
|
-
def not_8_chars_error
|
13
|
-
'is not having at least 8 characters'
|
14
|
-
end
|
15
|
-
|
16
|
-
def missing_monkey_error
|
17
|
-
'is missing @'
|
18
|
-
end
|
19
|
-
|
20
|
-
def db_field
|
21
|
-
opts = {}
|
22
|
-
opts[:limit] = @opts[:max] || 120
|
23
|
-
opts[:null] = false if @opts[:required]
|
24
|
-
[:string, opts]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
data/lib/typero/type/float.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
class Typero::FloatType < Typero::Type
|
2
|
-
|
3
|
-
def set
|
4
|
-
@value = @value.to_f
|
5
|
-
end
|
6
|
-
|
7
|
-
def validate
|
8
|
-
raise TypeError, error_for(:min_length_error) % @opts[:min] if @opts[:min] && value < @opts[:min]
|
9
|
-
raise TypeError, error_for(:max_length_error) % @opts[:max] if @opts[:max] && value > @opts[:max]
|
10
|
-
end
|
11
|
-
|
12
|
-
def min_length_error
|
13
|
-
"min lenght is %s"
|
14
|
-
end
|
15
|
-
|
16
|
-
def max_length_error
|
17
|
-
"max lenght is %s"
|
18
|
-
end
|
19
|
-
|
20
|
-
def db_field
|
21
|
-
opts = {}
|
22
|
-
opts[:null] = false if @opts[:required]
|
23
|
-
[:float, opts]
|
24
|
-
end
|
25
|
-
|
26
|
-
end
|
27
|
-
|
data/lib/typero/type/image.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
class Typero::ImageType < Typero::Type
|
2
|
-
# FORMATS = %w[jpg jpeg gif png svg]
|
3
|
-
|
4
|
-
def set
|
5
|
-
@value = 'https://%s' % @value unless @value.include?('://')
|
6
|
-
end
|
7
|
-
|
8
|
-
def validate
|
9
|
-
raise TypeError, error_for(:not_starting_error) unless @value =~ /^https?:\/\/./
|
10
|
-
# ext = @value.split('.').last.downcase
|
11
|
-
# raise TypeError, error_for(:not_image_format) unless FORMATS.include?(ext)
|
12
|
-
end
|
13
|
-
|
14
|
-
def not_starting_error
|
15
|
-
'URL is not starting with http'
|
16
|
-
end
|
17
|
-
|
18
|
-
# def not_image_format
|
19
|
-
# 'URL is not ending with %s' % FORMATS.join(', ')
|
20
|
-
# end
|
21
|
-
|
22
|
-
def db_field
|
23
|
-
opts = {}
|
24
|
-
opts[:null] = false if @opts[:required]
|
25
|
-
[:string, opts]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
data/lib/typero/type/integer.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
class Typero::IntegerType < Typero::Type
|
2
|
-
def set
|
3
|
-
@value = @value.to_i
|
4
|
-
end
|
5
|
-
|
6
|
-
def validate
|
7
|
-
raise TypeError, error_for(:min_value_error) % [@opts[:min], @value] if @opts[:min] && @value < @opts[:min]
|
8
|
-
raise TypeError, error_for(:max_value_error) % [@opts[:max], @value] if @opts[:max] && @value > @opts[:max]
|
9
|
-
end
|
10
|
-
|
11
|
-
def min_value_error
|
12
|
-
'min is %s, got %s'
|
13
|
-
end
|
14
|
-
|
15
|
-
def max_value_error
|
16
|
-
'max is %s, got %s'
|
17
|
-
end
|
18
|
-
|
19
|
-
def db_field
|
20
|
-
opts = {}
|
21
|
-
opts[:null] = false if @opts[:required]
|
22
|
-
opts[:default] = @opts[:default]
|
23
|
-
[:integer, opts]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
data/lib/typero/type/string.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
class Typero::StringType < Typero::Type
|
2
|
-
def set
|
3
|
-
@value = @value.to_s unless @value.is_a?(String)
|
4
|
-
@value = @value.downcase if @opts[:downcase]
|
5
|
-
end
|
6
|
-
|
7
|
-
def validate
|
8
|
-
raise TypeError, error_for(:min_length_error) % [@opts[:min], @value.length] if @opts[:min] && @value.length < @opts[:min]
|
9
|
-
raise TypeError, error_for(:max_length_error) % [@opts[:max], @value.length] if @opts[:max] && @value.length > @opts[:max]
|
10
|
-
end
|
11
|
-
|
12
|
-
# ready for localization
|
13
|
-
|
14
|
-
def min_length_error
|
15
|
-
'min lenght is %s, you have %s'
|
16
|
-
end
|
17
|
-
|
18
|
-
def max_length_error
|
19
|
-
'max lenght is %s, you have %s'
|
20
|
-
end
|
21
|
-
|
22
|
-
def db_field
|
23
|
-
opts = {}
|
24
|
-
opts[:limit] = @opts[:max] || 255
|
25
|
-
opts[:null] = false if @opts[:required]
|
26
|
-
opts[:default] = @opts[:default]
|
27
|
-
[:string, opts]
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|