typero 0.5.2 → 0.9.3

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/lib/adapters/sequel.rb +11 -33
  4. data/lib/typero.rb +8 -189
  5. data/lib/typero/params.rb +126 -0
  6. data/lib/typero/schema.rb +154 -0
  7. data/lib/typero/type/type.rb +125 -0
  8. data/lib/typero/type/types/boolean_type.rb +26 -0
  9. data/lib/typero/type/types/currency_type.rb +21 -0
  10. data/lib/typero/type/types/date_type.rb +35 -0
  11. data/lib/typero/type/types/datetime_type.rb +16 -0
  12. data/lib/typero/type/types/email_type.rb +20 -0
  13. data/lib/typero/type/types/float_type.rb +24 -0
  14. data/lib/typero/type/types/hash_type.rb +31 -0
  15. data/lib/typero/type/types/image_type.rb +22 -0
  16. data/lib/typero/type/types/integer_type.rb +16 -0
  17. data/lib/typero/type/types/label_type.rb +20 -0
  18. data/lib/typero/type/types/locale_type.rb +13 -0
  19. data/lib/typero/type/types/model_type.rb +23 -0
  20. data/lib/typero/type/{oib.rb → types/oib_type.rb} +16 -18
  21. data/lib/typero/type/types/point_type.rb +25 -0
  22. data/lib/typero/type/types/string_type.rb +20 -0
  23. data/lib/typero/type/types/text_type.rb +10 -0
  24. data/lib/typero/type/types/time_type.rb +5 -0
  25. data/lib/typero/type/types/timezone_type.rb +15 -0
  26. data/lib/typero/type/types/url_type.rb +13 -0
  27. data/lib/typero/typero.rb +98 -0
  28. metadata +33 -27
  29. data/lib/typero/type.rb +0 -32
  30. data/lib/typero/type/array.rb +0 -55
  31. data/lib/typero/type/boolean.rb +0 -16
  32. data/lib/typero/type/currency.rb +0 -17
  33. data/lib/typero/type/date.rb +0 -6
  34. data/lib/typero/type/datetime.rb +0 -6
  35. data/lib/typero/type/email.rb +0 -27
  36. data/lib/typero/type/float.rb +0 -27
  37. data/lib/typero/type/geography.rb +0 -13
  38. data/lib/typero/type/hash.rb +0 -18
  39. data/lib/typero/type/integer.rb +0 -25
  40. data/lib/typero/type/label.rb +0 -20
  41. data/lib/typero/type/point.rb +0 -25
  42. data/lib/typero/type/string.rb +0 -29
  43. data/lib/typero/type/text.rb +0 -7
  44. data/lib/typero/type/url.rb +0 -20
@@ -0,0 +1,125 @@
1
+ # Master class
2
+
3
+ module Typero
4
+ class Type
5
+ ERRORS = {
6
+ en: {
7
+ # errors shared between various types
8
+ min_length_error: 'min lenght is %s, you have %s',
9
+ max_length_error: 'max lenght is %s, you have %s',
10
+ min_value_error: 'min is %s, got %s',
11
+ max_value_error: 'max is %s, got %s',
12
+ unallowed_characters_error: 'is having unallowed characters',
13
+ not_in_range: 'Value in not in allowed range (%s)'
14
+ }
15
+ }
16
+
17
+ # default shared allowed opts keys
18
+ OPTS = {}
19
+ OPTS_KEYS = [
20
+ :allow,
21
+ :allowed,
22
+ :array,
23
+ :default,
24
+ :description,
25
+ :max_count,
26
+ :meta,
27
+ :min_count,
28
+ :model,
29
+ :name,
30
+ :req,
31
+ :required,
32
+ :type,
33
+ :values
34
+ ]
35
+
36
+ attr_reader :opts
37
+
38
+ class << self
39
+ def load name
40
+ klass = 'Typero::%sType' % name.to_s.gsub(/[^\w]/,'').classify
41
+
42
+ if const_defined? klass
43
+ klass.constantize
44
+ else
45
+ raise ArgumentError, 'Typero type "%s" is not defined (%s)' % [name, klass]
46
+ end
47
+ end
48
+
49
+ def error locale, key, message
50
+ locale = locale.to_sym
51
+ ERRORS[locale] ||= {}
52
+ ERRORS[locale][key.to_sym] = message
53
+ end
54
+
55
+ def opts key, desc
56
+ OPTS_KEYS.push key unless OPTS_KEYS.include?(key)
57
+
58
+ OPTS[self] ||= {}
59
+ OPTS[self][key] = desc
60
+ end
61
+ end
62
+
63
+ ###
64
+
65
+ def initialize value, opts={}, &block
66
+ value = value.strip.rstrip if value.is_a?(String)
67
+
68
+ @value = value
69
+ @opts = opts
70
+ @block = block
71
+ end
72
+
73
+ def value &block
74
+ if block_given?
75
+ @value = block.call @value
76
+ else
77
+ @value
78
+ end
79
+ end
80
+
81
+ def get
82
+ if value.nil?
83
+ opts[:default].nil? ? default : opts[:default]
84
+ else
85
+ set
86
+ error_for(:not_in_range, opts[:values].join(', ')) if opts[:values] && !opts[:values].include?(@value)
87
+ value
88
+ end
89
+ end
90
+
91
+ def default
92
+ nil
93
+ end
94
+
95
+ def db_field
96
+ out = db_schema
97
+ out[1] ||= {}
98
+ out[1][:default] ||= opts[:default] unless opts[:default].nil?
99
+ out[1][:null] = false if !opts[:array] && opts[:required]
100
+ out
101
+ end
102
+
103
+ private
104
+
105
+ # get error from option or the default one
106
+ def error_for name, *args
107
+ locale =
108
+ if defined?(Lux)
109
+ Lux.current.locale.to_s
110
+ elsif defined?(I18n)
111
+ I18n.locale
112
+ end
113
+
114
+ locale = :en if locale.to_s == ''
115
+ pointer = ERRORS[locale.to_sym] || ERRORS[:en]
116
+ error = @opts.dig(:meta, locale, name) || @opts.dig(:meta, name) || pointer[name]
117
+ error = error % args if args.first
118
+
119
+ raise 'Type error :%s not defined' % name unless error
120
+ raise TypeError.new(error)
121
+ end
122
+ end
123
+ end
124
+
125
+
@@ -0,0 +1,26 @@
1
+ class Typero::BooleanType < Typero::Type
2
+ error :en, :unsupported_boolean, 'Unsupported boolean param value: %s'
3
+
4
+ def set
5
+ value do |_|
6
+ bool = _.to_s
7
+
8
+ if value == ''
9
+ false
10
+ elsif %w(true 1 on).include?(bool)
11
+ true
12
+ elsif %w(false 0 off).include?(bool)
13
+ false
14
+ else
15
+ error_for :unsupported_boolean, bool
16
+ end
17
+ end
18
+ end
19
+
20
+ def db_schema
21
+ [:boolean, {
22
+ default: opts[:default] || false
23
+ }]
24
+ end
25
+ end
26
+
@@ -0,0 +1,21 @@
1
+ # you should not use this filed for currency calculations
2
+ # use integer and covert in code
3
+ # Example: use cents and divide by 100 for $
4
+
5
+ require_relative './float_type'
6
+
7
+ class Typero::CurrencyType < Typero::FloatType
8
+
9
+ def set
10
+ value { |data| data.to_f.round(2) }
11
+ end
12
+
13
+ def db_schema
14
+ [:decimal, {
15
+ precision: 8,
16
+ scale: 2
17
+ }]
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,35 @@
1
+ class Typero::DateType < Typero::Type
2
+ error :en, :min_date, 'Minimal allowed date is %s'
3
+ error :en, :max_date, 'Maximal allowed date is %s'
4
+
5
+ def set
6
+ unless [Date].include?(value.class)
7
+ value { |data| DateTime.parse(data) }
8
+ end
9
+
10
+ value { |data| DateTime.new(data.year, data.month, data.day) }
11
+
12
+ check_date_min_max
13
+ end
14
+
15
+ def db_schema
16
+ [:date, {}]
17
+ end
18
+
19
+ private
20
+
21
+ def check_date_min_max
22
+ if min = opts[:min]
23
+ min = DateTime.parse(min)
24
+ error_for(:min_date, min) % min if min > value
25
+ end
26
+
27
+ if max = opts[:max]
28
+ max = DateTime.parse(max)
29
+ error_for(:max_date, max) % max if value > max
30
+ end
31
+
32
+ value
33
+ end
34
+ end
35
+
@@ -0,0 +1,16 @@
1
+ require_relative 'date_type'
2
+
3
+ class Typero::DatetimeType < Typero::DateType
4
+ def set
5
+ unless [Time, DateTime].include?(value.class)
6
+ value { |data| DateTime.parse(data) }
7
+ end
8
+
9
+ check_date_min_max
10
+ end
11
+
12
+ def db_schema
13
+ [:datetime]
14
+ end
15
+ end
16
+
@@ -0,0 +1,20 @@
1
+ class Typero::EmailType < Typero::Type
2
+ error :en, :not_8_chars_error, 'is not having at least 8 characters'
3
+ error :en, :missing_monkey_error, 'is missing @'
4
+
5
+ def set
6
+ value do |email|
7
+ email.downcase.gsub(/\s+/,'+')
8
+ end
9
+
10
+ error_for(:not_8_chars_error) unless value.to_s.length > 7
11
+ error_for(:missing_monkey_error) unless value.include?('@')
12
+ end
13
+
14
+ def db_schema
15
+ [:string, {
16
+ limit: @opts[:max] || 120
17
+ }]
18
+ end
19
+ end
20
+
@@ -0,0 +1,24 @@
1
+ class Typero::FloatType < Typero::Type
2
+ opts :min, 'Minimum value'
3
+ opts :max, 'Maximun value'
4
+ opts :round, 'Round to (decimal spaces)'
5
+
6
+ def set
7
+ @value =
8
+ if opts[:round]
9
+ value.to_f.round(opts[:round])
10
+ else
11
+ value.to_f
12
+ end
13
+
14
+ error_for(:min_length_error, opts[:min], value) if opts[:min] && value < opts[:min]
15
+ error_for(:max_length_error, opts[:max], value) if opts[:max] && value > opts[:max]
16
+ end
17
+
18
+ def db_schema
19
+ opts = {}
20
+ opts[:null] = false if opts[:required]
21
+ [:float, opts]
22
+ end
23
+ end
24
+
@@ -0,0 +1,31 @@
1
+ class Typero::HashType < Typero::Type
2
+ error :en, :not_hash_type_error, 'value is not hash type'
3
+
4
+ def set
5
+ if value.is_a?(String) && value[0,1] == '{'
6
+ @value = JSON.load(value)
7
+ end
8
+
9
+ @value ||= {}
10
+
11
+ error_for(:not_hash_type_error) unless @value.respond_to?(:keys) && @value.respond_to?(:values)
12
+
13
+ if opts[:allow]
14
+ for key in @value.keys
15
+ @value.delete(key) unless opts[:allow].include?(key)
16
+ end
17
+ end
18
+ end
19
+
20
+ def default
21
+ {}
22
+ end
23
+
24
+ def db_schema
25
+ [:jsonb, {
26
+ null: false,
27
+ default: '{}'
28
+ }]
29
+ end
30
+ end
31
+
@@ -0,0 +1,22 @@
1
+ class Typero::ImageType < Typero::Type
2
+ FORMATS = %w[jpg jpeg gif png svg webp]
3
+
4
+ error :en, :image_not_starting_error, 'URL is not starting with http'
5
+ error :en, :image_not_image_format, 'URL is not ending with %s' % FORMATS.join(', ')
6
+
7
+ opts :strict, 'Force image to have known extension (%s)' % FORMATS.join(', ')
8
+
9
+ def set
10
+ error_for(:image_not_starting_error) unless value =~ /^https?:\/\/./
11
+
12
+ if opts[:strict]
13
+ ext = value.split('.').last.downcase
14
+ error_for(:image_not_image_format) unless FORMATS.include?(ext)
15
+ end
16
+ end
17
+
18
+ def db_schema
19
+ [:string]
20
+ end
21
+ end
22
+
@@ -0,0 +1,16 @@
1
+ class Typero::IntegerType < Typero::Type
2
+ opts :min, 'Minimum value'
3
+ opts :max, 'Maximun value'
4
+
5
+ def set
6
+ value(&:to_i)
7
+
8
+ error_for(:min_value_error, opts[:min], value) if opts[:min] && value < opts[:min]
9
+ error_for(:max_value_error, opts[:max], value) if opts[:max] && value > opts[:max]
10
+ end
11
+
12
+ def db_schema
13
+ [:integer, {}]
14
+ end
15
+ end
16
+
@@ -0,0 +1,20 @@
1
+ class Typero::LabelType < Typero::Type
2
+ def set
3
+ value do |data|
4
+ data
5
+ .to_s
6
+ .gsub(/\s+/,'-')
7
+ .gsub(/[^\w\-]/,'')
8
+ .gsub(/\-+/, '-')[0,30]
9
+ .downcase
10
+ end
11
+
12
+ error_for(:unallowed_characters_error) unless value =~ /^[\w\-]+$/
13
+ end
14
+
15
+ def db_schema
16
+ [:string, {
17
+ limit: 30
18
+ }]
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ class Typero::LocaleType < Typero::Type
2
+ error :en, :locale_bad_format, 'Locale "%s" is in bad format (should be xx or xx-xx)'
3
+
4
+ def set
5
+ error_for(:locale_bad_format, value) unless value =~ /^[\w\-]{2,5}$/
6
+ end
7
+
8
+ def db_schema
9
+ [:string, { limit: 5 }]
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,23 @@
1
+ class Typero::ModelType < Typero::Type
2
+ def set
3
+ value(&:to_h)
4
+
5
+ errors = {}
6
+
7
+ schema = opts[:model].is_a?(Typero::Schema) ? opts[:model] : Typero.schema(opts[:model])
8
+
9
+ # by default models in schems are strict true (remove undefined keys)
10
+ schema.validate value, strict: true do |field, error|
11
+ errors[field] = error
12
+ end
13
+
14
+ raise TypeError.new errors.to_json if errors.keys.first
15
+ end
16
+
17
+ def db_schema
18
+ [:jsonb, {
19
+ null: false
20
+ }]
21
+ end
22
+ end
23
+
@@ -1,4 +1,20 @@
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 do |data|
6
+ check?(data) ? data.to_i : error_for(:not_an_oib_error)
7
+ end
8
+ end
9
+
10
+ def db_schema
11
+ [:string, {
12
+ limit: 11
13
+ }]
14
+ end
15
+
16
+ private
17
+
2
18
  # http://domagoj.eu/oib/
3
19
  def check? oib
4
20
  oib = oib.to_s
@@ -19,23 +35,5 @@ class Typero::OibType < Typero::Type
19
35
  return control_sum == oib.at(10).to_i
20
36
  end
21
37
 
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[:req]
37
- opts[:limit] = 11
38
- [:string, opts]
39
- end
40
38
  end
41
39