parametron 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZWU4ODQyZTY0ZDczMTY5NzhmNWNhMWFhMTdiYjk5YmJmNGU4NThmOA==
5
+ data.tar.gz: !binary |-
6
+ ZTY4Mjg3NGFlYTZiZWZmYWEzMGM4NzljOGYyYmRjNmU4OTZiYzU0Yw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NDhkNTg4NzRlOTc1ZTZlOWQ0ODc4MTc3ZTIwZjEyYzZhMGMxYTVlY2IzZGJi
10
+ MmYwOWIxZDhiMGE0YTJiOTgzNmQ2MjI0YTM0YTE1ZTYzZTAxYzgwMWZjN2Qz
11
+ ZDEwZDk0OTIzMzIwYjk1NjAzNzY5N2FmYWZmZWYzYTZlYmZmOTg=
12
+ data.tar.gz: !binary |-
13
+ NDkyMzJkMWNmNGJmMzlhNmRjMmQ5ODZiMTY0NTU4NDI3MTU1MDJkZjkzNzhi
14
+ N2UyNGY0MDBkMWRiMzJmMzViMWU4MDNiNjQyNDg3YzU2MTNmZTJjNjM2ZGI0
15
+ MzVlNjZjODI2MzlkOTJkNTdjY2M2NWRiZmQ3YTFiMzQ3NWQ3ZmE=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in parametron.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Yury Batenko
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,49 @@
1
+ # Parametron
2
+
3
+ This simple library implements DSL for validating method input parameters
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'parametron'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install parametron
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+
23
+ class VictimStrict
24
+ include Parametron
25
+
26
+ params_for(:fetch, strict: true) do
27
+ required :city, validator: /\w+/
28
+ required :year, validator: /\d{4}/
29
+ optional :title, validator: ->(str){ str != "Moscow" }
30
+ optional :number, validator: /\d+/, default: 42
31
+ end
32
+
33
+ def fetch params
34
+ # .. do something useful
35
+ end
36
+ end
37
+ ```
38
+
39
+ See `spec/parametron_spec` how this library suppossed to work.
40
+ All hackers do this and you should too!
41
+
42
+
43
+ ## Contributing
44
+
45
+ 1. Fork it
46
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
47
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
48
+ 4. Push to the branch (`git push origin my-new-feature`)
49
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,87 @@
1
+ class Parametron::ParamsValidator
2
+
3
+ attr_accessor :required_vals, :optional_vals
4
+
5
+ def initialize(opts)
6
+ @raise_on_excess = opts.fetch(:strict, false)
7
+ self.required_vals = []
8
+ self.optional_vals = []
9
+ end
10
+
11
+ def optional(name, opts={})
12
+ default = opts.delete(:default)
13
+ validator = opts.delete(:validator)
14
+ raise Parametron::ErrorMethodParams.new("Not available param: #{opts.inspect}") unless opts.empty?
15
+ self.optional_vals << OptionalParameter.new(name.to_s, default, validator)
16
+ end
17
+
18
+ def required(name, opts={})
19
+ default = opts.delete(:default)
20
+ validator = opts.delete(:validator)
21
+ raise Parametron::ErrorMethodParams.new("Not available param: #{opts.inspect}") unless opts.empty?
22
+ self.required_vals << RequiredParameter.new(name.to_s, default, validator)
23
+ end
24
+
25
+ def validate!(obj, params)
26
+ obj.validation_error_cause = []
27
+ normalized_param_keys = params.keys.map(&:to_s).sort
28
+ exceed_params = normalized_param_keys - valid_keys
29
+ if exceed_params.any?
30
+ exceed_params.each do |par|
31
+ obj.validation_error_cause << [par, params[par.to_sym]]
32
+ end
33
+ raise Parametron::ExcessParameter.new(exceed_params.to_s) if @raise_on_excess
34
+ end
35
+
36
+ key_common = normalized_param_keys & required_keys
37
+ if key_common != required_keys
38
+ missing = required_keys - key_common
39
+ obj.validation_error_cause << missing
40
+ raise Parametron::RequiredParamError.new(missing)
41
+ end
42
+
43
+ params.each do |k, v|
44
+ key = k.to_s
45
+ next unless valid_keys.include?(key)
46
+ validators.find{|val| val.name == key}.tap do |curr_val|
47
+ unless curr_val.valid?(v)
48
+ obj.validation_error_cause << [key, v]
49
+ raise Parametron::MalformedParams.new(key)
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ private
57
+ def validators
58
+ self.required_vals + self.optional_vals
59
+ end
60
+
61
+ def valid_keys
62
+ validators.map{|x| x.name.to_s}.sort
63
+ end
64
+
65
+ def required_keys
66
+ self.required_vals.map{|x| x.name.to_s}
67
+ end
68
+
69
+ class GenericParameter < Struct.new(:name, :default, :validator)
70
+ def valid?(value)
71
+ case self.validator
72
+ when Regexp then value && !!self.validator.match(value.to_s)
73
+ when Proc then value && !!self.validator(value).call
74
+ else
75
+ true
76
+ end
77
+ end
78
+ end
79
+
80
+ class OptionalParameter < GenericParameter
81
+ end
82
+
83
+ class RequiredParameter < GenericParameter
84
+ end
85
+
86
+
87
+ end
@@ -0,0 +1,3 @@
1
+ module Parametron
2
+ VERSION = "0.0.1"
3
+ end
data/lib/parametron.rb ADDED
@@ -0,0 +1,63 @@
1
+ require "parametron/version"
2
+
3
+ module Parametron
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ base.send(:attr_accessor, :validation_error_cause)
8
+ end
9
+
10
+ module ClassMethods
11
+ attr_reader :params_validator
12
+ def params_for(method_name, opts={}, &block)
13
+ instance_eval do
14
+ @_method_name = method_name.to_sym
15
+ @params_validator = Parametron::ParamsValidator.new(opts)
16
+ @params_validator.instance_eval(&block)
17
+ end
18
+ end
19
+
20
+ def method_added(name)
21
+ return if name != @_method_name or instance_variable_get(:"@_METHOD_#{name}_WRAPPED")
22
+ instance_variable_set(:"@_METHOD_#{name}_WRAPPED", true)
23
+ original = instance_method(name.to_sym)
24
+ remove_method(name.to_sym)
25
+ define_method(name) do |params|
26
+ new_params = _validate!(_set_defaults!(params))
27
+ original.bind(self).call(new_params)
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+ def _set_defaults!(params)
34
+ new_par = params.dup
35
+ _validators_list.each do |v|
36
+ if new_par[v.name].nil? && new_par[v.name.to_sym].nil?
37
+ new_par[v.name.to_sym] = v.default if v.default
38
+ end
39
+ end
40
+ new_par
41
+ end
42
+
43
+ def _validate!(params)
44
+ _params_validator.validate!(self, params)
45
+ params
46
+ end
47
+
48
+ def _params_validator
49
+ self.class.params_validator
50
+ end
51
+
52
+ def _validators_list
53
+ _params_validator.required_vals + _params_validator.optional_vals
54
+ end
55
+
56
+ MalformedParams = Class.new(ArgumentError)
57
+ RequiredParamError = Class.new(ArgumentError)
58
+ ErrorMethodParams = Class.new(ArgumentError)
59
+ ExcessParameter = Class.new(ArgumentError)
60
+
61
+ end
62
+
63
+ require 'parametron/params_validator'
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'parametron/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "parametron"
8
+ spec.version = Parametron::VERSION
9
+ spec.authors = ["Yury Batenko"]
10
+ spec.email = ["jurbat@gmail.com"]
11
+ spec.description = %q{DSL for method arguments validation}
12
+ spec.summary = %q{DSL for method arguments validation}
13
+ spec.homepage = "http://github.com/svenyurgensson/parametron"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = ">= 1.9"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+
27
+ end
@@ -0,0 +1,208 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parametron do
4
+
5
+ context 'Params' do
6
+ context '.params' do
7
+ it 'accepts params block' do
8
+ expect do
9
+ class Victim
10
+ include Parametron
11
+ params_for(:_) do; end
12
+ end
13
+ end.not_to raise_error
14
+ end
15
+ end
16
+
17
+ context '.optional' do
18
+ it 'accepts optional params' do
19
+ expect do
20
+ class Victim
21
+ include Parametron
22
+ params_for(:_) do
23
+ optional :city, default: ''
24
+ end
25
+ end
26
+ end.not_to raise_error
27
+ end
28
+
29
+ it 'accepts defaults and validator params' do
30
+ expect do
31
+ class Victim
32
+ include Parametron
33
+ params_for(:_) do
34
+ optional :city, default: '', validator: /\d+/
35
+ end
36
+ end
37
+ end.not_to raise_error
38
+ end
39
+
40
+ it 'store params validator' do
41
+ class Victim
42
+ include Parametron
43
+ params_for(:_) do
44
+ optional :city, default: '', validator: /\d+/
45
+ end
46
+ end
47
+ Victim.params_validator.should be_kind_of(Parametron::ParamsValidator)
48
+ Victim.params_validator.required_vals.should be_empty
49
+ Victim.params_validator.optional_vals.should have(1).items
50
+ end
51
+
52
+ end # context .optional
53
+
54
+
55
+ context '.required' do
56
+ it 'accepts required params' do
57
+ expect do
58
+ class Victim
59
+ include Parametron
60
+ params_for(:_) do
61
+ required :city, default: ''
62
+ end
63
+ end
64
+ end.not_to raise_error
65
+ end
66
+
67
+ it 'accepts defaults and validator params' do
68
+ expect do
69
+ class Victim
70
+ include Parametron
71
+ params_for(:_) do
72
+ required :city, default: '', validator: /\d+/
73
+ end
74
+ end
75
+ end.not_to raise_error
76
+ end
77
+
78
+ it 'store params validator' do
79
+ class Victim
80
+ include Parametron
81
+ params_for(:_) do
82
+ required :city, default: '', validator: /\d+/
83
+ end
84
+ end
85
+ Victim.params_validator.should be_kind_of(Parametron::ParamsValidator)
86
+ Victim.params_validator.optional_vals.should be_empty
87
+ Victim.params_validator.required_vals.should have(1).items
88
+ end
89
+
90
+ end # context .required
91
+
92
+
93
+ context '.validate' do
94
+ class VictimStrict
95
+ include Parametron
96
+ params_for(:fetch, strict: true) do
97
+ required :city, validator: /\w+/
98
+ required :year, validator: /\d+/, default: 2012
99
+ optional :title, validator: /\w+/
100
+ optional :other, default: 'staff'
101
+ end
102
+ def fetch params
103
+ end
104
+ end
105
+ class VictimRelaxed
106
+ include Parametron
107
+ params_for(:fetch) do
108
+ required :city, validator: /\w+/
109
+ required :year, validator: /\d+/, default: 2012
110
+ optional :title, validator: /\w+/
111
+ optional :other, default: 'staff'
112
+ end
113
+ def fetch params
114
+ end
115
+ end
116
+
117
+ subject{ VictimStrict.new }
118
+
119
+ it 'accepts valid params' do
120
+ expect do
121
+ subject.fetch({city: 'Moskow', year: '1917', title: 'Nothing', other: 'Not need'})
122
+ end.not_to raise_error
123
+ end
124
+
125
+ it 'raise error when required param absent' do
126
+ expect do
127
+ subject.fetch({year: 1917, title: 'Nothing', other: 'Not need'})
128
+ end.to raise_error(Parametron::RequiredParamError)
129
+ subject.validation_error_cause.should eql [["city"]]
130
+ end
131
+
132
+
133
+ context "when strict" do
134
+ subject{ VictimStrict.new }
135
+
136
+ it 'raise on unknown params' do
137
+ expect do
138
+ subject.fetch({city: 'Moskow', year: 1917, title: 'Nothing', other: 'Not need', invalid: 'Yes'})
139
+ end.to raise_error(Parametron::ExcessParameter)
140
+ subject.validation_error_cause.should eql [["invalid", "Yes"]]
141
+ end
142
+ end # context strict
143
+
144
+ context "when not strict" do
145
+ subject{ VictimRelaxed.new }
146
+
147
+ it 'not raise on unknown params' do
148
+ expect do
149
+ subject.fetch({city: 'Moskow', year: 1917, title: 'Nothing', other: 'Not need', invalid: 'Yes'})
150
+ end.not_to raise_error
151
+ subject.validation_error_cause.should eql [["invalid", "Yes"]]
152
+ end
153
+ end # context not strict
154
+
155
+ end # context .validate
156
+
157
+ context 'setting defaults for parameters' do
158
+ class VictimWithDef
159
+ include Parametron
160
+ params_for(:fetch, strict: true) do
161
+ required :city, validator: /\w+/
162
+ required :year, validator: /\d+/, default: '2012'
163
+ optional :title, validator: /\w+/
164
+ optional :other, default: 'staff'
165
+ end
166
+ attr_reader :city, :year, :title, :other
167
+
168
+ def fetch params
169
+ @city = params[:city]
170
+ @year = params[:year]
171
+ @title= params[:title]
172
+ @other= params[:other]
173
+ end
174
+ end
175
+
176
+ let(:par){{city: 'Krasn', title: 'No way'}}
177
+ let(:par_full){{city: 'Krasn', year: '2000', title: 'No way', other: 'KillAll'}}
178
+
179
+ it 'accepts params' do
180
+ expect do
181
+ @obj = VictimWithDef.new
182
+ @obj.fetch(par)
183
+ end.not_to raise_error
184
+ end
185
+
186
+ it 'set defaults' do
187
+ @obj = VictimWithDef.new
188
+ @obj.fetch(par)
189
+ @obj.city.should eql par[:city]
190
+ @obj.title.should eql par[:title]
191
+ @obj.year.should eql '2012'
192
+ @obj.other.should eql 'staff'
193
+ end
194
+
195
+ it 'given params have advantage over defaults' do
196
+ @obj = VictimWithDef.new
197
+ @obj.fetch(par_full)
198
+ @obj.city.should eql par_full[:city]
199
+ @obj.title.should eql par_full[:title]
200
+ @obj.year.should eql par_full[:year]
201
+ @obj.other.should eql par_full[:other]
202
+ end
203
+ end
204
+
205
+
206
+ end # context Params
207
+
208
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'parametron'
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parametron
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Yury Batenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-05-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ~>
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: '1.3'
24
+ type: :development
25
+ prerelease: false
26
+ name: bundler
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ version_requirements: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ name: rake
41
+ - !ruby/object:Gem::Dependency
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ type: :development
53
+ prerelease: false
54
+ name: rspec
55
+ description: DSL for method arguments validation
56
+ email:
57
+ - jurbat@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - .gitignore
63
+ - .rspec
64
+ - .travis.yml
65
+ - Gemfile
66
+ - LICENSE.txt
67
+ - README.md
68
+ - Rakefile
69
+ - lib/parametron.rb
70
+ - lib/parametron/params_validator.rb
71
+ - lib/parametron/version.rb
72
+ - parametron.gemspec
73
+ - spec/parametron_spec.rb
74
+ - spec/spec_helper.rb
75
+ homepage: http://github.com/svenyurgensson/parametron
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '1.9'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.0.3
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: DSL for method arguments validation
99
+ test_files:
100
+ - spec/parametron_spec.rb
101
+ - spec/spec_helper.rb