typero 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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