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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 448ffb9631eeb66159f13e45da0e28e600f91f3ed58684789740f2ad351a317b
4
- data.tar.gz: 41feafdf4c139e47c02dc71c0c5dbdea154b6e75e0b8a746e1b7f41c6beb89d6
3
+ metadata.gz: ba656a56b8d2e5d3d977a2973f6de881eefd3257397a4a4ebe908a7abaed70a7
4
+ data.tar.gz: 8374f340b6af235c274ceaa9aace33f0c462b5a203d667a199fa519c1f56f53e
5
5
  SHA512:
6
- metadata.gz: 28c28657d7a0c7c5e71b5cfac08eb314a857c9b4e01337666ab38f65c64aa99f1c07a56d3276f8446e0134bc28968b7b34fb13232769c97ed01c6847cdab657e
7
- data.tar.gz: 65f56f37df4731adad738862fdb79ed540bdd22a3a85744f519370e4ae5d905bafa74ab4720d17d6916c068a4f9508225d20cde64a6735c29a9f9e978a9ef145
6
+ metadata.gz: ba14e29595a5d17c3c3d102f49d1234a82e7ea23a7062a4a61c6dc56770d9d4922f1be7ae01663c41382efb6c6c51ed3fb5dfd4c188315ec1a5bf3f43eb699fc
7
+ data.tar.gz: c2d8b322fac4048067b809bf7944c8ca27bb787d4b590a68eec62169a768a65ddbb089359da73f550a32b59a0405b95629ae4932bf7a3dfb9d5426e2e26d3e7d
data/.version CHANGED
@@ -1 +1 @@
1
- 0.7.0
1
+ 0.8.0
@@ -2,13 +2,17 @@
2
2
 
3
3
  module Sequel::Plugins::TyperoAttributes
4
4
  module ClassMethods
5
- attr_accessor :typero
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
- typero = self.class.typero || return
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)
@@ -1,10 +1,10 @@
1
1
  # base libs
2
2
  require_relative 'typero/typero'
3
- require_relative 'typero/schema'
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
+
@@ -1,3 +1,5 @@
1
+ # you should not use this filed for currency calculations
2
+
1
3
  require_relative './float'
2
4
 
3
5
  class Typero::CurrencyType < Typero::FloatType
@@ -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
- raise TypeError, error_for(:not_hash_type_error) unless @value.is_a?(Hash)
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
- raise TypeError, error_for(:unallowed_characters_error) unless @value =~ /^[\w\-]+$/
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
- raise TypeError, error_for(:unallowed_characters_error)
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,6 +1,9 @@
1
1
  require_relative 'string'
2
2
 
3
3
  class Typero::TextType < Typero::StringType
4
+ opts :min, 'Minimun string length'
5
+ opts :max, 'Maximun string length'
6
+
4
7
  def db_field
5
8
  opts = {}
6
9
  opts[:null] = false if @opts[:required]
@@ -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
- raise TypeError, error_for(:not_starting_error) unless @value =~ /^https?:\/\/./
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
@@ -17,127 +17,100 @@
17
17
  # rules.validate(@object) { |errors| ... }
18
18
 
19
19
  class Typero
20
- SCHEMAS = {}
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 name=nil, &block
72
- type = :default
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 or object with hash variable interface
48
+ # validates any instance object with hash variable interface
94
49
  # it also coarces values
95
- def validate instance
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
- instance[field] = opts[:default] if opts[:default] && instance[field].blank?
56
+ @object[field] = opts[:default] if opts[:default] && @object[field].blank?
101
57
 
102
58
  # get field value
103
- value = instance[field]
104
-
105
- if value.present?
106
- klass = 'Typero::%sType' % safe_type(opts[:type])
107
- check = klass.constantize.new value, opts
108
- check.value = check.default if check.value.nil?
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
- elsif opts[:required]
120
- msg = opts[:required].class == TrueClass ? 'is required' : opts[:required]
121
- add_error field, msg
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? instance
133
- errors = validate instance
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
- type, opts = Typero::Type.load(opts[:type]).new(nil, opts).db_field
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 filter=nil, &block
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
- for k, v in out
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] += ', %s' % msg
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$/,'').humanize
172
- msg = '%s %s' % [field_name, 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]/,'').classify
151
+ type.to_s.gsub(/[^\w]/, "").classify
181
152
  end
182
153
 
183
- def name_fix name
184
- if name.is_a?(Symbol)
185
- name.to_s.classify if name.is_a?(Symbol)
186
- else
187
- name.to_s
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.7.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-02-29 00:00:00.000000000 Z
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: '1'
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: '1'
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/schema.rb"
38
- - "./lib/typero/type.rb"
39
- - "./lib/typero/type/array.rb"
40
- - "./lib/typero/type/boolean.rb"
41
- - "./lib/typero/type/currency.rb"
42
- - "./lib/typero/type/date.rb"
43
- - "./lib/typero/type/datetime.rb"
44
- - "./lib/typero/type/email.rb"
45
- - "./lib/typero/type/float.rb"
46
- - "./lib/typero/type/hash.rb"
47
- - "./lib/typero/type/image.rb"
48
- - "./lib/typero/type/integer.rb"
49
- - "./lib/typero/type/label.rb"
50
- - "./lib/typero/type/oib.rb"
51
- - "./lib/typero/type/point.rb"
52
- - "./lib/typero/type/string.rb"
53
- - "./lib/typero/type/text.rb"
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:
@@ -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
@@ -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
-
@@ -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
-
@@ -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
-
@@ -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
-
@@ -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
-
@@ -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
-
@@ -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
-