typero 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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
+