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.
@@ -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: []