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 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
-