kharon 1.1.0 → 1.1.1

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 (52) hide show
  1. checksums.yaml +5 -13
  2. data/{.rspec → .rspec.old} +0 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +12 -4
  5. data/dist/kharon-1.1.0.gem +0 -0
  6. data/doc/Kharon.html +384 -6
  7. data/doc/Kharon/Errors.html +1 -1
  8. data/doc/Kharon/Errors/Validation.html +1 -1
  9. data/doc/Kharon/Handlers.html +1 -1
  10. data/doc/Kharon/Handlers/Exceptions.html +1 -1
  11. data/doc/Kharon/Handlers/Messages.html +1 -1
  12. data/doc/Kharon/Processor.html +425 -0
  13. data/doc/Kharon/Processors.html +144 -0
  14. data/doc/Kharon/Processors/ArrayProcessor.html +429 -0
  15. data/doc/Kharon/Processors/BooleanProcessor.html +306 -0
  16. data/doc/Kharon/Processors/BoxProcessor.html +440 -0
  17. data/doc/Kharon/Processors/DateProcessor.html +306 -0
  18. data/doc/Kharon/Processors/DatetimeProcessor.html +306 -0
  19. data/doc/Kharon/Processors/EmailProcessor.html +307 -0
  20. data/doc/Kharon/Processors/HashProcessor.html +431 -0
  21. data/doc/Kharon/Processors/IntegerProcessor.html +441 -0
  22. data/doc/Kharon/Processors/NumericProcessor.html +463 -0
  23. data/doc/Kharon/Processors/SSIDProcessor.html +307 -0
  24. data/doc/Kharon/Processors/TextProcessor.html +430 -0
  25. data/doc/Kharon/Validate.html +17 -5
  26. data/doc/Kharon/Validator.html +198 -1194
  27. data/doc/_index.html +159 -1
  28. data/doc/class_list.html +1 -1
  29. data/doc/file.README.html +15 -4
  30. data/doc/index.html +15 -4
  31. data/doc/method_list.html +135 -39
  32. data/doc/top-level-namespace.html +1 -1
  33. data/kharon.gemspec +4 -2
  34. data/lib/kharon.rb +32 -2
  35. data/lib/kharon/processor.rb +162 -0
  36. data/lib/kharon/processors.rb +6 -0
  37. data/lib/kharon/processors/array_processor.rb +30 -0
  38. data/lib/kharon/processors/boolean_processor.rb +31 -0
  39. data/lib/kharon/processors/box_processor.rb +63 -0
  40. data/lib/kharon/processors/date_processor.rb +21 -0
  41. data/lib/kharon/processors/datetime_processor.rb +21 -0
  42. data/lib/kharon/processors/email_processor.rb +21 -0
  43. data/lib/kharon/processors/hash_processor.rb +31 -0
  44. data/lib/kharon/processors/integer_processor.rb +55 -0
  45. data/lib/kharon/processors/numeric_processor.rb +66 -0
  46. data/lib/kharon/processors/ssid_processor.rb +21 -0
  47. data/lib/kharon/processors/text_processor.rb +30 -0
  48. data/lib/kharon/validate.rb +1 -1
  49. data/lib/kharon/validator.rb +26 -390
  50. data/lib/kharon/version.rb +1 -1
  51. data/spec/results.html +5277 -321
  52. metadata +69 -28
@@ -103,7 +103,7 @@
103
103
  </div>
104
104
 
105
105
  <div id="footer">
106
- Generated on Tue May 17 11:54:24 2016 by
106
+ Generated on Thu Jun 2 16:20:54 2016 by
107
107
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
108
  0.8.7.6 (ruby-1.9.3).
109
109
  </div>
@@ -2,6 +2,7 @@
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'kharon/version'
5
+ require 'date'
5
6
 
6
7
  Gem::Specification.new do |specification|
7
8
  specification.name = "kharon"
@@ -13,12 +14,12 @@ Gem::Specification.new do |specification|
13
14
  specification.email = "courtois.vincent@outlook.com"
14
15
  specification.files = `git ls-files`.split($/)
15
16
  specification.homepage = "https://rubygems.org/gems/kharon"
16
- specification.license = "Apache License 2"
17
+ specification.license = "Apache-2.0"
17
18
  specification.test_files = ["spec/spec_helper.rb", "spec/lib/kharon/validator_spec.rb"]
18
19
 
19
20
  specification.required_ruby_version = ">= 1.9.3"
20
21
 
21
- specification.add_runtime_dependency "bson", "~> 2.2", ">= 2.2.2"
22
+ specification.add_runtime_dependency "bson", ">= 4.2.1", "< 5.0.0"
22
23
 
23
24
  specification.add_development_dependency "yard", "~> 0.8"
24
25
  specification.add_development_dependency "redcarpet", "3.3.1"
@@ -26,4 +27,5 @@ Gem::Specification.new do |specification|
26
27
  specification.add_development_dependency "rake", "~> 10.0"
27
28
  specification.add_development_dependency "rack-test", "~> 0.6.2"
28
29
  specification.add_development_dependency "rspec", "~> 3.0", ">= 3.0.0"
30
+ specification.add_development_dependency "pry"
29
31
  end
@@ -2,10 +2,10 @@
2
2
  # @author Vincent Courtois <courtois.vincent@outlook.com>
3
3
  module Kharon
4
4
 
5
- [:Validator, :Version, :Errors, :Handlers, :Helpers, :Validate].each { |classname| autoload(classname, "kharon/#{classname.downcase}") }
6
-
7
5
  @@use_exceptions = true
8
6
 
7
+ @@processors = {}
8
+
9
9
  # Configuration method used to tell the module if it uses exceptions or stores error messages.
10
10
  # @param [Boolean] use TRUE if you want to use exceptions, FALSE else.
11
11
  def self.use_exceptions(use = true)
@@ -17,4 +17,34 @@ module Kharon
17
17
  def self.errors_handler
18
18
  @@use_exceptions ? Kharon::Handlers::Exceptions.instance : Kharon::Handlers::Messages.new
19
19
  end
20
+
21
+ # Adds a processor class to the list of processors.
22
+ # @param [Symbol] name the name of the stored processor to retrieve it after. It will be the method you call to process a data with that processor.
23
+ # @param [Class] classname the class object for the processor to be instanciated later.
24
+ def self.add_processor(name, classname)
25
+ @@processors[name] = classname if classname.ancestors.include? Kharon::Processor
26
+ end
27
+
28
+ # Removes a processor from the list of available processors.
29
+ # @param [Symbol] name the name (key) of the processor to delete.
30
+ def self.remove_processor(name)
31
+ @@processors.delete(name) if self.has_processor?(name)
32
+ end
33
+
34
+ # Getter for the list of processors.
35
+ # @return [Hash] the list of processors currently available.
36
+ def self.processors
37
+ @@processors
38
+ end
39
+
40
+ # Checks if a processor currently exists in the system.
41
+ # @param [String] name the name of the processor to check the existence.
42
+ # @return [Boolean] TRUE if the processor exists, FALSE if not.
43
+ def self.has_processor?(name)
44
+ @@processors.keys.include?(name)
45
+ end
46
+
47
+ [:Processor, :Validator, :Version, :Errors, :Handlers, :Helpers, :Validate].each { |classname| autoload(classname, "kharon/#{classname.downcase}") }
48
+
49
+ Dir[File.join(File.dirname(__FILE__), "kharon/processors/*.rb")].each { |filename| require filename}
20
50
  end
@@ -0,0 +1,162 @@
1
+ module Kharon
2
+ # A basic processor used to be subclassed by all different processors.
3
+ # It provides basic informations to process a hash key validation.
4
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
5
+ class Processor
6
+
7
+ Kharon.add_processor(:any, Kharon::Processor)
8
+
9
+ attr_accessor :validator
10
+
11
+ def initialize(validator)
12
+ @validator = validator
13
+ end
14
+
15
+ # Default processing method, simply storing the validated key in the filtered hash.
16
+ # @param [Object] key the key associated with the value currently filteres in the filtered datas.
17
+ # @param [Hash] options the options applied to the initial value.
18
+ def process(key, options = {})
19
+ store(key, ->(item){item}, options)
20
+ end
21
+
22
+ protected
23
+
24
+ # This method is executed before any call to a public method.
25
+ # @param [Object] key the key associated with the value currently filteres in the filtered datas.
26
+ # @param [Hash] options the options applied to the initial value.
27
+ def before_all(key, options)
28
+ required(key) if (options.has_key?(:required) and options[:required] == true)
29
+ if options.has_key?(:dependencies)
30
+ dependencies(key, options[:dependencies])
31
+ elsif options.has_key?(:dependency)
32
+ dependency(key, options[:dependency])
33
+ end
34
+ end
35
+
36
+ # Tries to store the associated key in the filtered key, transforming it with the given process.
37
+ # @param [Object] key the key associated with the value to store in the filtered datas.
38
+ # @param [Proc] process a process (lambda) to execute on the initial value. Must contain strictly one argument.
39
+ # @param [Hash] options the options applied to the initial value.
40
+ def store(key, process, options = {})
41
+ unless (options.has_key?(:extract) and options[:extract] == false)
42
+ if validator.datas.has_key?(key)
43
+ value = ((options.has_key?(:cast) and options[:cast] == false) ? validator.datas[key] : process.call(validator.datas[key]))
44
+ if(options.has_key?(:in))
45
+ in_array?(key, options[:in])
46
+ elsif(options.has_key?(:equals))
47
+ equals_to?(key, options[:equals])
48
+ elsif(options.has_key?(:equals_key))
49
+ equals_key?(key, options[:equals_key])
50
+ end
51
+ options.has_key?(:rename) ? (validator.filtered[options[:rename]] = value) : (validator.filtered[key] = value)
52
+ end
53
+ end
54
+ end
55
+
56
+ # Raises a type error with a generic message.
57
+ # @param [Object] key the key associated from the value triggering the error.
58
+ # @param [Class] type the expected type, not respected by the initial value.
59
+ # @raise [ArgumentError] the chosen type error.
60
+ def raise_type_error(key, type)
61
+ raise_error(type: "type", key: key, supposed: type, found: key.class)
62
+ end
63
+
64
+ # Raises an error giving a message to display.
65
+ # @param [String] message the the message to display with the exception.
66
+ # @raise ArgumentError an error to stop the execution when this method is invoked.
67
+ def raise_error(message)
68
+ validator.handler.report_error(message)
69
+ end
70
+
71
+ # Accessor for the errors, use only if the handler is a Kharon::Handlers::Messages.
72
+ # @return [Array] the errors encountered during validation or an empty array if the handler was a Kharon::Handlers::Exceptions.
73
+ def errors
74
+ validator.handler.respond_to?(:errors) ? validator.handler.errors : []
75
+ end
76
+
77
+ # Checks if a required key is present in provided datas.
78
+ # @param [Object] key the key of which check the presence.
79
+ # @raise [ArgumentError] if the key is not present.
80
+ def required(key)
81
+ raise_error(type: "required", key: key) unless validator.datas.has_key?(key)
82
+ end
83
+
84
+ # Syntaxic sugar used to chack several dependencies at once.
85
+ # @param [Object] key the key needing another key to properly work.
86
+ # @param [Object] dependencies the keys needed by another key for it to properly work.
87
+ # @raise [ArgumentError] if the required dependencies are not present.
88
+ # @see self#check_dependency the associated singular method.
89
+ def dependencies(key, dependencies)
90
+ dependencies.each { |dependency| dependency(key, dependency) }
91
+ end
92
+
93
+ # Checks if a dependency is respected. A dependency is a key openly needed by another key.
94
+ # @param [Object] key the key needing another key to properly work.
95
+ # @param [Object] dependency the key needed by another key for it to properly work.
96
+ # @raise [ArgumentError] if the required dependency is not present.
97
+ def dependency(key, dependency)
98
+ raise_error(type: "dependency", key: "key", needed: dependency) unless validator.datas.has_key?(dependency)
99
+ end
100
+
101
+ # Check if the value associated with the given key is typed with the given type, or with a type inheriting from it.
102
+ # @param [Object] key the key of the value to check the type from.
103
+ # @param [Class] type the type with which check the initial value.
104
+ # @return [Boolean] true if the initial value is from the right type, false if not.
105
+ def is_typed?(key, type)
106
+ return (!validator.datas.has_key?(key) or validator.datas[key].kind_of?(type))
107
+ end
108
+
109
+ # Checks if the value associated with the given key is included in the given array of values.
110
+ # @param [Object] key the key associated with the value to check.
111
+ # @param [Array] values the values in which the initial value should be contained.
112
+ # @raise [ArgumentError] if the initial value is not included in the given possible values.
113
+ def in_array?(key, values)
114
+ raise_error(type: "array.in", key: key, supposed: values, value: validator.datas[key]) unless (values.empty? or values.include?(validator.datas[key]))
115
+ end
116
+
117
+ # Checks if the value associated with the given key is equal to the given value.
118
+ # @param [Object] key the key associated with the value to check.
119
+ # @param [Object] value the values with which the initial value should be compared.
120
+ # @raise [ArgumentError] if the initial value is not equal to the given value.
121
+ def equals_to?(key, value)
122
+ raise_error(type: "equals", key: key, supposed: value, found: validator.datas[key]) unless validator.datas[key] == value
123
+ end
124
+
125
+ # Checks if the value associated with the given key is equal to the given key.
126
+ # @param [Object] key the key associated with the value to check.
127
+ # @param [Object] value the key to compare the currently validated key with.
128
+ # @raise [ArgumentError] if the initial value is not equal to the given value.
129
+ def equals_key?(key, value)
130
+ raise_error(type: "equals", key: key, supposed: validator.datas[value], found: validator.datas[key]) unless validator.datas[key] == validator.datas[value]
131
+ end
132
+
133
+ # Check if the value associated with the given key matches the given regular expression.
134
+ # @param [Object] key the key of the value to compare with the given regexp.
135
+ # @param [Regexp] regex the regex with which match the initial value.
136
+ # @return [Boolean] true if the initial value matches the regex, false if not.
137
+ def match?(key, regex)
138
+ return (!validator.datas.has_key?(key) or validator.datas[key].to_s.match(regex))
139
+ end
140
+
141
+ def match_regex?(key, value, regex)
142
+ regex = Regexp.new(regex) if regex.kind_of?(String)
143
+ raise_error(type: "regex", regex: regex, value: value, key: key) unless regex.match(value)
144
+ end
145
+
146
+ # Checks if the value associated with the given key has the given required values.
147
+ # @param [Object] key the key associated with the value to check.
148
+ # @param [Array] required_values the values that the initial Enumerable typed value should contain.
149
+ # @raise [ArgumentError] if the initial value has not each and every one of the given values.
150
+ def contains?(key, values, required_values)
151
+ raise_error(type: "contains.values", required: required_values, key: key) if (values & required_values) != required_values
152
+ end
153
+
154
+ # Checks if the value associated with the given key has the given required keys.
155
+ # @param [Object] key the key associated with the value to check.
156
+ # @param [Array] required_keys the keys that the initial Hash typed value should contain.
157
+ # @raise [ArgumentError] if the initial value has not each and every one of the given keys.
158
+ def has_keys?(key, required_keys)
159
+ raise_error(type: "contains.keys", required: required_keys, key: key) if (validator.datas[key].keys & required_keys) != required_keys
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,6 @@
1
+ module Kharon
2
+ # Module containing all the processors used to process and validate datas.
3
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
4
+ module Processors
5
+ end
6
+ end
@@ -0,0 +1,30 @@
1
+ module Kharon
2
+ module Processors
3
+
4
+ # Processor to validate arrays. It has the :contains option plus the default ones.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class ArrayProcessor < Kharon::Processor
7
+
8
+ Kharon.add_processor(:array, Kharon::Processors::ArrayProcessor)
9
+
10
+ # Checks if the given key is a datetime or not.
11
+ # @param [Object] key the key about which verify the type.
12
+ # @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
13
+ # @example Validates a key so it has to be a datetime, and depends on two other keys.
14
+ # @validator.datetime(:a_datetime, dependencies: [:another_key, :a_third_key])
15
+ def process(key, options = {})
16
+ before_all(key, options)
17
+ is_typed?(key, Array) ? store(key, ->(item){item.to_a}, options) : raise_type_error(key, "Array")
18
+ end
19
+
20
+ # Stores an array after verifying that it contains the values given in the contains? option.
21
+ # @param [Object] key the key associated with the value to store in the filtered datas.
22
+ # @param [Proc] process a process (lambda) to execute on the initial value. Must contain strictly one argument.
23
+ # @param [Hash] options the options applied to the initial value.
24
+ def store(key, process, options)
25
+ contains?(key, validator.datas[key], options[:contains]) if(options.has_key?(:contains))
26
+ super(key, process, options)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module Kharon
2
+ module Processors
3
+
4
+ # Processor to validate booleans. It only has the default options.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class BooleanProcessor < Kharon::Processor
7
+
8
+ Kharon.add_processor(:boolean, Kharon::Processors::BooleanProcessor)
9
+
10
+ # Checks if the given key is a boolean or not.
11
+ # @param [Object] key the key about which verify the type.
12
+ # @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
13
+ # @example Validates a key so it has to be a boolean.
14
+ # @validator.boolean(:a_boolean)
15
+ def process(key, options = {})
16
+ before_all(key, options)
17
+ match?(key, /(true)|(false)/) ? store(key, ->(item){to_boolean(item)}, options) : raise_type_error(key, "Numeric")
18
+ end
19
+
20
+ private
21
+
22
+ # Transforms a given value in a boolean.
23
+ # @param [Object] value the value to transform into a boolean.
24
+ # @return [Boolean] true if the value was true, 1 or yes, false if not.
25
+ def to_boolean(value)
26
+ ["true", "1", "yes"].include?(value.to_s) ? true : false
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,63 @@
1
+ module Kharon
2
+ module Processors
3
+
4
+ # Processor to validate boxes. It has the :at_most and :at_least with the default ones.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class BoxProcessor < Kharon::Processor
7
+
8
+ Kharon.add_processor(:box, Kharon::Processors::BoxProcessor)
9
+
10
+ # Checks if the given key is a box (geofences) or not. A box is composed of four numbers (positive or negative, decimal or not) separed by commas.
11
+ # @param [Object] key the key about which verify the type.
12
+ # @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
13
+ # @example Validates a key so it has to be a box.
14
+ # @validator.box(:a_box)
15
+ def process(key, options = {})
16
+ before_all(key, options)
17
+ match?(key, /^(?:[+-]?\d{1,3}(?:\.\d{1,7})?,?){4}$/) ? store(key, nil, options) : raise_type_error(key, "Box")
18
+ end
19
+
20
+ # Tries to store the associated key in the filtered key, transforming it with the given process.
21
+ # @param [Object] key the key associated with the value to store in the filtered datas.
22
+ # @param [Proc] process a process (lambda) to execute on the initial value. Must contain strictly one argument.
23
+ # @param [Hash] options the options applied to the initial value.
24
+ def store(key, process, options)
25
+ if(options.has_key?(:at_least))
26
+ box_contains?(key, validator.datas[key], options[:at_least])
27
+ end
28
+ if(options.has_key?(:at_most))
29
+ box_contains?(key, options[:at_most], validator.datas[key])
30
+ end
31
+ super(key, ->(item){parse_box(key, validator.datas[key])}, options)
32
+ end
33
+
34
+ private
35
+
36
+ # Verify if a box contains another box.
37
+ # @param [Object] container any object that can be treated as a box, container of the other box
38
+ # @param [Object] contained any object that can be treated as a box, contained in the other box
39
+ # @return [Boolean] TRUE if the box is contained in the other one, FALSE if not.
40
+ def box_contains?(key, container, contained)
41
+ container = parse_box(key, container)
42
+ contained = parse_box(key, contained)
43
+ result = ((container[0][0] <= contained[0][0]) and (container[0][1] <= container[0][1]) and (container[1][0] >= container[1][0]) and (container[1][1] >= container[1][1]))
44
+ raise_error(type: "box.containment", contained: contained, container: container, key: key) unless result
45
+ end
46
+
47
+ # Parses a box given as a string of four numbers separated by commas.
48
+ # @param [String] box the string representing the box.
49
+ # @return [Array] an array of size 2, containing two arrays of size 2 (the first being the coordinates of the top-left corner, the second the ones of the bottom-right corner)
50
+ def parse_box(key, box)
51
+ if box.kind_of?(String)
52
+ begin
53
+ raw_box = box.split(",").map(&:to_f)
54
+ box = [[raw_box[0], raw_box[1]], [raw_box[2], raw_box[3]]]
55
+ rescue
56
+ raise_error(type: "box.format", key: "key", value: box)
57
+ end
58
+ end
59
+ return box
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ module Kharon
2
+ module Processors
3
+
4
+ # Processor to validate dates. It only has the default options.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class DateProcessor < Kharon::Processor
7
+
8
+ Kharon.add_processor(:date, Kharon::Processors::DateProcessor)
9
+
10
+ # Checks if the given key is a datetime or not.
11
+ # @param [Object] key the key about which verify the type.
12
+ # @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
13
+ # @example Validates a key so it has to be a datetime, and depends on two other keys.
14
+ # @validator.datetime(:a_datetime, dependencies: [:another_key, :a_third_key])
15
+ def process(key, options = {})
16
+ before_all(key, options)
17
+ begin; store(key, ->(item){Date.parse(item.to_s)}, options); rescue; raise_type_error(key, "Date"); end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Kharon
2
+ module Processors
3
+
4
+ # Processor to validate datetimes. It only has the default options.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class DatetimeProcessor < Kharon::Processor
7
+
8
+ Kharon.add_processor(:datetime, Kharon::Processors::DatetimeProcessor)
9
+
10
+ # Checks if the given key is a datetime or not.
11
+ # @param [Object] key the key about which verify the type.
12
+ # @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
13
+ # @example Validates a key so it has to be a datetime, and depends on two other keys.
14
+ # @validator.datetime(:a_datetime, dependencies: [:another_key, :a_third_key])
15
+ def process(key, options = {})
16
+ before_all(key, options)
17
+ begin; store(key, ->(item){DateTime.parse(item.to_s)} , options); rescue; raise_type_error(key, "DateTime"); end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Kharon
2
+ module Processors
3
+
4
+ # Processor to validate emails. It only has the default options.
5
+ # @author Vincent Courtois <courtois.vincent@outlook.com>
6
+ class EmailProcessor < Kharon::Processor
7
+
8
+ Kharon.add_processor(:email, Kharon::Processors::EmailProcessor)
9
+
10
+ # Checks if the given key is a not-empty string or not.
11
+ # @param [Object] key the key about which verify the type.
12
+ # @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
13
+ # @example Validates a key so it has to be a string, and seems like and email address (not sure of the regular expression though).
14
+ # @validator.text(:an_email, regex: "[a-zA-Z]+@[a-zA-Z]+\.[a-zA-Z]{2-4}")
15
+ def process(key, options = {})
16
+ before_all(key, options)
17
+ match?(key, /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/) ? store(key, ->(item){item}, options) : raise_type_error(key, "Email")
18
+ end
19
+ end
20
+ end
21
+ end