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
@@ -0,0 +1,32 @@
|
|
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
|
+
value { |data| DateTime.parse(data) }
|
7
|
+
value { |data| DateTime.new(data.year, data.month, data.day) }
|
8
|
+
|
9
|
+
check_date_min_max
|
10
|
+
end
|
11
|
+
|
12
|
+
def db_schema
|
13
|
+
[:date, {}]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def check_date_min_max
|
19
|
+
if min = opts[:min]
|
20
|
+
min = DateTime.parse(min)
|
21
|
+
error_for(:min_date, min) % min if min > value
|
22
|
+
end
|
23
|
+
|
24
|
+
if max = opts[:max]
|
25
|
+
max = DateTime.parse(max)
|
26
|
+
error_for(:max_date, max) % max if value > max
|
27
|
+
end
|
28
|
+
|
29
|
+
value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -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,25 @@
|
|
1
|
+
class Typero::HashType < Typero::Type
|
2
|
+
error :en, :not_hash_type_error, 'value is not hash type'
|
3
|
+
|
4
|
+
def set
|
5
|
+
error_for(:not_hash_type_error) unless value.is_a?(Hash)
|
6
|
+
|
7
|
+
if opts[:allow]
|
8
|
+
for key in value.keys
|
9
|
+
value.delete(key) unless opts[:allow].include?(key)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def default
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def db_schema
|
19
|
+
[:jsonb, {
|
20
|
+
null: false,
|
21
|
+
default: '{}'
|
22
|
+
}]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -7,22 +7,16 @@ class Typero::ImageType < Typero::Type
|
|
7
7
|
opts :strict, 'Force image to have known extension (%s)' % FORMATS.join(', ')
|
8
8
|
|
9
9
|
def set
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def validate
|
14
|
-
error_for(:image_not_starting_error) unless @value =~ /^https?:\/\/./
|
10
|
+
error_for(:image_not_starting_error) unless value =~ /^https?:\/\/./
|
15
11
|
|
16
12
|
if opts[:strict]
|
17
|
-
ext =
|
13
|
+
ext = value.split('.').last.downcase
|
18
14
|
error_for(:image_not_image_format) unless FORMATS.include?(ext)
|
19
15
|
end
|
20
16
|
end
|
21
17
|
|
22
|
-
def
|
23
|
-
|
24
|
-
opts[:null] = false if @opts[:required]
|
25
|
-
[:string, opts]
|
18
|
+
def db_schema
|
19
|
+
[:string]
|
26
20
|
end
|
27
21
|
end
|
28
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,21 @@
|
|
1
|
+
class Typero::ModelType < Typero::Type
|
2
|
+
def set
|
3
|
+
value(&:to_h)
|
4
|
+
|
5
|
+
errors = []
|
6
|
+
|
7
|
+
schema = Typero.schema(opts[:model])
|
8
|
+
schema.validate(value) do |field, error|
|
9
|
+
errors.push '%s (%s)' % [error, field]
|
10
|
+
end
|
11
|
+
|
12
|
+
raise TypeError.new errors.join(', ') if errors.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def db_schema
|
16
|
+
[:jsonb, {
|
17
|
+
null: false
|
18
|
+
}]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -2,18 +2,15 @@ class Typero::OibType < Typero::Type
|
|
2
2
|
error :en, :not_an_oib_error, 'not in an OIB format'
|
3
3
|
|
4
4
|
def set
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def validate
|
9
|
-
error_for(:not_an_oib_error) unless check?(@value)
|
5
|
+
value do |data|
|
6
|
+
check?(data) ? data.to_i : error_for(:not_an_oib_error)
|
7
|
+
end
|
10
8
|
end
|
11
9
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
[:string, opts]
|
10
|
+
def db_schema
|
11
|
+
[:string, {
|
12
|
+
limit: 11
|
13
|
+
}]
|
17
14
|
end
|
18
15
|
|
19
16
|
private
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# for postgres - select st_asewkt(lon_lat) || st_astext(lon_lat)
|
2
|
+
# point = @object.class.xselect("ST_AsText(#{field}) as #{field}").where(id: @object.id).first[field.to_sym]
|
3
|
+
|
4
|
+
class Typero::PointType < Typero::Type
|
5
|
+
def set
|
6
|
+
if value.include?('/@')
|
7
|
+
point = value.split('/@', 2).last.split(',')
|
8
|
+
value { [point[0], point[1]].join(',') }
|
9
|
+
end
|
10
|
+
|
11
|
+
if !value.include?('POINT') && value.include?(',')
|
12
|
+
point = value.sub(/,\s*/, ' ')
|
13
|
+
value { 'SRID=4326;POINT(%s)' % point }
|
14
|
+
end
|
15
|
+
|
16
|
+
if value && value.include?(',') && !value =~ /^SRID=4326;POINT\(/
|
17
|
+
error_for(:unallowed_characters_error)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def db_schema
|
22
|
+
[:geography, {}]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Typero::StringType < Typero::Type
|
2
|
+
opts :min, 'Minimun string length'
|
3
|
+
opts :max, 'Maximun string length'
|
4
|
+
opts :downcase, 'is the string in downcase?'
|
5
|
+
|
6
|
+
def set
|
7
|
+
value(&:to_s)
|
8
|
+
value(&:downcase) if opts[:downcase]
|
9
|
+
|
10
|
+
error_for(:min_length_error, opts[:min], value.length) if opts[:min] && value.length < opts[:min]
|
11
|
+
error_for(:max_length_error, opts[:max], value.length) if opts[:max] && value.length > opts[:max]
|
12
|
+
end
|
13
|
+
|
14
|
+
def db_schema
|
15
|
+
[:string, {
|
16
|
+
limit: @opts[:max] || 255
|
17
|
+
}]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -1,12 +1,10 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'string_type'
|
2
2
|
|
3
3
|
class Typero::TextType < Typero::StringType
|
4
4
|
opts :min, 'Minimun string length'
|
5
5
|
opts :max, 'Maximun string length'
|
6
6
|
|
7
|
-
def
|
8
|
-
|
9
|
-
opts[:null] = false if @opts[:required]
|
10
|
-
[:text, opts]
|
7
|
+
def db_schema
|
8
|
+
[:text, {}]
|
11
9
|
end
|
12
10
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Typero::UrlType < Typero::Type
|
2
|
+
error :en, :url_not_starting_error, 'URL is not starting with http or https'
|
3
|
+
|
4
|
+
def set
|
5
|
+
parts = value.split('://')
|
6
|
+
error_for(:url_not_starting_error) unless parts[1]
|
7
|
+
end
|
8
|
+
|
9
|
+
def db_schema
|
10
|
+
[:string, {}]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
data/lib/typero/typero.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# rules = Typero.
|
1
|
+
# rules = Typero.schema do
|
2
2
|
# set :name, String, req: true
|
3
3
|
# set :email, :email, req: true
|
4
4
|
# set :skills, [:email], min: 2
|
@@ -6,7 +6,7 @@
|
|
6
6
|
#
|
7
7
|
# or
|
8
8
|
#
|
9
|
-
# rules = Typero.
|
9
|
+
# rules = Typero.schema do
|
10
10
|
# string :name, req: true
|
11
11
|
# email :email, req: true
|
12
12
|
# email [:skills], min: 2
|
@@ -16,161 +16,74 @@
|
|
16
16
|
# rules.valid?
|
17
17
|
# rules.validate(@object) { |errors| ... }
|
18
18
|
|
19
|
-
|
19
|
+
module Typero
|
20
|
+
extend self
|
21
|
+
|
20
22
|
VERSION = File.read File.expand_path "../../.version", File.dirname(__FILE__)
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
false
|
34
|
-
else
|
35
|
-
raise error
|
36
|
-
end
|
24
|
+
# check and coerce value
|
25
|
+
# Typero.set(:label, 'Foo bar') -> "foo-bar"
|
26
|
+
def set type, value, opts = {}, &block
|
27
|
+
check = Typero::Type.load(type).new value, opts
|
28
|
+
check.get
|
29
|
+
rescue TypeError => error
|
30
|
+
if block
|
31
|
+
block.call error
|
32
|
+
false
|
33
|
+
else
|
34
|
+
raise error
|
37
35
|
end
|
38
36
|
end
|
39
37
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
raise "Params not defined" unless block_given?
|
45
|
-
@schema = Params.new &block
|
46
|
-
end
|
47
|
-
|
48
|
-
# validates any instance object with hash variable interface
|
49
|
-
# it also coarces values
|
50
|
-
def validate object
|
51
|
-
@object = object
|
52
|
-
@errors = {}
|
53
|
-
|
54
|
-
@schema.rules.each do |field, opts|
|
55
|
-
# set value to default if value is blank and default given
|
56
|
-
@object[field] = opts[:default] if opts[:default] && @object[field].blank?
|
57
|
-
|
58
|
-
# get field value
|
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])
|
65
|
-
end
|
38
|
+
# load type schema
|
39
|
+
def schema name=nil, &block
|
40
|
+
# :user -> 'User'
|
41
|
+
name = name.to_s.classify if name
|
66
42
|
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
43
|
+
if block_given?
|
44
|
+
Typero::Schema.new(&block).tap do |schema|
|
45
|
+
Typero::Schema::SCHEMAS[name] = schema if name
|
86
46
|
end
|
47
|
+
else
|
48
|
+
raise ArgumentErorr.new('Schema name not given') unless name
|
87
49
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if @errors.keys.length > 0 && block_given?
|
93
|
-
@errors.each { |k, v| yield(k, v) }
|
94
|
-
end
|
95
|
-
|
96
|
-
@errors
|
97
|
-
end
|
98
|
-
|
99
|
-
def valid? object
|
100
|
-
errors = validate object
|
101
|
-
errors.keys.length == 0
|
102
|
-
end
|
103
|
-
|
104
|
-
# returns field, db_type, db_opts
|
105
|
-
def db_schema
|
106
|
-
out = @schema.rules.inject([]) do |total, (field, opts)|
|
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
|
-
|
114
|
-
total << [type, field, opts]
|
50
|
+
schema = Typero::Schema::SCHEMAS[name]
|
51
|
+
schema ||= class_finder name, :schema
|
52
|
+
schema || raise('Typero schema "%s" not defined' % name)
|
115
53
|
end
|
116
|
-
|
117
|
-
out += @schema.db_rules
|
118
|
-
|
119
|
-
out
|
120
54
|
end
|
121
55
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
def rules(filter = nil, &block)
|
126
|
-
return @schema.rules unless filter
|
127
|
-
out = @schema.rules
|
128
|
-
out = out.select { |k, v| v[:type].to_s == filter.to_s || v[:array_type].to_s == filter.to_s } if filter
|
129
|
-
return out unless block_given?
|
130
|
-
|
131
|
-
out.each { |k, v| yield k, v }
|
132
|
-
end
|
133
|
-
|
134
|
-
private
|
135
|
-
|
136
|
-
# adds error to array or prefixes with field name
|
137
|
-
def add_error field, msg, opts
|
138
|
-
if @errors[field]
|
139
|
-
@errors[field] += ", %s" % msg
|
56
|
+
def export model, opts={}, &block
|
57
|
+
if block_given?
|
58
|
+
Exporter::EXPORTERS[model.to_s.classify] = block
|
140
59
|
else
|
141
|
-
|
142
|
-
field_name = opts[:name] || field.to_s.sub(/_id$/, "").humanize
|
143
|
-
msg = "%s %s" % [field_name, msg]
|
144
|
-
end
|
145
|
-
|
146
|
-
@errors[field] = msg
|
60
|
+
Exporter.new(model, opts).render
|
147
61
|
end
|
148
62
|
end
|
149
63
|
|
150
|
-
def
|
151
|
-
|
64
|
+
def defined? name
|
65
|
+
Typero::Type.load name
|
66
|
+
true
|
67
|
+
rescue ArgumentError
|
68
|
+
false
|
152
69
|
end
|
153
70
|
|
154
|
-
|
155
|
-
return if !opts[:required] || value
|
156
|
-
msg = opts[:required].class == TrueClass ? "is required" : opts[:required]
|
157
|
-
add_error field, msg, opts
|
158
|
-
end
|
71
|
+
private
|
159
72
|
|
160
|
-
|
161
|
-
|
73
|
+
# class_finder :user, :exporter, :representer
|
74
|
+
# find first UserExporter, User::Exporter, User::Representer, UserRepresenter
|
75
|
+
def class_finder *args
|
76
|
+
name = args.shift.to_s.classify
|
162
77
|
|
163
|
-
|
164
|
-
|
165
|
-
|
78
|
+
for el in args
|
79
|
+
for separator in ['_','/']
|
80
|
+
klass = [name, el].join(separator).classify
|
166
81
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
rescue TypeError => e
|
173
|
-
add_error field, e.message, opts
|
82
|
+
begin
|
83
|
+
return klass.constantize
|
84
|
+
rescue NameError => e
|
85
|
+
nil
|
86
|
+
end
|
174
87
|
end
|
175
88
|
end
|
176
89
|
end
|