typero 0.4.0 → 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 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