parametron 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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