with_validations 0.5.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.
- data/ChangeLog.md +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +114 -0
- data/Rakefile +22 -0
- data/lib/with_validations.rb +133 -0
- data/lib/with_validations/core_ext/hash.rb +22 -0
- data/lib/with_validations/version.rb +3 -0
- metadata +68 -0
data/ChangeLog.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Stefan Rohlfing
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# WithValidations
|
2
|
+
|
3
|
+
### Easy validation of option keys and their values.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* Validate If all option values are valid.
|
8
|
+
* Automatically assign default values to missing but required keys.
|
9
|
+
* (Optionally) check for unsupported keys in the options hash.
|
10
|
+
* Assign the validated values to one or more variables.
|
11
|
+
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
``` bash
|
16
|
+
$ gem install with_validations
|
17
|
+
```
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
__Note__: A class including this module also need to stick to the following conventions:
|
22
|
+
|
23
|
+
* Provide a hash of the type `{ option_key => [default_value, validation_proc], ...}`
|
24
|
+
assigned to a constant named `OPTIONS`.
|
25
|
+
* Name the option hash of a method `options`.
|
26
|
+
|
27
|
+
### Sample Setup
|
28
|
+
|
29
|
+
````ruby
|
30
|
+
require 'with_validations'
|
31
|
+
|
32
|
+
class TestClass
|
33
|
+
include WithValidations
|
34
|
+
|
35
|
+
# option_key => [default_value, validation_proc]
|
36
|
+
OPTIONS = {:compact => [false, lambda {|value| is_boolean?(value) }],
|
37
|
+
:with_pinyin => [true, lambda {|value| is_boolean?(value) }],
|
38
|
+
:thread_count => [8, lambda {|value| value.kind_of?(Integer) }]}
|
39
|
+
|
40
|
+
# Instance method
|
41
|
+
def test_method_1(options={}) # options hash has to be named 'options'
|
42
|
+
@thread_count = validate { :thread_count }
|
43
|
+
|
44
|
+
#...
|
45
|
+
end
|
46
|
+
|
47
|
+
# Singleton method
|
48
|
+
def self.test_method_2(options={})
|
49
|
+
@compact, @with_pinyin = validate { [:compact, :with_pinyin] }
|
50
|
+
|
51
|
+
# ...
|
52
|
+
end
|
53
|
+
|
54
|
+
# Optional parameter set to true.
|
55
|
+
def self.test_method_3(options={})
|
56
|
+
@compact, @with_pinyin = validate(true) { [:compact, :with_pinyin] }
|
57
|
+
|
58
|
+
# ...
|
59
|
+
end
|
60
|
+
|
61
|
+
#...
|
62
|
+
end
|
63
|
+
````
|
64
|
+
|
65
|
+
#### Use Cases
|
66
|
+
|
67
|
+
```` ruby
|
68
|
+
obj = TestClass.new
|
69
|
+
obj.test_method_1(:thread_count => 10)
|
70
|
+
# @thread_count set to 10
|
71
|
+
|
72
|
+
# No options hash passed, used default value
|
73
|
+
my_class.test_method_1
|
74
|
+
# @thread_count set to 8
|
75
|
+
|
76
|
+
# Passing any invalid value
|
77
|
+
my_class.test_method_1(:thread_count => 'invalid')
|
78
|
+
# ArgumentError: Value 'invalid' is not a valid value for key 'thread_count'
|
79
|
+
|
80
|
+
|
81
|
+
TestClass.test_method_2(:compact => true, :with_pinyin => false)
|
82
|
+
# @compact set to true, @with_pinyin set to false
|
83
|
+
|
84
|
+
TestClass.test_method_2(:compact => true)
|
85
|
+
# No options for key :with_pinyin provided: Use the default value
|
86
|
+
# @compact is set to true, @with_pinyin is set to true
|
87
|
+
|
88
|
+
# By default, any keys from the options hash that are not part of the block are ignored.
|
89
|
+
TestClass.test_method_2(:compact => true,
|
90
|
+
:with_pinyin => false,
|
91
|
+
:thread_count => 10, # Key in OPTIONS, but not part of the block.
|
92
|
+
:not_listed => 'some value') # Not part of the block
|
93
|
+
# @compact set to true, @with_pinyin set to false
|
94
|
+
|
95
|
+
# Optional argument set to true: Any key from the options hash that is not part of the keys
|
96
|
+
# passed to the block will throw an exception.
|
97
|
+
TestClass.test_method_3(:compact => true,
|
98
|
+
:with_pinyin => false,
|
99
|
+
:thread_count => 10, # Key in OPTIONS, but not part of the block.
|
100
|
+
:not_listed => 'some value') # Not part of the block
|
101
|
+
# Exception: The following keys are not supported: not_listed
|
102
|
+
````
|
103
|
+
|
104
|
+
## Documentation
|
105
|
+
|
106
|
+
### Main method
|
107
|
+
|
108
|
+
* [validate](http://rubydoc.info/github/bytesource/with_validations/master/WithValidations:validate)
|
109
|
+
### Optional methods
|
110
|
+
|
111
|
+
* [extract_options](http://rubydoc.info/github/bytesource/with_validations/master/WithValidations:extract_options)
|
112
|
+
* [is_boolean?](http://rubydoc.info/github/bytesource/with_validations/master/WithValidations:is_boolean%3F)
|
113
|
+
|
114
|
+
__Note__: All methods are available as both instance and singleton methods.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# require 'spec/rake/spectask' # depreciated
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
# require 'rake/gempackagetask' # depreciated
|
4
|
+
require 'rubygems/package_task'
|
5
|
+
require 'rdoc/task'
|
6
|
+
|
7
|
+
# Build gem: rake gem
|
8
|
+
# Push gem: rake push
|
9
|
+
|
10
|
+
task :default => [ :spec, :gem ]
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new { :spec }
|
13
|
+
|
14
|
+
gem_spec = eval(File.read('with_validations.gemspec'))
|
15
|
+
|
16
|
+
Gem::PackageTask.new( gem_spec ) do |t|
|
17
|
+
t.need_zip = true
|
18
|
+
end
|
19
|
+
|
20
|
+
task :push => :gem do |t|
|
21
|
+
sh "gem push -v pkg/#{gem_spec.name}-#{gem_spec.version}.gem"
|
22
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "with_validations/version"
|
3
|
+
require 'with_validations/core_ext/hash'
|
4
|
+
|
5
|
+
module WithValidations
|
6
|
+
|
7
|
+
# In order to be able to validate options in both
|
8
|
+
# instance and singleton methods,
|
9
|
+
# the following method makes sure that all module methods are available
|
10
|
+
# as both instance and singleton methods.
|
11
|
+
def self.included(klass)
|
12
|
+
klass.extend(self)
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Validates the options passed in the block.
|
17
|
+
# Options can either be a single option key or an array of options keys.
|
18
|
+
# Option keys and their values are validated based on the information given in a
|
19
|
+
# mandatory constant called `OPTIONS`. Keys from a methods `options` has that are not listed in `OPTIONS` are ignored.
|
20
|
+
# @note A class that includes this module is required to:
|
21
|
+
#
|
22
|
+
# * provide a constant named `OPTIONS` with a hash of the following type:
|
23
|
+
# `{ option_key => [default_value, validation_proc], ...}`.
|
24
|
+
# * name the optional option hash of a method `options`.
|
25
|
+
# @overload validate(strict)
|
26
|
+
# @param [Boolean] strict Whether or not raise an exception if the options has contains any unsupported keys.
|
27
|
+
# Default: `false`
|
28
|
+
# @overload validate()
|
29
|
+
# @return [Array, Object] If more than one keys are evaluated, return an array of the key values, otherwise returns a single value.
|
30
|
+
# @example
|
31
|
+
# require 'with_validations'
|
32
|
+
#
|
33
|
+
# class TestClass
|
34
|
+
# include WithValidations
|
35
|
+
#
|
36
|
+
# # option_key => [default_value, validation_proc]
|
37
|
+
# OPTIONS = {:compact => [false, lambda {|value| is_boolean?(value) }],
|
38
|
+
# :with_pinyin => [true, lambda {|value| is_boolean?(value) }],
|
39
|
+
# :thread_count => [8, lambda {|value| value.kind_of?(Integer) }]}
|
40
|
+
#
|
41
|
+
# # Instance method
|
42
|
+
# def test_method_1(options={})
|
43
|
+
# @thread_count = validate { :thread_count }
|
44
|
+
# # ...
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# # Singleton method
|
48
|
+
# def self.test_method_2(options={})
|
49
|
+
# @compact, @with_pinyin = validate { [:compact, :with_pinyin] }
|
50
|
+
# # ...
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # Optional parameter set to true.
|
54
|
+
# def self.test_method_3(options={})
|
55
|
+
# @compact, @with_pinyin = validate(true) { [:compact, :with_pinyin] }
|
56
|
+
# #...
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# #...
|
60
|
+
# end
|
61
|
+
def validate(strict = false, &block)
|
62
|
+
raise ArgumentError, "No block given" unless block
|
63
|
+
|
64
|
+
argument = block.call
|
65
|
+
# Raise exception if the block is empty.
|
66
|
+
raise ArgumentError, "Block is empty" if argument.nil?
|
67
|
+
|
68
|
+
keys = Array(argument) # Wrap single key as array. If passed an array, just return the array.
|
69
|
+
|
70
|
+
constant = eval("OPTIONS", block.binding)
|
71
|
+
options = eval("options", block.binding) # Alternative: constant = block.binding.eval("OPTIONS")
|
72
|
+
|
73
|
+
# Handling optional argument 'strict'
|
74
|
+
ops = options.dup
|
75
|
+
ops.delete_keys!(*keys)
|
76
|
+
# If 'strict' is set to 'true', any key from the options key that is not part of the keys
|
77
|
+
# passed to the block will throw an exception.
|
78
|
+
raise Exception, "The following keys are not supported: #{ops.keys.join(', ')}" if ops.size > 0 && strict
|
79
|
+
|
80
|
+
values = keys.map do |key|
|
81
|
+
# Raise exception if 'key' from the block is NOT a key in the OPTIONS constant.
|
82
|
+
raise ArgumentError, "Key '#{key}' passed to the block not found in OPTIONS" unless constant.keys.include?(key)
|
83
|
+
|
84
|
+
if options.has_key?(key) # Supported key in block found in options => extract its value from options.
|
85
|
+
value = options[key]
|
86
|
+
# Check if 'value' is a valid value.
|
87
|
+
validation = constant[key][1]
|
88
|
+
if validation.call(value) # Validation passed => return value from options
|
89
|
+
value
|
90
|
+
else # Validation failed => raise exception
|
91
|
+
raise ArgumentError, "'#{value}' (#{value.class}) is not a valid value for key '#{key}'."
|
92
|
+
end
|
93
|
+
else # Supported key in block not found in options => return its default value.
|
94
|
+
default_value = constant[key][0]
|
95
|
+
default_value
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
values.size > 1 ? values : values [0]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns a new hash from `options` based on the keys in `*keys`.
|
103
|
+
# in `arr`. Keys in `arr` not found in `options` are ignored.
|
104
|
+
# *Use case*: When a method's options hash contains options for another method
|
105
|
+
# that throws an exeption if the options hash contains keys not handled internally (Example: CSV library)
|
106
|
+
# the options special to that method need to be extracted before passed as an argument.
|
107
|
+
# @return [Hash]
|
108
|
+
# @example
|
109
|
+
# def sample_method(text, options={})
|
110
|
+
# @compact, @with_pinyin = validate { [:compact, :with_pinyin] }
|
111
|
+
#
|
112
|
+
# csv_options = extract_options(CSV::DEFAULT_OPTIONS.keys, options)
|
113
|
+
# csv = CSV.parse(text, csv_options)
|
114
|
+
# #...
|
115
|
+
# end
|
116
|
+
def extract_options(arr, options)
|
117
|
+
options.slice(*arr)
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
# Some useful validation methods
|
123
|
+
# =============================
|
124
|
+
|
125
|
+
# Helper method that can be used in a validation proc.
|
126
|
+
# @return [true, false] Returns `true` if the argument passed is either `true` or `false`.
|
127
|
+
# Returns `false` on any other argument.
|
128
|
+
def is_boolean?(value)
|
129
|
+
# Only true for either 'false' or 'true'
|
130
|
+
!!value == value
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
|
5
|
+
# Removes `*keys` from self.
|
6
|
+
# @note (see #slice)
|
7
|
+
# @return [Hash] self with `*keys` removed.
|
8
|
+
def delete_keys!(*keys)
|
9
|
+
keys.each do |key|
|
10
|
+
self.delete(key)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
# Creates a sub-hash from `self` with the keys from `*keys`.
|
16
|
+
# @note keys in `*keys` not present in `self` are silently ignored.
|
17
|
+
# @return [Hash] a copy of `self`.
|
18
|
+
def slice(*keys)
|
19
|
+
self.select { |k,v| keys.include?(k) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: with_validations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Stefan Rohlfing
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-12 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &23227300 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *23227300
|
25
|
+
description: ! 'Easy validation of option keys and their values. Installation and
|
26
|
+
usage is very simple. You find all
|
27
|
+
|
28
|
+
the information you need on my homepage listed below.
|
29
|
+
|
30
|
+
'
|
31
|
+
email: stefan.rohlfing@gmail.com
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/with_validations.rb
|
37
|
+
- lib/with_validations/version.rb
|
38
|
+
- lib/with_validations/core_ext/hash.rb
|
39
|
+
- ChangeLog.md
|
40
|
+
- README.md
|
41
|
+
- Rakefile
|
42
|
+
- LICENSE
|
43
|
+
- Gemfile
|
44
|
+
homepage: http://github.com/bytesource/with_validations
|
45
|
+
licenses: []
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.9.1
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
requirements: []
|
63
|
+
rubyforge_project: with_validations
|
64
|
+
rubygems_version: 1.8.15
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: WithValidation - Easy validation of option keys and values.
|
68
|
+
test_files: []
|