typero 0.5.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.version +1 -1
- data/lib/adapters/sequel.rb +3 -12
- data/lib/typero.rb +4 -188
- data/lib/typero/schema.rb +84 -0
- data/lib/typero/type.rb +39 -28
- data/lib/typero/type/array.rb +2 -2
- data/lib/typero/type/date.rb +4 -0
- data/lib/typero/type/datetime.rb +4 -0
- data/lib/typero/type/email.rb +1 -1
- data/lib/typero/type/float.rb +1 -1
- data/lib/typero/type/hash.rb +7 -1
- data/lib/typero/type/image.rb +28 -0
- data/lib/typero/type/integer.rb +2 -1
- data/lib/typero/type/label.rb +1 -2
- data/lib/typero/type/oib.rb +1 -1
- data/lib/typero/type/point.rb +8 -2
- data/lib/typero/type/string.rb +3 -2
- data/lib/typero/type/text.rb +3 -1
- data/lib/typero/type/url.rb +1 -1
- data/lib/typero/typero.rb +191 -0
- metadata +6 -4
- data/lib/typero/type/geography.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 448ffb9631eeb66159f13e45da0e28e600f91f3ed58684789740f2ad351a317b
|
4
|
+
data.tar.gz: 41feafdf4c139e47c02dc71c0c5dbdea154b6e75e0b8a746e1b7f41c6beb89d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28c28657d7a0c7c5e71b5cfac08eb314a857c9b4e01337666ab38f65c64aa99f1c07a56d3276f8446e0134bc28968b7b34fb13232769c97ed01c6847cdab657e
|
7
|
+
data.tar.gz: 65f56f37df4731adad738862fdb79ed540bdd22a3a85744f519370e4ae5d905bafa74ab4720d17d6916c068a4f9508225d20cde64a6735c29a9f9e978a9ef145
|
data/.version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/lib/adapters/sequel.rb
CHANGED
@@ -1,19 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'typero'
|
4
|
-
|
5
3
|
module Sequel::Plugins::TyperoAttributes
|
6
4
|
module ClassMethods
|
7
|
-
|
8
|
-
instance_variable_set :@typero, Typero.new(&block)
|
9
|
-
|
10
|
-
# attributes migrate: true do ...
|
11
|
-
AutoMigrate.typero to_s.tableize.to_sym if opts[:migrate] && Lux.config.migrate
|
12
|
-
end
|
13
|
-
|
14
|
-
def typero
|
15
|
-
instance_variable_get :@typero
|
16
|
-
end
|
5
|
+
attr_accessor :typero
|
17
6
|
end
|
18
7
|
|
19
8
|
module InstanceMethods
|
@@ -27,6 +16,8 @@ module Sequel::Plugins::TyperoAttributes
|
|
27
16
|
|
28
17
|
# this are rules unique to database, so we check them here
|
29
18
|
typero.rules.each do |field, rule|
|
19
|
+
self[field] ||= {} if rule[:type] == 'hash'
|
20
|
+
|
30
21
|
# check uniqe fields
|
31
22
|
if rule[:unique]
|
32
23
|
id = self[:id] || 0
|
data/lib/typero.rb
CHANGED
@@ -1,193 +1,9 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
4
|
-
# set :skills, [:email], min: 2
|
5
|
-
# end
|
6
|
-
#
|
7
|
-
# or
|
8
|
-
#
|
9
|
-
# rules = Typero.new do
|
10
|
-
# string :name, req: true
|
11
|
-
# email :email, req: true
|
12
|
-
# email [:skills], min: 2
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# errors = rules.validate @object
|
16
|
-
# rules.valid?
|
17
|
-
# rules.validate(@object) { |errors| ... }
|
18
|
-
|
19
|
-
class Typero
|
20
|
-
attr_reader :rules
|
21
|
-
|
22
|
-
VERSION = File.read File.expand_path '../.version', File.dirname(__FILE__)
|
23
|
-
|
24
|
-
class << self
|
25
|
-
# validate single value in type
|
26
|
-
def validate type, value, opts={}
|
27
|
-
field = type.to_s.tableize.singularize.to_sym
|
28
|
-
|
29
|
-
# we need to have pointer to hash, so value can be changed (coerced) if needed
|
30
|
-
h = { field => value }
|
31
|
-
|
32
|
-
rule = new
|
33
|
-
rule.set field, type, opts
|
34
|
-
|
35
|
-
if error = rule.validate(h)[field]
|
36
|
-
block_given? ? yield(error) : raise(TypeError.new(error))
|
37
|
-
end
|
38
|
-
|
39
|
-
h[field]
|
40
|
-
end
|
41
|
-
|
42
|
-
# check and coerce value
|
43
|
-
# Typero.set(:label, 'Foo bar') -> "foo-bar"
|
44
|
-
def set type, value, opts={}
|
45
|
-
check = Typero::Type.load(type).new value, opts
|
46
|
-
check.value
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
###
|
51
|
-
|
52
|
-
# accepts dsl block to
|
53
|
-
def initialize hash={}, &block
|
54
|
-
@rules = {}
|
55
|
-
@db = []
|
56
|
-
hash.each { |k, v| set(k, v) }
|
57
|
-
instance_exec &block if block
|
58
|
-
end
|
59
|
-
|
60
|
-
# validates any instance object or object with hash variable interface
|
61
|
-
# it also coarces values
|
62
|
-
def validate instance
|
63
|
-
@errors = {}
|
64
|
-
|
65
|
-
@rules.each do |field, opts|
|
66
|
-
# set value to default if value is blank and default given
|
67
|
-
instance[field] = opts[:default] if opts[:default] && instance[field].blank?
|
68
|
-
|
69
|
-
# get field value
|
70
|
-
value = instance[field]
|
71
|
-
|
72
|
-
if value.present?
|
73
|
-
klass = 'Typero::%sType' % safe_type(opts[:type])
|
74
|
-
check = klass.constantize.new value, opts
|
75
|
-
check.value = check.default if check.value.nil?
|
76
|
-
|
77
|
-
unless check.value.nil?
|
78
|
-
begin
|
79
|
-
check.set
|
80
|
-
check.validate
|
81
|
-
instance[field] = check.value
|
82
|
-
rescue TypeError => e
|
83
|
-
add_error field, e.message
|
84
|
-
end
|
85
|
-
end
|
86
|
-
elsif opts[:required]
|
87
|
-
msg = opts[:required].class == TrueClass ? 'is required' : opts[:req]
|
88
|
-
add_error field, msg
|
89
|
-
end
|
90
|
-
end
|
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? instance
|
100
|
-
errors = validate instance
|
101
|
-
errors.keys.length == 0
|
102
|
-
end
|
103
|
-
|
104
|
-
# returns field, db_type, db_opts
|
105
|
-
def db_schema
|
106
|
-
out = @rules.inject([]) do |total, (field, opts)|
|
107
|
-
type, opts = Typero::Type.load(opts[:type]).new(nil, opts).db_field
|
108
|
-
total << [type, field, opts]
|
109
|
-
end
|
110
|
-
|
111
|
-
out += @db if @db[0]
|
112
|
-
|
113
|
-
out
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
|
118
|
-
# adds error to array or prefixes with field name
|
119
|
-
def add_error field, msg
|
120
|
-
if @errors[field]
|
121
|
-
@errors[field] += ', %s' % msg
|
122
|
-
else
|
123
|
-
if msg[0,1].downcase == msg[0,1]
|
124
|
-
field_name = field.to_s.sub(/_id$/,'').humanize
|
125
|
-
msg = '%s %s' % [field_name, msg]
|
126
|
-
end
|
127
|
-
|
128
|
-
@errors[field] = msg
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# used in dsl to define value
|
133
|
-
def set field, type=String, opts={}
|
134
|
-
opts = type.is_a?(Hash) ? type : opts.merge(type: type)
|
135
|
-
opts[:type] ||= :string
|
136
|
-
opts[:req] = true if opts[:null].class == FalseClass
|
137
|
-
klass = Typero::Type.load opts[:type]
|
138
|
-
@rules[field] = parse_option opts
|
139
|
-
end
|
140
|
-
|
141
|
-
def safe_type type
|
142
|
-
type.to_s.gsub(/[^\w]/,'').classify
|
143
|
-
end
|
144
|
-
|
145
|
-
# coerce opts values
|
146
|
-
def parse_option opts
|
147
|
-
opts[:type] ||= 'string'
|
148
|
-
|
149
|
-
if opts[:type].is_a?(Array)
|
150
|
-
opts[:array_type] = opts[:type][0] if opts[:type][0]
|
151
|
-
opts[:type] = 'array'
|
152
|
-
end
|
153
|
-
|
154
|
-
opts[:type] = opts[:type].to_s.downcase
|
155
|
-
|
156
|
-
opts[:required] = opts.delete(:req) unless opts[:req].nil?
|
157
|
-
opts[:unique] = opts.delete(:uniq) unless opts[:uniq].nil?
|
158
|
-
opts[:description] = opts.delete(:desc) unless opts[:desc].nil?
|
159
|
-
|
160
|
-
opts
|
161
|
-
end
|
162
|
-
|
163
|
-
|
164
|
-
# pass values for db_schema only
|
165
|
-
# db :timestamps
|
166
|
-
# db :add_index, :code -> t.add_index :code
|
167
|
-
def db *args
|
168
|
-
@db.push args
|
169
|
-
end
|
170
|
-
|
171
|
-
# set :age, type: :integer -> integer :age
|
172
|
-
# email :email
|
173
|
-
# set :email, [:emails]
|
174
|
-
# email [:emails]
|
175
|
-
def method_missing name, *args, &block
|
176
|
-
field = args.shift
|
177
|
-
|
178
|
-
if field.class == Array
|
179
|
-
field = field.first
|
180
|
-
name = [name]
|
181
|
-
end
|
182
|
-
|
183
|
-
name = args.shift if name == :set
|
184
|
-
|
185
|
-
set field, type=name, *args
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
1
|
+
# base libs
|
2
|
+
require_relative 'typero/typero'
|
3
|
+
require_relative 'typero/schema'
|
189
4
|
require_relative 'typero/type'
|
190
5
|
|
6
|
+
# checker types
|
191
7
|
Dir['%s/typero/type/*.rb' % __dir__].each do |file|
|
192
8
|
require file
|
193
9
|
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Base class for schema validation
|
2
|
+
|
3
|
+
class Typero
|
4
|
+
class Schema
|
5
|
+
attr_reader :rules, :db_rules
|
6
|
+
|
7
|
+
def initialize &block
|
8
|
+
@db_rules = []
|
9
|
+
@rules = {}
|
10
|
+
instance_exec &block
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# used in dsl to define value
|
16
|
+
def set field, type=String, opts={}
|
17
|
+
raise "Field name not given (Typero)" unless field
|
18
|
+
|
19
|
+
field = field.to_sym
|
20
|
+
opts = type.is_a?(Hash) ? type : opts.merge(type: type)
|
21
|
+
opts[:type] ||= :string
|
22
|
+
opts[:required] = true if opts[:null].class == FalseClass
|
23
|
+
|
24
|
+
db :add_index, field if opts.delete(:index)
|
25
|
+
|
26
|
+
klass = Typero::Type.load opts[:type]
|
27
|
+
@rules[field] = parse_option opts
|
28
|
+
end
|
29
|
+
|
30
|
+
# coerce opts values
|
31
|
+
def parse_option opts
|
32
|
+
opts[:type] ||= 'string'
|
33
|
+
|
34
|
+
if opts[:type].is_a?(Array)
|
35
|
+
opts[:array_type] = opts[:type][0] if opts[:type][0]
|
36
|
+
opts[:type] = 'array'
|
37
|
+
end
|
38
|
+
|
39
|
+
opts[:type] = opts[:type].to_s.downcase
|
40
|
+
|
41
|
+
opts[:required] = opts.delete(:req) unless opts[:req].nil?
|
42
|
+
opts[:unique] = opts.delete(:uniq) unless opts[:uniq].nil?
|
43
|
+
opts[:description] = opts.delete(:desc) unless opts[:desc].nil?
|
44
|
+
|
45
|
+
opts
|
46
|
+
end
|
47
|
+
|
48
|
+
# pass values for db_schema only
|
49
|
+
# db :timestamps
|
50
|
+
# db :add_index, :code -> t.add_index :code
|
51
|
+
def db *args
|
52
|
+
@db_rules.push args
|
53
|
+
end
|
54
|
+
|
55
|
+
def link klass, opts={}
|
56
|
+
klass.is! Class
|
57
|
+
|
58
|
+
# TODO: Add can? update check before save
|
59
|
+
integer '%s_id' % klass.to_s.tableize.singularize, opts
|
60
|
+
end
|
61
|
+
|
62
|
+
def hash name
|
63
|
+
set name, :hash
|
64
|
+
end
|
65
|
+
|
66
|
+
# set :age, type: :integer -> integer :age
|
67
|
+
# email :email
|
68
|
+
# set :email, [:emails]
|
69
|
+
# email [:emails]
|
70
|
+
def method_missing name, *args, &block
|
71
|
+
field = args.shift
|
72
|
+
|
73
|
+
if field.class == Array
|
74
|
+
field = field.first
|
75
|
+
name = [name]
|
76
|
+
end
|
77
|
+
|
78
|
+
name = args.shift if name == :set
|
79
|
+
|
80
|
+
set field, type=name, *args
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
data/lib/typero/type.rb
CHANGED
@@ -1,31 +1,42 @@
|
|
1
|
-
class
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
1
|
+
# Master class
|
2
|
+
|
3
|
+
class Typero
|
4
|
+
class Type
|
5
|
+
attr_accessor :opts
|
6
|
+
attr_accessor :value
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def load name
|
10
|
+
klass = 'Typero::%sType' % name.to_s.gsub(/[^\w]/,'').classify
|
11
|
+
|
12
|
+
if const_defined? klass
|
13
|
+
klass.constantize
|
14
|
+
else
|
15
|
+
raise ArgumentError, 'Typero type "%s" is not defined (%s)' % [name, klass]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
###
|
21
|
+
|
22
|
+
def initialize value, opts={}
|
23
|
+
@value = value
|
24
|
+
@opts = opts
|
25
|
+
end
|
26
|
+
|
27
|
+
# default validation for any type
|
28
|
+
def validate
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
# get error from option or the default one
|
33
|
+
def error_for name
|
34
|
+
@opts[name] || send(name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def default
|
38
|
+
nil
|
39
|
+
end
|
29
40
|
end
|
30
41
|
end
|
31
42
|
|
data/lib/typero/type/array.rb
CHANGED
@@ -27,8 +27,8 @@ class Typero::ArrayType < Typero::Type
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def validate
|
30
|
-
raise TypeError, error_for(:
|
31
|
-
raise TypeError, error_for(:
|
30
|
+
raise TypeError, error_for(:min_error) % @opts[:min] if @opts[:min] && @value.length < @opts[:min]
|
31
|
+
raise TypeError, error_for(:max_error) % @opts[:max] if @opts[:max] && @value.length > @opts[:max]
|
32
32
|
true
|
33
33
|
end
|
34
34
|
|
data/lib/typero/type/date.rb
CHANGED
data/lib/typero/type/datetime.rb
CHANGED
data/lib/typero/type/email.rb
CHANGED
data/lib/typero/type/float.rb
CHANGED
data/lib/typero/type/hash.rb
CHANGED
@@ -3,6 +3,10 @@ class Typero::HashType < Typero::Type
|
|
3
3
|
{}
|
4
4
|
end
|
5
5
|
|
6
|
+
def set
|
7
|
+
@value = @value.to_h
|
8
|
+
end
|
9
|
+
|
6
10
|
def validate
|
7
11
|
raise TypeError, error_for(:not_hash_type_error) unless @value.is_a?(Hash)
|
8
12
|
end
|
@@ -12,7 +16,9 @@ class Typero::HashType < Typero::Type
|
|
12
16
|
end
|
13
17
|
|
14
18
|
def db_field
|
15
|
-
[:jsonb, {
|
19
|
+
[:jsonb, {
|
20
|
+
null: false
|
21
|
+
}]
|
16
22
|
end
|
17
23
|
end
|
18
24
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Typero::ImageType < Typero::Type
|
2
|
+
# FORMATS = %w[jpg jpeg gif png svg]
|
3
|
+
|
4
|
+
def set
|
5
|
+
@value = 'https://%s' % @value unless @value.include?('://')
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate
|
9
|
+
raise TypeError, error_for(:not_starting_error) unless @value =~ /^https?:\/\/./
|
10
|
+
# ext = @value.split('.').last.downcase
|
11
|
+
# raise TypeError, error_for(:not_image_format) unless FORMATS.include?(ext)
|
12
|
+
end
|
13
|
+
|
14
|
+
def not_starting_error
|
15
|
+
'URL is not starting with http'
|
16
|
+
end
|
17
|
+
|
18
|
+
# def not_image_format
|
19
|
+
# 'URL is not ending with %s' % FORMATS.join(', ')
|
20
|
+
# end
|
21
|
+
|
22
|
+
def db_field
|
23
|
+
opts = {}
|
24
|
+
opts[:null] = false if @opts[:required]
|
25
|
+
[:string, opts]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
data/lib/typero/type/integer.rb
CHANGED
data/lib/typero/type/label.rb
CHANGED
data/lib/typero/type/oib.rb
CHANGED
data/lib/typero/type/point.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
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
|
+
|
1
4
|
class Typero::PointType < Typero::Type
|
2
5
|
def set
|
6
|
+
ap @value
|
3
7
|
if @value.present?
|
4
8
|
if @value.include?('/@')
|
5
9
|
point = @value.split('/@', 2).last.split(',')
|
6
10
|
@value = [point[0], point[1]].join(',')
|
7
11
|
end
|
8
12
|
|
9
|
-
|
13
|
+
if !@value.include?('POINT') && @value.include?(',')
|
10
14
|
point = @value.sub(/,\s*/, ' ')
|
11
15
|
@value = 'SRID=4326;POINT(%s)' % point
|
12
16
|
end
|
@@ -14,7 +18,9 @@ class Typero::PointType < Typero::Type
|
|
14
18
|
end
|
15
19
|
|
16
20
|
def validate
|
17
|
-
|
21
|
+
if @value && @value.include?(',') && !@value =~ /^SRID=4326;POINT\(/
|
22
|
+
raise TypeError, error_for(:unallowed_characters_error)
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
def db_field
|
data/lib/typero/type/string.rb
CHANGED
@@ -21,8 +21,9 @@ class Typero::StringType < Typero::Type
|
|
21
21
|
|
22
22
|
def db_field
|
23
23
|
opts = {}
|
24
|
-
opts[:limit]
|
25
|
-
opts[:null]
|
24
|
+
opts[:limit] = @opts[:max] || 255
|
25
|
+
opts[:null] = false if @opts[:required]
|
26
|
+
opts[:default] = @opts[:default]
|
26
27
|
[:string, opts]
|
27
28
|
end
|
28
29
|
end
|
data/lib/typero/type/text.rb
CHANGED
data/lib/typero/type/url.rb
CHANGED
@@ -0,0 +1,191 @@
|
|
1
|
+
# rules = Typero.new do
|
2
|
+
# set :name, String, req: true
|
3
|
+
# set :email, :email, req: true
|
4
|
+
# set :skills, [:email], min: 2
|
5
|
+
# end
|
6
|
+
#
|
7
|
+
# or
|
8
|
+
#
|
9
|
+
# rules = Typero.new do
|
10
|
+
# string :name, req: true
|
11
|
+
# email :email, req: true
|
12
|
+
# email [:skills], min: 2
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# errors = rules.validate @object
|
16
|
+
# rules.valid?
|
17
|
+
# rules.validate(@object) { |errors| ... }
|
18
|
+
|
19
|
+
class Typero
|
20
|
+
SCHEMAS = {}
|
21
|
+
VERSION = File.read File.expand_path '../../.version', File.dirname(__FILE__)
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# validate single value in type
|
25
|
+
def validate type, value, opts={}
|
26
|
+
field = type.to_s.tableize.singularize.to_sym
|
27
|
+
|
28
|
+
# we need to have pointer to hash, so value can be changed (coerced) if needed
|
29
|
+
h = { field => value }
|
30
|
+
|
31
|
+
rule = new do
|
32
|
+
set field, type, opts
|
33
|
+
end
|
34
|
+
|
35
|
+
if error = rule.validate(h)[field]
|
36
|
+
block_given? ? yield(error) : raise(TypeError.new(error))
|
37
|
+
end
|
38
|
+
|
39
|
+
h[field]
|
40
|
+
end
|
41
|
+
|
42
|
+
# check and coerce value
|
43
|
+
# Typero.set(:label, 'Foo bar') -> "foo-bar"
|
44
|
+
def set type, value, opts={}
|
45
|
+
check = Typero::Type.load(type).new value, opts
|
46
|
+
check.value
|
47
|
+
end
|
48
|
+
|
49
|
+
# list loaded classes
|
50
|
+
def list type=nil
|
51
|
+
SCHEMAS
|
52
|
+
.select { |k, v| type ? v[1] == type : true; }
|
53
|
+
.keys
|
54
|
+
end
|
55
|
+
|
56
|
+
# def schema table_name, &block
|
57
|
+
# schema = Typero.new(&block)
|
58
|
+
|
59
|
+
# if Lux.config.migrate
|
60
|
+
# AutoMigrate.typero table_name, schema
|
61
|
+
# else
|
62
|
+
# klass = table_name.to_s.classify.constantize
|
63
|
+
# klass.typero = schema
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
end
|
67
|
+
|
68
|
+
###
|
69
|
+
|
70
|
+
# accepts dsl block to
|
71
|
+
def initialize name=nil, &block
|
72
|
+
type = :default
|
73
|
+
|
74
|
+
if name.is_a?(Hash)
|
75
|
+
type = name.keys.first
|
76
|
+
name = name.values.first
|
77
|
+
end
|
78
|
+
|
79
|
+
if block_given?
|
80
|
+
@schema = Schema.new &block
|
81
|
+
|
82
|
+
if name
|
83
|
+
SCHEMAS[name_fix(name)] = [@schema, type]
|
84
|
+
end
|
85
|
+
elsif name
|
86
|
+
schema_name = name_fix(name)
|
87
|
+
@schema = SCHEMAS[schema_name][0] || raise(ArgumentError.new('Schema nemed "%s" not found (%s)' % [schema_name, name]))
|
88
|
+
else
|
89
|
+
raise ArgumentError, 'No block or schema name given'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# validates any instance object or object with hash variable interface
|
94
|
+
# it also coarces values
|
95
|
+
def validate instance
|
96
|
+
@errors = {}
|
97
|
+
|
98
|
+
@schema.rules.each do |field, opts|
|
99
|
+
# set value to default if value is blank and default given
|
100
|
+
instance[field] = opts[:default] if opts[:default] && instance[field].blank?
|
101
|
+
|
102
|
+
# get field value
|
103
|
+
value = instance[field]
|
104
|
+
|
105
|
+
if value.present?
|
106
|
+
klass = 'Typero::%sType' % safe_type(opts[:type])
|
107
|
+
check = klass.constantize.new value, opts
|
108
|
+
check.value = check.default if check.value.nil?
|
109
|
+
|
110
|
+
unless check.value.nil?
|
111
|
+
begin
|
112
|
+
check.set
|
113
|
+
check.validate
|
114
|
+
instance[field] = check.value
|
115
|
+
rescue TypeError => e
|
116
|
+
add_error field, e.message
|
117
|
+
end
|
118
|
+
end
|
119
|
+
elsif opts[:required]
|
120
|
+
msg = opts[:required].class == TrueClass ? 'is required' : opts[:required]
|
121
|
+
add_error field, msg
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
if @errors.keys.length > 0 && block_given?
|
126
|
+
@errors.each { |k,v| yield(k, v) }
|
127
|
+
end
|
128
|
+
|
129
|
+
@errors
|
130
|
+
end
|
131
|
+
|
132
|
+
def valid? instance
|
133
|
+
errors = validate instance
|
134
|
+
errors.keys.length == 0
|
135
|
+
end
|
136
|
+
|
137
|
+
# returns field, db_type, db_opts
|
138
|
+
def db_schema
|
139
|
+
out = @schema.rules.inject([]) do |total, (field, opts)|
|
140
|
+
type, opts = Typero::Type.load(opts[:type]).new(nil, opts).db_field
|
141
|
+
total << [type, field, opts]
|
142
|
+
end
|
143
|
+
|
144
|
+
out += @schema.db_rules
|
145
|
+
|
146
|
+
out
|
147
|
+
end
|
148
|
+
|
149
|
+
# iterate trough all the ruels via block interface
|
150
|
+
# schema.rules do |field, opts|
|
151
|
+
# schema.rules(:url) do |field, opts|
|
152
|
+
def rules filter=nil, &block
|
153
|
+
return @schema.rules unless filter
|
154
|
+
out = @schema.rules
|
155
|
+
out = out.select { |k,v| v[:type].to_s == filter.to_s || v[:array_type].to_s == filter.to_s } if filter
|
156
|
+
return out unless block_given?
|
157
|
+
|
158
|
+
for k, v in out
|
159
|
+
yield k, v
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
# adds error to array or prefixes with field name
|
166
|
+
def add_error field, msg
|
167
|
+
if @errors[field]
|
168
|
+
@errors[field] += ', %s' % msg
|
169
|
+
else
|
170
|
+
if msg && msg[0,1].downcase == msg[0,1]
|
171
|
+
field_name = field.to_s.sub(/_id$/,'').humanize
|
172
|
+
msg = '%s %s' % [field_name, msg]
|
173
|
+
end
|
174
|
+
|
175
|
+
@errors[field] = msg
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def safe_type type
|
180
|
+
type.to_s.gsub(/[^\w]/,'').classify
|
181
|
+
end
|
182
|
+
|
183
|
+
def name_fix name
|
184
|
+
if name.is_a?(Symbol)
|
185
|
+
name.to_s.classify if name.is_a?(Symbol)
|
186
|
+
else
|
187
|
+
name.to_s
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typero
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dino Reic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fast_blank
|
@@ -34,6 +34,7 @@ files:
|
|
34
34
|
- "./.version"
|
35
35
|
- "./lib/adapters/sequel.rb"
|
36
36
|
- "./lib/typero.rb"
|
37
|
+
- "./lib/typero/schema.rb"
|
37
38
|
- "./lib/typero/type.rb"
|
38
39
|
- "./lib/typero/type/array.rb"
|
39
40
|
- "./lib/typero/type/boolean.rb"
|
@@ -42,8 +43,8 @@ files:
|
|
42
43
|
- "./lib/typero/type/datetime.rb"
|
43
44
|
- "./lib/typero/type/email.rb"
|
44
45
|
- "./lib/typero/type/float.rb"
|
45
|
-
- "./lib/typero/type/geography.rb"
|
46
46
|
- "./lib/typero/type/hash.rb"
|
47
|
+
- "./lib/typero/type/image.rb"
|
47
48
|
- "./lib/typero/type/integer.rb"
|
48
49
|
- "./lib/typero/type/label.rb"
|
49
50
|
- "./lib/typero/type/oib.rb"
|
@@ -51,6 +52,7 @@ files:
|
|
51
52
|
- "./lib/typero/type/string.rb"
|
52
53
|
- "./lib/typero/type/text.rb"
|
53
54
|
- "./lib/typero/type/url.rb"
|
55
|
+
- "./lib/typero/typero.rb"
|
54
56
|
homepage: https://github.com/dux/typero
|
55
57
|
licenses:
|
56
58
|
- MIT
|
@@ -70,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
72
|
- !ruby/object:Gem::Version
|
71
73
|
version: '0'
|
72
74
|
requirements: []
|
73
|
-
rubygems_version: 3.0.
|
75
|
+
rubygems_version: 3.0.6
|
74
76
|
signing_key:
|
75
77
|
specification_version: 4
|
76
78
|
summary: Ruby type system
|