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.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/lib/typero.rb +2 -1
- data/lib/typero/exporter.rb +93 -0
- data/lib/typero/params.rb +24 -9
- data/lib/typero/schema.rb +132 -0
- data/lib/typero/type/type.rb +37 -9
- data/lib/typero/type/types/boolean_type.rb +26 -0
- data/lib/typero/type/types/currency_type.rb +21 -0
- data/lib/typero/type/types/date_type.rb +32 -0
- data/lib/typero/type/types/datetime_type.rb +14 -0
- data/lib/typero/type/types/email_type.rb +20 -0
- data/lib/typero/type/types/float_type.rb +24 -0
- data/lib/typero/type/types/hash_type.rb +25 -0
- data/lib/typero/type/types/{image.rb → image_type.rb} +4 -10
- data/lib/typero/type/types/integer_type.rb +16 -0
- data/lib/typero/type/types/label_type.rb +20 -0
- data/lib/typero/type/types/model_type.rb +21 -0
- data/lib/typero/type/types/{oib.rb → oib_type.rb} +7 -10
- data/lib/typero/type/types/point_type.rb +25 -0
- data/lib/typero/type/types/string_type.rb +20 -0
- data/lib/typero/type/types/{text.rb → text_type.rb} +3 -5
- data/lib/typero/type/types/url_type.rb +13 -0
- data/lib/typero/typero.rb +50 -137
- metadata +20 -18
- data/lib/typero/func.rb +0 -14
- data/lib/typero/type/types/boolean.rb +0 -16
- data/lib/typero/type/types/currency.rb +0 -19
- data/lib/typero/type/types/date.rb +0 -10
- data/lib/typero/type/types/datetime.rb +0 -10
- data/lib/typero/type/types/email.rb +0 -21
- data/lib/typero/type/types/float.rb +0 -21
- data/lib/typero/type/types/hash.rb +0 -22
- data/lib/typero/type/types/integer.rb +0 -21
- data/lib/typero/type/types/label.rb +0 -15
- data/lib/typero/type/types/point.rb +0 -29
- data/lib/typero/type/types/string.rb +0 -23
- data/lib/typero/type/types/url.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e78c338c182793ab2bffd1b4c5469d5285f57f55697402b6e80fb1881a3f7a8
|
4
|
+
data.tar.gz: 18a803b38ef8a24cb3756a43e5b5704082a08f4d805be57239cb054c28e6f132
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1c02d8cec42f465c5cf34b0b69c856cf193f1c5e289898af0aefdebf1d61ed91c95148a1fa9ca1c047a07810c99bd518946dbfe323e0e53c4502b66b08910c85
|
7
|
+
data.tar.gz: de185fca3ac80a4e7e9d6b87778872b3b352f89678f334e24b0047480d401cbd0abf95bb8940ba0e29d9d3f485ef62521b38335f1f6cc3a7d1f59194383f4a67
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.9.0
|
data/lib/typero.rb
CHANGED
@@ -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
|
+
|
data/lib/typero/params.rb
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
# Base class for schema validation
|
2
2
|
|
3
|
-
|
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]
|
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 -
|
58
|
-
raise ArgumentError.new('Unallowed Type params found: %s, allowed: %s' % [result.join('
|
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
|
-
|
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
|
data/lib/typero/type/type.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Master class
|
2
2
|
|
3
|
-
|
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
|
-
|
17
|
+
# default shared allowed opts keys
|
18
|
+
OPTS_KEYS = [:type, :required, :array, :max_count, :default, :name, :meta, :model]
|
19
|
+
OPTS = {}
|
17
20
|
|
18
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
+
|