with_validations 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ ## Version 0.5.0 (April 12, 2012)
2
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in with_validations.gemspec
4
+ gemspec
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.
@@ -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.
@@ -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
+
@@ -0,0 +1,3 @@
1
+ module WithValidations
2
+ VERSION = "0.5.0"
3
+ end
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: []