typero 0.8.1 → 0.9.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/lib/typero.rb +2 -1
  4. data/lib/typero/exporter.rb +93 -0
  5. data/lib/typero/params.rb +24 -9
  6. data/lib/typero/schema.rb +132 -0
  7. data/lib/typero/type/type.rb +37 -9
  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 +32 -0
  11. data/lib/typero/type/types/datetime_type.rb +14 -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 +25 -0
  15. data/lib/typero/type/types/{image.rb → image_type.rb} +4 -10
  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/model_type.rb +21 -0
  19. data/lib/typero/type/types/{oib.rb → oib_type.rb} +7 -10
  20. data/lib/typero/type/types/point_type.rb +25 -0
  21. data/lib/typero/type/types/string_type.rb +20 -0
  22. data/lib/typero/type/types/{text.rb → text_type.rb} +3 -5
  23. data/lib/typero/type/types/url_type.rb +13 -0
  24. data/lib/typero/typero.rb +50 -137
  25. metadata +20 -18
  26. data/lib/typero/func.rb +0 -14
  27. data/lib/typero/type/types/boolean.rb +0 -16
  28. data/lib/typero/type/types/currency.rb +0 -19
  29. data/lib/typero/type/types/date.rb +0 -10
  30. data/lib/typero/type/types/datetime.rb +0 -10
  31. data/lib/typero/type/types/email.rb +0 -21
  32. data/lib/typero/type/types/float.rb +0 -21
  33. data/lib/typero/type/types/hash.rb +0 -22
  34. data/lib/typero/type/types/integer.rb +0 -21
  35. data/lib/typero/type/types/label.rb +0 -15
  36. data/lib/typero/type/types/point.rb +0 -29
  37. data/lib/typero/type/types/string.rb +0 -23
  38. data/lib/typero/type/types/url.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1b145b43e7736b2c9f673b6685388a39ee83fcba0bbcbc0ad2fcf8af2c1f692
4
- data.tar.gz: dd730a85ab31d2b181b875d7052e3b109a263caba0618d869e262e3004e6d52f
3
+ metadata.gz: 8e78c338c182793ab2bffd1b4c5469d5285f57f55697402b6e80fb1881a3f7a8
4
+ data.tar.gz: 18a803b38ef8a24cb3756a43e5b5704082a08f4d805be57239cb054c28e6f132
5
5
  SHA512:
6
- metadata.gz: 86b7d2a84fccf9d800ae9c121773d3ae960b6f2c32100466836d6f07056ad2f2affa4fdbe8df8c91ea5cc208d7e006db993ddbce86c30b9652077b87f22cd4c5
7
- data.tar.gz: 95a83f6b58874de8b27edb08811ea9543ebe7036550364d535f1a73042af90d77e36a891ccfcab9db36817572e6a400c203356cc54fb929358730bfc8425999e
6
+ metadata.gz: 1c02d8cec42f465c5cf34b0b69c856cf193f1c5e289898af0aefdebf1d61ed91c95148a1fa9ca1c047a07810c99bd518946dbfe323e0e53c4502b66b08910c85
7
+ data.tar.gz: de185fca3ac80a4e7e9d6b87778872b3b352f89678f334e24b0047480d401cbd0abf95bb8940ba0e29d9d3f485ef62521b38335f1f6cc3a7d1f59194383f4a67
data/.version CHANGED
@@ -1 +1 @@
1
- 0.8.1
1
+ 0.9.0
@@ -1,7 +1,8 @@
1
1
  # base libs
2
2
  require_relative 'typero/typero'
3
- require_relative 'typero/func'
3
+ require_relative 'typero/schema'
4
4
  require_relative 'typero/params'
5
+ require_relative 'typero/exporter'
5
6
  require_relative 'typero/type/type'
6
7
 
7
8
  # checker types
@@ -0,0 +1,93 @@
1
+ module Typero
2
+ class Exporter
3
+ EXPORTERS ||= {}
4
+
5
+ attr_accessor :response
6
+
7
+ def initialize model, opts={}
8
+ opts = { user: opts } unless opts.is_a?(Hash)
9
+
10
+ opts[:exporter] ||= model.class
11
+ opts[:depth] ||= 1
12
+ opts[:current_depth] ||= 0
13
+
14
+ exporter = opts.delete(:exporter).to_s.classify
15
+
16
+ @model = model
17
+ @opts = opts
18
+ @block = EXPORTERS[exporter] || raise('Exporter "%s" (:%s) not found' % [block, block.underscore])
19
+ @response = {}
20
+ end
21
+
22
+ def render
23
+ instance_exec &@block
24
+ @response
25
+ end
26
+
27
+ private
28
+
29
+ def export object, opts={}
30
+ if object.is_a?(Symbol)
31
+ return property object, export(model.send(object))
32
+ end
33
+
34
+ return if @opts[:current_depth] >= @opts[:depth]
35
+
36
+ @opts[:current_depth] += 1
37
+ out = self.class.new(object, @opts.merge(opts)).render
38
+ @opts[:current_depth] -= 1
39
+ out
40
+ end
41
+
42
+ def property name, data=:_undefined
43
+ if block_given?
44
+ data = yield if data == :_undefined
45
+ @response[name] = data
46
+ else
47
+ data = data == :_undefined ? model.send(name) : data
48
+
49
+ if data.respond_to?(:export_json)
50
+ data = data.export_json
51
+ elsif data.respond_to?(:to_h)
52
+ data = data.to_h
53
+ end
54
+
55
+ @response[name] = data
56
+ end
57
+ end
58
+ alias :prop :property
59
+
60
+ def hproperty name
61
+ @response[name] = model[name]
62
+ end
63
+ alias :hprop :hproperty
64
+
65
+ def namespace name, &block
66
+
67
+ end
68
+
69
+ def meta &block
70
+ namespace :meta, &block
71
+ end
72
+
73
+ def model
74
+ @model
75
+ end
76
+
77
+ # get current user from globals if globals defined
78
+ def user
79
+ if @opts[:user]
80
+ @opts[:user]
81
+ elsif defined?(User) && User.respond_to?(:current)
82
+ User.current
83
+ elsif defined?(Current) && Current.respond_to?(:user)
84
+ Current.user
85
+ elsif current_user = Thread.current[:current_user]
86
+ current_user
87
+ else
88
+ nil
89
+ end
90
+ end
91
+ end
92
+ end
93
+
@@ -1,9 +1,7 @@
1
1
  # Base class for schema validation
2
2
 
3
- class Typero
3
+ module Typero
4
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
5
  attr_reader :rules, :db_rules
8
6
 
9
7
  def initialize &block
@@ -25,8 +23,7 @@ class Typero
25
23
  opts[:type] ||= args[0]
26
24
  end
27
25
 
28
- opts[:type] ||= :string
29
- opts[:required] = true unless opts[:required].is_a?(FalseClass) || opts[:req].is_a?(FalseClass)
26
+ opts[:type] = :string if opts[:type].nil?
30
27
 
31
28
  field = field.to_s
32
29
 
@@ -36,6 +33,8 @@ class Typero
36
33
  opts[:required] = false
37
34
  end
38
35
 
36
+ opts[:required] = true if opts[:required].nil?
37
+
39
38
  # array that allows duplicates
40
39
  if opts[:type].is_a?(Array)
41
40
  opts[:type] = opts[:type].first
@@ -48,20 +47,36 @@ class Typero
48
47
  opts[:array] = true
49
48
  end
50
49
 
50
+ # Boolean
51
+ if opts[:type].is_a?(TrueClass)
52
+ opts[:required] = false
53
+ opts[:default] = true
54
+ opts[:type] = :boolean
55
+ elsif opts[:type].is_a?(FalseClass)
56
+ opts[:required] = false
57
+ opts[:default] = false
58
+ opts[:type] = :boolean
59
+ end
60
+
61
+ opts[:model] = opts.delete(:schema) if opts[:schema]
62
+ opts[:type] = :model if opts[:model]
63
+
51
64
  opts[:type] ||= 'string'
52
- opts[:type] = opts[:type].to_s.downcase
65
+ opts[:type] = opts[:type].to_s.downcase.to_sym
53
66
 
54
67
  opts[:description] = opts.delete(:desc) unless opts[:desc].nil?
55
68
 
56
69
  # 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
70
+ result = opts.keys - Typero::Type::OPTS_KEYS
71
+ raise ArgumentError.new('Unallowed Type params found: "%s", allowed: %s' % [result.join(' and '), Typero::Type::OPTS_KEYS.sort]) if result.length > 0
59
72
 
60
73
  field = field.to_sym
61
74
 
62
75
  db :add_index, field if opts.delete(:index)
63
76
 
64
- klass = Typero::Type.load opts[:type]
77
+ # trigger error if type not found
78
+ Typero::Type.load opts[:type]
79
+
65
80
  @rules[field] = opts
66
81
  end
67
82
 
@@ -0,0 +1,132 @@
1
+ module Typero
2
+ class Schema
3
+ SCHEMAS = {}
4
+
5
+ # accepts dsl block to
6
+ def initialize &block
7
+ raise "Params not defined" unless block_given?
8
+ @schema = Params.new &block
9
+ end
10
+
11
+ # validates any instance object with hash variable interface
12
+ # it also coarces values
13
+ def validate object
14
+ @object = object
15
+ @errors = {}
16
+
17
+ @schema.rules.each do |field, opts|
18
+ # set value to default if value is blank and default given
19
+ @object[field] = opts[:default] if opts[:default] && @object[field].blank?
20
+
21
+ # get field value
22
+ value = @object[field]
23
+
24
+ if opts[:array]
25
+ unless value.is_a?(Array)
26
+ opts[:delimiter] ||= /\s*,\s*/
27
+ value = value.to_s.split(opts[:delimiter])
28
+ end
29
+
30
+ value = value
31
+ .map { |el| check_filed_value field, el, opts }
32
+ .map { |el| el.to_s == '' ? nil : el }
33
+ .compact
34
+
35
+ value = Set.new(value).to_a unless opts[:duplicates]
36
+
37
+ opts[:max_count] ||= 100
38
+ 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]
39
+
40
+ add_required_error field, value.first, opts
41
+ else
42
+ value = check_filed_value field, value, opts
43
+ add_required_error field, value, opts
44
+ end
45
+
46
+ # if value is not list of allowed values, raise error
47
+ if opts[:allowed] && !opts[:values].include?(value)
48
+ add_error field, 'Value "%s" is not allowed' % value, opts
49
+ end
50
+
51
+ # present empty string values as nil
52
+ @object[field] = value.to_s.sub(/\s+/, '') == '' ? nil : value
53
+ end
54
+
55
+ if @errors.keys.length > 0 && block_given?
56
+ @errors.each { |k, v| yield(k, v) }
57
+ end
58
+
59
+ @errors
60
+ end
61
+
62
+ def valid? object
63
+ errors = validate object
64
+ errors.keys.length == 0
65
+ end
66
+
67
+ # returns field, db_type, db_opts
68
+ def db_schema
69
+ out = @schema.rules.inject([]) do |total, (field, opts)|
70
+ # get db filed schema
71
+ type, opts = Typero::Type.load(opts[:type]).new(nil, opts).db_field
72
+
73
+ # add array true to field it ont defined in schema
74
+ schema_opts = @schema.rules[field]
75
+ opts[:array] = true if schema_opts[:array]
76
+
77
+ total << [type, field, opts]
78
+ end
79
+
80
+ out += @schema.db_rules
81
+
82
+ out
83
+ end
84
+
85
+ # iterate trough all the ruels via block interface
86
+ # schema.rules do |field, opts|
87
+ # schema.rules(:url) do |field, opts|
88
+ def rules filter = nil, &block
89
+ return @schema.rules unless filter
90
+ out = @schema.rules
91
+ out = out.select { |k, v| v[:type].to_s == filter.to_s || v[:array_type].to_s == filter.to_s } if filter
92
+ return out unless block_given?
93
+
94
+ out.each { |k, v| yield k, v }
95
+ end
96
+ alias :to_h :rules
97
+
98
+ private
99
+
100
+ # adds error to array or prefixes with field name
101
+ def add_error field, msg, opts
102
+ if @errors[field]
103
+ @errors[field] += ", %s" % msg
104
+ else
105
+ if msg && msg[0, 1].downcase == msg[0, 1]
106
+ field_name = opts[:name] || field.to_s.sub(/_id$/, "").capitalize
107
+ msg = "%s %s" % [field_name, msg]
108
+ end
109
+
110
+ @errors[field] = msg
111
+ end
112
+ end
113
+
114
+ def safe_type type
115
+ type.to_s.gsub(/[^\w]/, "").classify
116
+ end
117
+
118
+ def add_required_error field, value, opts
119
+ return unless opts[:required] && value.nil?
120
+ msg = opts[:required].class == TrueClass ? "is required" : opts[:required]
121
+ add_error field, msg, opts
122
+ end
123
+
124
+ def check_filed_value field, value, opts
125
+ klass = "Typero::%sType" % safe_type(opts[:type])
126
+ check = klass.constantize.new value, opts
127
+ check.get
128
+ rescue TypeError => e
129
+ add_error field, e.message, opts
130
+ end
131
+ end
132
+ end
@@ -1,6 +1,6 @@
1
1
  # Master class
2
2
 
3
- class Typero
3
+ module Typero
4
4
  class Type
5
5
  ERRORS = {
6
6
  en: {
@@ -9,14 +9,16 @@ class Typero
9
9
  max_length_error: 'max lenght is %s, you have %s',
10
10
  min_value_error: 'min is %s, got %s',
11
11
  max_value_error: 'max is %s, got %s',
12
- unallowed_characters_error: 'is having unallowed characters'
12
+ unallowed_characters_error: 'is having unallowed characters',
13
+ not_in_range: 'Value in not in allowed range (%s)'
13
14
  }
14
15
  }
15
16
 
16
- OPTS = {}
17
+ # default shared allowed opts keys
18
+ OPTS_KEYS = [:type, :required, :array, :max_count, :default, :name, :meta, :model]
19
+ OPTS = {}
17
20
 
18
- attr_accessor :opts
19
- attr_accessor :value
21
+ attr_reader :opts
20
22
 
21
23
  class << self
22
24
  def load name
@@ -36,6 +38,8 @@ class Typero
36
38
  end
37
39
 
38
40
  def opts key, desc
41
+ OPTS_KEYS.push key unless OPTS_KEYS.include?(key)
42
+
39
43
  OPTS[self] ||= {}
40
44
  OPTS[self][key] = desc
41
45
  end
@@ -43,20 +47,44 @@ class Typero
43
47
 
44
48
  ###
45
49
 
46
- def initialize value, opts={}
50
+ def initialize value, opts={}, &block
51
+ value = value.sub(/^\s+/, '').sub(/\s+$/, '') if value.is_a?(String)
52
+
47
53
  @value = value
48
54
  @opts = opts
55
+ @block = block
56
+ end
57
+
58
+ def value &block
59
+ if block_given?
60
+ @value = block.call @value
61
+ else
62
+ @value
63
+ end
49
64
  end
50
65
 
51
- # default validation for any type
52
- def validate
53
- true
66
+ def get
67
+ if value.nil?
68
+ opts[:default].nil? ? default : opts[:default]
69
+ else
70
+ set
71
+ error_for(:not_in_range, opts[:values].join(', ')) if opts[:values] && !opts[:values].include?(@value)
72
+ value
73
+ end
54
74
  end
55
75
 
56
76
  def default
57
77
  nil
58
78
  end
59
79
 
80
+ def db_field
81
+ out = db_schema
82
+ out[1] ||= {}
83
+ out[1][:default] ||= opts[:default] unless opts[:default].nil?
84
+ out[1][:null] = false if !opts[:array] && opts[:required]
85
+ out
86
+ end
87
+
60
88
  private
61
89
 
62
90
  # get error from option or the default one
@@ -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
+