typero 0.4.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2abbf2baf5e54e738a8144eba2aa35218d88faaf1f26032a041474df61ebc393
4
- data.tar.gz: 5d0eed00df648f251b43148b185dab5f05b07068ed225d831584e5a831d41b8a
3
+ metadata.gz: 8e78c338c182793ab2bffd1b4c5469d5285f57f55697402b6e80fb1881a3f7a8
4
+ data.tar.gz: 18a803b38ef8a24cb3756a43e5b5704082a08f4d805be57239cb054c28e6f132
5
5
  SHA512:
6
- metadata.gz: 78c673d3df0f2a968f44f9fab440c11831de686684d505ac3224ac937a470b0a773c1353a4c37eeb703fdf00fa13dab0a799f59e5ff5596ef75ab32860c0a9b4
7
- data.tar.gz: 735f2eea8437315bf330e9d31f7e3fd9b910b3da8b7c9a3792e9b06f4628789798a4aeef089064baac2db412a0fefbeb948908c50bccb4ee6fdfba9497c8f0d4
6
+ metadata.gz: 1c02d8cec42f465c5cf34b0b69c856cf193f1c5e289898af0aefdebf1d61ed91c95148a1fa9ca1c047a07810c99bd518946dbfe323e0e53c4502b66b08910c85
7
+ data.tar.gz: de185fca3ac80a4e7e9d6b87778872b3b352f89678f334e24b0047480d401cbd0abf95bb8940ba0e29d9d3f485ef62521b38335f1f6cc3a7d1f59194383f4a67
data/.version CHANGED
@@ -1 +1 @@
1
- 0.4.0
1
+ 0.9.0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sequel::Plugins::TyperoAttributes
4
+ module ClassMethods
5
+ def typero
6
+ Typero.new self
7
+ end
8
+ end
9
+
10
+ module InstanceMethods
11
+ # calling typero! on any object will validate all fields
12
+ def typero! field_name=nil
13
+ return unless Typero.defined?(self.class)
14
+
15
+ typero = self.class.typero
16
+
17
+ typero.validate(self) do |name, err|
18
+ errors.add(name, err) unless (errors.on(name) || []).include?(err)
19
+ end
20
+
21
+ # this are rules unique to database, so we check them here
22
+ typero.rules.each do |field, rule|
23
+ self[field] ||= {} if rule[:type] == 'hash'
24
+
25
+ # check uniqe fields
26
+ if rule[:unique]
27
+ id = self[:id] || 0
28
+ value = self[field]
29
+
30
+ # we only check if field is changed
31
+ if value.present? && column_changed?(field) && self.class.xwhere('LOWER(%s)=LOWER(?) and id<>?' % field, value, id).first
32
+ error = rule[:unique].class == TrueClass ? %[Value '"#{value}"' for #{field} allready exists] : rule[:unique]
33
+ errors.add(field, error) unless (errors.on(field) || []).include?(error)
34
+ end
35
+ end
36
+
37
+ # check protected fields
38
+ if rule[:protected] && self[:id]
39
+ if column_changed?(field)
40
+ error = rule[:protected].class == TrueClass ? "value once defined can't be overwritten." : rule[:protected]
41
+ errors.add(field, error) unless (errors.on(field) || []).include?(error)
42
+ end
43
+ end
44
+ end
45
+
46
+ # check single field if single field given
47
+ if field_name
48
+ raise ArgumentError.new 'Field :%s not found in %s' % [field_name, self] unless self[field_name]
49
+ return unless errors.on(field_name)
50
+
51
+ errors.on(field_name).join(', ')
52
+ end
53
+
54
+ true
55
+ end
56
+
57
+ def validate
58
+ typero!
59
+ super
60
+ end
61
+ end
62
+
63
+ module DatasetMethods
64
+
65
+ end
66
+ end
67
+
68
+ Sequel::Model.plugin :typero_attributes
69
+
@@ -1,172 +1,14 @@
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
- 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.set
47
- end
48
- end
49
-
50
- ###
51
-
52
- # accepts dsl block to
53
- def initialize hash={}, &block
54
- @rules = {}
55
- hash.each { |k, v| set(k, v) }
56
- instance_exec &block if block
57
- end
58
-
59
- # set :age, type: :integer -> integer :age
60
- # email :email
61
- # set :email, [:emails]
62
- # email [:emails]
63
- def method_missing name, *args, &block
64
- field = args.shift
65
-
66
- if field.class == Array
67
- field = field.first
68
- name = [name]
69
- end
70
-
71
- set field, type=name, *args
72
- end
73
-
74
- # coerce opts values
75
- def parse_option opts
76
- opts[:type] ||= 'string'
77
- opts[:req] = opts.delete(:required) unless opts[:required].nil?
78
-
79
- if opts[:type].is_a?(Array)
80
- opts[:array_type] = opts[:type][0] if opts[:type][0]
81
- opts[:type] = 'array'
82
- end
83
-
84
- opts[:type] = opts[:type].to_s.downcase
85
-
86
- opts[:required] = opts[:req] unless opts[:req].nil?
87
- opts[:unique] = opts[:uniq] unless opts[:uniq].nil?
88
- opts[:description] = opts[:desc] unless opts[:desc].nil?
89
-
90
- # allowed_names = [:req, :uniq, :protected, :type, :min, :max, :array_type, :default, :downcase, :desc, :label]
91
- # opts.keys.each do |key|
92
- # raise ArgumentError.new('%s is not allowed as typero option' % key) unless allowed_names.index(key)
93
- # end
94
-
95
- opts
96
- end
97
-
98
- # used in dsl to define value
99
- def set field, type=String, opts={}
100
- opts = type.is_a?(Hash) ? type : opts.merge(type: type)
101
-
102
- opts[:type] ||= :string
103
- klass = Typero::Type.load opts[:type]
104
- @rules[field] = parse_option opts
105
- end
106
-
107
- def safe_type type
108
- type.to_s.gsub(/[^\w]/,'').classify
109
- end
110
-
111
- # adds error to array or prefixes with field name
112
- def add_error field, msg
113
- if @errors[field]
114
- @errors[field] += ', %s' % msg
115
- else
116
- if msg[0,1].downcase == msg[0,1]
117
- field_name = field.to_s.sub(/_id$/,'').humanize
118
- msg = '%s %s' % [field_name, msg]
119
- end
120
-
121
- @errors[field] = msg
122
- end
123
- end
124
-
125
- # validates any instance object or object with hash variable interface
126
- # it also coarces values
127
- def validate instance
128
- @errors = {}
129
-
130
- @rules.each do |field, opts|
131
- # set value to default if value is blank and default given
132
- instance[field] = opts[:default] if opts[:default] && instance[field].blank?
133
-
134
- # get field value
135
- value = instance[field]
136
-
137
- if value.present?
138
- klass = 'Typero::%sType' % safe_type(opts[:type])
139
- check = klass.constantize.new value, opts
140
- check.value = check.default if check.value.nil?
141
-
142
- unless check.value.nil?
143
- begin
144
- check.set
145
- check.validate
146
- instance[field] = check.value
147
- rescue TypeError => e
148
- add_error field, e.message
149
- end
150
- end
151
- elsif opts[:required]
152
- msg = opts[:required].class == TrueClass ? 'is required' : opts[:req]
153
- add_error field, msg
154
- end
155
- end
156
-
157
- if @errors.keys.length > 0 && block_given?
158
- @errors.each { |k,v| yield(k, v) }
159
- end
160
-
161
- @errors
162
- end
163
-
164
- def valid? instance
165
- errors = validate instance
166
- errors.keys.length == 0
167
- end
1
+ # base libs
2
+ require_relative 'typero/typero'
3
+ require_relative 'typero/schema'
4
+ require_relative 'typero/params'
5
+ require_relative 'typero/exporter'
6
+ require_relative 'typero/type/type'
7
+
8
+ # checker types
9
+ Dir['%s/typero/type/types/*.rb' % __dir__].each do |file|
10
+ require file
168
11
  end
169
12
 
170
- require_relative 'typero/type'
171
-
172
- Dir['%s/typero/type/*.rb' % File.dirname(__FILE__)].each { |file| require file }
13
+ # load Sequel adapter is Sequel is available
14
+ require_relative './adapters/sequel' if defined?(Sequel)
@@ -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
+
@@ -0,0 +1,99 @@
1
+ # Base class for schema validation
2
+
3
+ module Typero
4
+ class Params
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 schema field options
16
+ def set field, *args
17
+ raise "Field name not given (Typero)" unless field
18
+
19
+ if args.first.is_a?(Hash)
20
+ opts = args.first || {}
21
+ else
22
+ opts = args[1] || {}
23
+ opts[:type] ||= args[0]
24
+ end
25
+
26
+ opts[:type] = :string if opts[:type].nil?
27
+
28
+ field = field.to_s
29
+
30
+ # name? - opional name
31
+ if field.include?('?')
32
+ field = field.sub('?', '')
33
+ opts[:required] = false
34
+ end
35
+
36
+ opts[:required] = true if opts[:required].nil?
37
+
38
+ # array that allows duplicates
39
+ if opts[:type].is_a?(Array)
40
+ opts[:type] = opts[:type].first
41
+ opts[:array] = true
42
+ end
43
+
44
+ # no duplicates array
45
+ if opts[:type].is_a?(Set)
46
+ opts[:type] = opts[:type].to_a.first
47
+ opts[:array] = true
48
+ end
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
+
64
+ opts[:type] ||= 'string'
65
+ opts[:type] = opts[:type].to_s.downcase.to_sym
66
+
67
+ opts[:description] = opts.delete(:desc) unless opts[:desc].nil?
68
+
69
+ # chek alloed params, all optional should go in meta
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
72
+
73
+ field = field.to_sym
74
+
75
+ db :add_index, field if opts.delete(:index)
76
+
77
+ # trigger error if type not found
78
+ Typero::Type.load opts[:type]
79
+
80
+ @rules[field] = opts
81
+ end
82
+
83
+ # pass values for db_schema only
84
+ # db :timestamps
85
+ # db :add_index, :code -> t.add_index :code
86
+ def db *args
87
+ @db_rules.push args
88
+ end
89
+
90
+ # set :age, type: :integer -> integer :age
91
+ # email :email
92
+ #
93
+ # set :emails, Array[:email]
94
+ # email Array[:emails]
95
+ def method_missing field, *args, &block
96
+ set field, *args
97
+ end
98
+ end
99
+ end