typero 0.7.0 → 0.8.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 +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
|
-
|