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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/lib/typero.rb +2 -1
  4. data/lib/typero/exporter.rb +93 -0
  5. data/lib/typero/params.rb +24 -9
  6. data/lib/typero/schema.rb +132 -0
  7. data/lib/typero/type/type.rb +37 -9
  8. data/lib/typero/type/types/boolean_type.rb +26 -0
  9. data/lib/typero/type/types/currency_type.rb +21 -0
  10. data/lib/typero/type/types/date_type.rb +32 -0
  11. data/lib/typero/type/types/datetime_type.rb +14 -0
  12. data/lib/typero/type/types/email_type.rb +20 -0
  13. data/lib/typero/type/types/float_type.rb +24 -0
  14. data/lib/typero/type/types/hash_type.rb +25 -0
  15. data/lib/typero/type/types/{image.rb → image_type.rb} +4 -10
  16. data/lib/typero/type/types/integer_type.rb +16 -0
  17. data/lib/typero/type/types/label_type.rb +20 -0
  18. data/lib/typero/type/types/model_type.rb +21 -0
  19. data/lib/typero/type/types/{oib.rb → oib_type.rb} +7 -10
  20. data/lib/typero/type/types/point_type.rb +25 -0
  21. data/lib/typero/type/types/string_type.rb +20 -0
  22. data/lib/typero/type/types/{text.rb → text_type.rb} +3 -5
  23. data/lib/typero/type/types/url_type.rb +13 -0
  24. data/lib/typero/typero.rb +50 -137
  25. metadata +20 -18
  26. data/lib/typero/func.rb +0 -14
  27. data/lib/typero/type/types/boolean.rb +0 -16
  28. data/lib/typero/type/types/currency.rb +0 -19
  29. data/lib/typero/type/types/date.rb +0 -10
  30. data/lib/typero/type/types/datetime.rb +0 -10
  31. data/lib/typero/type/types/email.rb +0 -21
  32. data/lib/typero/type/types/float.rb +0 -21
  33. data/lib/typero/type/types/hash.rb +0 -22
  34. data/lib/typero/type/types/integer.rb +0 -21
  35. data/lib/typero/type/types/label.rb +0 -15
  36. data/lib/typero/type/types/point.rb +0 -29
  37. data/lib/typero/type/types/string.rb +0 -23
  38. 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,14 @@
1
+ require_relative 'date_type'
2
+
3
+ class Typero::DatetimeType < Typero::DateType
4
+ def set
5
+ value { |data| DateTime.parse(data) }
6
+
7
+ check_date_min_max
8
+ end
9
+
10
+ def db_schema
11
+ [:datetime]
12
+ end
13
+ end
14
+
@@ -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
- @value = 'https://%s' % @value unless @value.include?('://')
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 = @value.split('.').last.downcase
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 db_field
23
- opts = {}
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
- @value = check?(@value) ? @value.to_i : nil
6
- end
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 db_field
13
- opts = {}
14
- opts[:null] = false if @opts[:required]
15
- opts[:limit] = 11
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 'string'
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 db_field
8
- opts = {}
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
+
@@ -1,4 +1,4 @@
1
- # rules = Typero.new do
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.new do
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
- class Typero
19
+ module Typero
20
+ extend self
21
+
20
22
  VERSION = File.read File.expand_path "../../.version", File.dirname(__FILE__)
21
23
 
22
- class << self
23
- # check and coerce value
24
- # Typero.set(:label, 'Foo bar') -> "foo-bar"
25
- def set type, value, opts = {}, &block
26
- check = Typero::Type.load(type).new value, opts
27
- check.set
28
- check.validate
29
- check.value
30
- rescue TypeError => error
31
- if block
32
- block.call error
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
- # accepts dsl block to
43
- def initialize &block
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
- value = value
68
- .map { |el| check_filed_value field, el, opts }
69
- .map { |el| el.to_s == '' ? nil : el }
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
- # present empty string values as nil
89
- @object[field] = value.to_s.sub(/\s+/, '') == '' ? nil : value
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? 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
- # iterate trough all the ruels via block interface
123
- # schema.rules do |field, opts|
124
- # schema.rules(:url) do |field, opts|
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
- if msg && msg[0, 1].downcase == msg[0, 1]
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 safe_type type
151
- type.to_s.gsub(/[^\w]/, "").classify
64
+ def defined? name
65
+ Typero::Type.load name
66
+ true
67
+ rescue ArgumentError
68
+ false
152
69
  end
153
70
 
154
- def check_required field, value, opts
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
- def check_filed_value field, value, opts
161
- return unless value
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
- klass = "Typero::%sType" % safe_type(opts[:type])
164
- check = klass.constantize.new value, opts
165
- check.value = check.default if check.value.nil?
78
+ for el in args
79
+ for separator in ['_','/']
80
+ klass = [name, el].join(separator).classify
166
81
 
167
- unless check.value.nil?
168
- begin
169
- check.set
170
- check.validate
171
- check.value
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