input_sanitizer 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
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
18
+ /bin
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order random
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ script: bundle exec rspec spec
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - jruby-18mode
8
+ - jruby-19mode
9
+ - rbx-18mode
10
+
11
+ # keeps failing
12
+ #- rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in input_sanitizer.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Tomek Paczkowski, Tomasz Werbicki, Michal Bugno
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,70 @@
1
+ # InputSanitizer [![Build Status](https://secure.travis-ci.org/futuresimple/input_sanitizer.png?branch=master)](http://travis-ci.org/futuresimple/input_sanitizer)
2
+
3
+ Gem to sanitize hash of incoming data
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'input_sanitizer'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install input_sanitizer
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+
23
+ class PersonSanitizer < InputSanitizer::Sanitizer
24
+ string :name
25
+ string :address
26
+ integer :height
27
+ float :weight
28
+ date :birthday
29
+ end
30
+
31
+ # filters unwanted parameters
32
+ sanitizer = PersonSanitizer.new({:account_id => 1, :name => "John"})
33
+ sanitizer.cleaned() # => {:name => "John"}
34
+
35
+ # provides key access
36
+ sanitizer[:name] # => "John"
37
+
38
+ # also provides shortcut method, same as new({}).cleaned
39
+ PersonSanitizer.clean({:account_id => 1})
40
+
41
+ # supports inheritance
42
+ class PrivilegedSanitizer < PersonSanitizer
43
+ integer :account_id
44
+ end
45
+
46
+ PrivilegedSanitizer.clean({:account_id => 1})
47
+ # => {:account_id => 1}
48
+
49
+ # handles type conversions
50
+ PrivilegedSanitizer.clean({:account_id => '1'})
51
+ # => {:account_id => 1}
52
+
53
+ PrivilegedSanitizer.clean({:birthday => '1986-10-06'})
54
+ # => {:birthday => Date.new(1986, 10, 6)}
55
+
56
+ # it prevents obvious errors
57
+ data = PrivilegedSanitizer.clean({:account_id => 3})
58
+ data[:account] # instead of :account_id
59
+ # => InputSanitizer::KeyNotAllowedError: Key not allowed: account
60
+ # => ...
61
+ ```
62
+
63
+
64
+ ## Contributing
65
+
66
+ 1. Fork it
67
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
68
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
69
+ 4. Push to the branch (`git push origin my-new-feature`)
70
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'input_sanitizer/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Tomek Paczkowski", "Tomasz Werbicki", "Michal Bugno"]
7
+ gem.email = ["tom@futuresimple.com", "tomasz@futuresimple.com", "michal@futuresimple.com"]
8
+ gem.description = %q{Gem to sanitize hash of incoming data}
9
+ gem.summary = %q{Gem to sanitize hash of incoming data}
10
+ gem.homepage = ""
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "input_sanitizer"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = InputSanitizer::VERSION
18
+
19
+ gem.add_development_dependency "rspec"
20
+ gem.add_development_dependency "simplecov"
21
+ end
@@ -0,0 +1,81 @@
1
+ require 'time'
2
+
3
+ module InputSanitizer
4
+ class ConversionError < Exception
5
+ end
6
+
7
+ class IntegerConverter
8
+ def call(value)
9
+ cast = value.to_i
10
+ if cast.to_s != value.to_s
11
+ raise ConversionError.new("invalid integer")
12
+ end
13
+ cast
14
+ end
15
+ end
16
+
17
+ class StringConverter
18
+ def call(value)
19
+ value.to_s
20
+ end
21
+ end
22
+
23
+ class DateConverter
24
+ ISO_RE = /\A\d{4}-?\d{2}-?\d{2}/
25
+
26
+ def call(value)
27
+ raise ConversionError.new("invalid time") unless value =~ ISO_RE
28
+ Date.parse(value)
29
+ rescue ArgumentError
30
+ raise ConversionError.new("invalid iso8601 date")
31
+ end
32
+ end
33
+
34
+ class TimeConverter
35
+ ISO_RE = /\A\d{4}-?\d{2}-?\d{2}([T ]?\d{2}(:?\d{2}(:?\d{2})?)?)?\Z/
36
+
37
+ def call(value)
38
+ if value =~ ISO_RE
39
+ strip_timezone(Time.parse(value))
40
+ else
41
+ raise ConversionError.new("invalid time")
42
+ end
43
+ rescue ArgumentError
44
+ raise ConversionError.new("invalid time")
45
+ end
46
+
47
+ def strip_timezone(time)
48
+ Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec)
49
+ end
50
+ end
51
+
52
+ class BooleanConverter
53
+ BOOLEAN_MAP = {
54
+ true => true,
55
+ false => false,
56
+ 'true' => true,
57
+ 'false' => false,
58
+ '1' => true,
59
+ '0' => false,
60
+ 'yes' => true,
61
+ 'no' => false,
62
+ }
63
+
64
+ def call(value)
65
+ if BOOLEAN_MAP.has_key?(value)
66
+ BOOLEAN_MAP[value]
67
+ else
68
+ truthy, falsy = BOOLEAN_MAP.partition { |_, value| value }
69
+ truthy = truthy.map { |e| "'#{e[0]}'" }.uniq
70
+ falsy = falsy.map { |e| "'#{e[0]}'" }.uniq
71
+
72
+ message = "Invalid boolean: use "
73
+ message += truthy.join(", ")
74
+ message += " for true, or "
75
+ message += falsy.join(", ")
76
+ message += " for false."
77
+ raise ConversionError.new(message)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,42 @@
1
+ module InputSanitizer
2
+ class PositiveIntegerConverter < IntegerConverter
3
+ def call(value)
4
+ val = super
5
+ raise ConversionError.new("invalid integer (neagtive or zero)") if val <= 0
6
+ val
7
+ end
8
+ end
9
+
10
+ class CommaJoinedIntegersConverter
11
+ def call(value)
12
+ non_valid = value.gsub(/[0-9,]/, "")
13
+ if non_valid.empty?
14
+ parts = value.split(",").map(&:to_i)
15
+ else
16
+ invalid_chars = non_valid.split(//)
17
+ invalid_chars_desc = invalid_chars.join(", ")
18
+ raise InputSanitizer::ConversionError.new("Invalid chars: #{invalid_chars_desc}")
19
+ end
20
+ end
21
+ end
22
+
23
+ class SpecificValuesConverter
24
+ def initialize(values)
25
+ @valid_values = values
26
+ end
27
+
28
+ def call(value)
29
+ found = @valid_values.include?(value) ? value : nil
30
+ if !found
31
+ found = @valid_values.include?(value.to_sym) ? value.to_sym : nil
32
+ end
33
+ if !found
34
+ values_joined = @valid_values.join(", ")
35
+ error_message = "Possible values: #{values_joined}"
36
+ raise InputSanitizer::ConversionError.new(error_message)
37
+ else
38
+ found
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,24 @@
1
+ module InputSanitizer
2
+ class KeyNotAllowedError < ArgumentError; end
3
+
4
+ class RestrictedHash < Hash
5
+ def initialize(allowed_keys)
6
+ @allowed_keys = allowed_keys
7
+ super() { |hash, key| default_for_key(key) }
8
+ end
9
+
10
+ def key_allowed?(key)
11
+ @allowed_keys.include?(key)
12
+ end
13
+
14
+ private
15
+ def default_for_key(key)
16
+ key_allowed?(key) ? nil : raise_not_allowed(key)
17
+ end
18
+
19
+ def raise_not_allowed(key)
20
+ msg = "Key not allowed: #{key}"
21
+ raise KeyNotAllowedError.new(msg)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,147 @@
1
+ require 'input_sanitizer/restricted_hash'
2
+ require 'input_sanitizer/default_converters'
3
+
4
+ class InputSanitizer::Sanitizer
5
+ def initialize(data)
6
+ @data = symbolize_keys(data)
7
+ @performed = false
8
+ @errors = []
9
+ @cleaned = InputSanitizer::RestrictedHash.new(self.class.fields.keys)
10
+ end
11
+
12
+ def self.clean(data)
13
+ new(data).cleaned
14
+ end
15
+
16
+ def [](field)
17
+ cleaned[field]
18
+ end
19
+
20
+ def cleaned
21
+ return @cleaned if @performed
22
+ self.class.fields.each do |field, hash|
23
+ type = hash[:type]
24
+ required = hash[:options][:required]
25
+ clean_field(field, type, required)
26
+ end
27
+ @performed = true
28
+ @cleaned.freeze
29
+ end
30
+
31
+ def valid?
32
+ cleaned
33
+ @errors.empty?
34
+ end
35
+
36
+ def errors
37
+ cleaned
38
+ @errors
39
+ end
40
+
41
+ def self.converters
42
+ {
43
+ :integer => InputSanitizer::IntegerConverter.new,
44
+ :string => InputSanitizer::StringConverter.new,
45
+ :date => InputSanitizer::DateConverter.new,
46
+ :time => InputSanitizer::TimeConverter.new,
47
+ :boolean => InputSanitizer::BooleanConverter.new,
48
+ }
49
+ end
50
+
51
+ def self.inherited(subclass)
52
+ subclass.fields = self.fields.dup
53
+ end
54
+
55
+ def self.string(*keys)
56
+ set_keys_to_type(keys, :string)
57
+ end
58
+
59
+ def self.integer(*keys)
60
+ set_keys_to_type(keys, :integer)
61
+ end
62
+
63
+ def self.boolean(*keys)
64
+ set_keys_to_type(keys, :boolean)
65
+ end
66
+
67
+ def self.date(*keys)
68
+ set_keys_to_type(keys, :date)
69
+ end
70
+
71
+ def self.time(*keys)
72
+ set_keys_to_type(keys, :time)
73
+ end
74
+
75
+ def self.custom(*keys)
76
+ options = keys.pop
77
+ converter = options.delete(:converter)
78
+ keys.push(options)
79
+ raise "You did not define a converter for a custom type" if converter == nil
80
+ self.set_keys_to_type(keys, converter)
81
+ end
82
+
83
+ protected
84
+ def self.fields
85
+ @fields ||= {}
86
+ end
87
+
88
+ def self.fields=(new_fields)
89
+ @fields = new_fields
90
+ end
91
+
92
+ private
93
+ def self.extract_options!(array)
94
+ array.last.is_a?(Hash) ? array.pop : {}
95
+ end
96
+
97
+ def self.extract_options(array)
98
+ array.last.is_a?(Hash) ? array.last : {}
99
+ end
100
+
101
+ def clean_field(field, type, required)
102
+ if @data.has_key?(field)
103
+ begin
104
+ @cleaned[field] = convert(field, type)
105
+ rescue InputSanitizer::ConversionError => ex
106
+ add_error(field, :invalid_value, @data[field], ex.message)
107
+ end
108
+ elsif required
109
+ add_missing(field)
110
+ end
111
+ end
112
+
113
+ def add_error(field, error_type, value, description = nil)
114
+ @errors << {
115
+ :field => field,
116
+ :type => error_type,
117
+ :value => value,
118
+ :description => description
119
+ }
120
+ end
121
+
122
+ def add_missing(field)
123
+ add_error(field, :missing, nil, nil)
124
+ end
125
+
126
+ def convert(field, type)
127
+ converter(type).call(@data[field])
128
+ end
129
+
130
+ def converter(type)
131
+ type.respond_to?(:call) ? type : self.class.converters[type]
132
+ end
133
+
134
+ def symbolize_keys(data)
135
+ data.inject({}) do |memo, kv|
136
+ memo[kv.first.to_sym] = kv.last
137
+ memo
138
+ end
139
+ end
140
+
141
+ def self.set_keys_to_type(keys, type)
142
+ opts = extract_options!(keys)
143
+ keys.each do |key|
144
+ fields[key] = { :type => type, :options => opts }
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,3 @@
1
+ module InputSanitizer
2
+ VERSION = "0.1.8"
3
+ end
@@ -0,0 +1,5 @@
1
+ module InputSanitizer
2
+ end
3
+
4
+ require "input_sanitizer/version"
5
+ require 'input_sanitizer/sanitizer'
@@ -0,0 +1,101 @@
1
+ require 'spec_helper'
2
+
3
+ describe InputSanitizer::IntegerConverter do
4
+ let(:converter) { InputSanitizer::IntegerConverter.new }
5
+
6
+ it "casts string to integer" do
7
+ converter.call("42").should == 42
8
+ end
9
+
10
+ it "casts integer to integer" do
11
+ converter.call(42).should == 42
12
+ end
13
+
14
+ it "raises error if cannot cast" do
15
+ lambda { converter.call("f") }.should raise_error(InputSanitizer::ConversionError)
16
+ end
17
+ end
18
+
19
+ describe InputSanitizer::DateConverter do
20
+ let(:converter) { InputSanitizer::DateConverter.new }
21
+
22
+ it "casts dates in iso format" do
23
+ converter.call("2012-05-15").should == Date.new(2012, 5, 15)
24
+ end
25
+
26
+ it "raises error if cannot cast" do
27
+ lambda { converter.call("2012-02-30") }.should raise_error(InputSanitizer::ConversionError)
28
+ end
29
+ end
30
+
31
+ describe InputSanitizer::BooleanConverter do
32
+ let(:converter) { InputSanitizer::BooleanConverter.new }
33
+
34
+ it "casts 'true' to true" do
35
+ converter.call('true').should be_true
36
+ end
37
+
38
+ it "casts true to true" do
39
+ converter.call(true).should be_true
40
+ end
41
+
42
+ it "casts '1' to true" do
43
+ converter.call('1').should be_true
44
+ end
45
+
46
+ it "casts 'yes' to true" do
47
+ converter.call('yes').should be_true
48
+ end
49
+
50
+ it "casts 'false' to false" do
51
+ converter.call('false').should be_false
52
+ end
53
+
54
+ it "casts false to false" do
55
+ converter.call(false).should be_false
56
+ end
57
+
58
+ it "casts '0' to false" do
59
+ converter.call('0').should be_false
60
+ end
61
+
62
+ it "casts 'no' to false" do
63
+ converter.call('no').should be_false
64
+ end
65
+
66
+ it "raises error if cannot cast" do
67
+ lambda { converter.call("notboolean") }.should raise_error(InputSanitizer::ConversionError)
68
+ end
69
+ end
70
+
71
+
72
+ describe InputSanitizer::TimeConverter do
73
+ let(:converter) { InputSanitizer::TimeConverter.new }
74
+
75
+ it "raises if timezone part given" do
76
+ lambda { converter.call("2012-05-15 13:42:54 +01:00") }.should raise_error(InputSanitizer::ConversionError)
77
+ end
78
+
79
+ it "casts date time in iso format" do
80
+ t = Time.utc(2012, 5, 15, 13, 42, 54)
81
+ converter.call("2012-05-15 13:42:54").should == t
82
+ converter.call("2012-05-15T13:42:54").should == t
83
+ converter.call("20120515134254").should == t
84
+
85
+ end
86
+
87
+ it "does not require time part" do
88
+ converter.call("2012-05-15 13:42").should == Time.utc(2012, 5, 15, 13, 42)
89
+ converter.call("2012-05-15 13").should == Time.utc(2012, 5, 15, 13)
90
+ converter.call("2012-05-15").should == Time.utc(2012, 5, 15)
91
+
92
+ end
93
+
94
+ it "raises error if can format is wrong" do
95
+ lambda { converter.call("2/10/2031 13:44:22") }.should raise_error(InputSanitizer::ConversionError)
96
+ end
97
+
98
+ it "raises error if date is wrong" do
99
+ lambda { converter.call("2012-02-32") }.should raise_error(InputSanitizer::ConversionError)
100
+ end
101
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'input_sanitizer/extended_converters'
3
+
4
+ describe InputSanitizer::PositiveIntegerConverter do
5
+ let(:converter) { InputSanitizer::PositiveIntegerConverter.new }
6
+
7
+ it "raises error if integer less than zero" do
8
+ lambda { converter.call("-3") }.should raise_error(InputSanitizer::ConversionError)
9
+ end
10
+
11
+ it "raises error if integer equals zero" do
12
+ lambda { converter.call("0") }.should raise_error(InputSanitizer::ConversionError)
13
+ end
14
+ end
15
+
16
+ describe InputSanitizer::CommaJoinedIntegersConverter do
17
+ let(:converter) { InputSanitizer::CommaJoinedIntegersConverter.new }
18
+
19
+ it "parses to array of ids" do
20
+ converter.call("1,2,3,5").should == [1, 2, 3, 5]
21
+ end
22
+
23
+ it "raises on invalid character" do
24
+ lambda { converter.call(":") }.should raise_error(InputSanitizer::ConversionError)
25
+ end
26
+ end
27
+
28
+ describe InputSanitizer::SpecificValuesConverter do
29
+ let(:converter) { InputSanitizer::SpecificValuesConverter.new([:a, :b]) }
30
+
31
+ it "converts valid value to symbol" do
32
+ converter.call("b").should == :b
33
+ end
34
+
35
+ it "raises on invalid value" do
36
+ lambda { converter.call("c") }.should raise_error(InputSanitizer::ConversionError)
37
+ end
38
+
39
+ it "converts valid value to string" do
40
+ converter = InputSanitizer::SpecificValuesConverter.new(["a", "b"])
41
+ converter.call("a").should == "a"
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe InputSanitizer::RestrictedHash do
4
+ let(:hash) { InputSanitizer::RestrictedHash.new([:a, :b]) }
5
+ subject { hash }
6
+
7
+ it "does not allow bad keys" do
8
+ lambda{hash[:c]}.should raise_error(InputSanitizer::KeyNotAllowedError)
9
+ end
10
+
11
+ it "does allow correct keys" do
12
+ hash[:a].should be_nil
13
+ end
14
+
15
+ it "returns value for correct key" do
16
+ hash[:a] = 'stuff'
17
+ hash[:a].should == 'stuff'
18
+ end
19
+ end
@@ -0,0 +1,237 @@
1
+ require 'spec_helper'
2
+
3
+ class BasicSanitizer < InputSanitizer::Sanitizer
4
+ string :x, :y, :z
5
+ integer :num
6
+ date :birthday
7
+ time :updated_at
8
+ custom :cust1, :cust2, :converter => lambda { |v| v.reverse }
9
+ end
10
+
11
+ class BrokenCustomSanitizer < InputSanitizer::Sanitizer
12
+
13
+ end
14
+
15
+ class ExtendedSanitizer < BasicSanitizer
16
+ boolean :is_nice
17
+ end
18
+
19
+ class OverridingSanitizer < BasicSanitizer
20
+ integer :is_nice
21
+ end
22
+
23
+ class RequiredParameters < BasicSanitizer
24
+ integer :is_nice, :required => true
25
+ end
26
+
27
+ class RequiredCustom < BasicSanitizer
28
+ custom :c1, :required => true, :converter => lambda { |v| v }
29
+ end
30
+
31
+ describe InputSanitizer::Sanitizer do
32
+ let(:sanitizer) { BasicSanitizer.new(@params) }
33
+
34
+ describe ".clean" do
35
+ it "returns cleaned data" do
36
+ clean_data = mock()
37
+ BasicSanitizer.any_instance.should_receive(:cleaned).and_return(clean_data)
38
+ BasicSanitizer.clean({}).should be(clean_data)
39
+ end
40
+ end
41
+
42
+ describe "#cleaned" do
43
+ let(:cleaned) { sanitizer.cleaned }
44
+ let(:required) { RequiredParameters.new(@params) }
45
+
46
+ it "includes specified params" do
47
+ @params = {"x" => 3, "y" => "tom", "z" => "mike"}
48
+
49
+ cleaned.should have_key(:x)
50
+ cleaned.should have_key(:y)
51
+ cleaned.should have_key(:z)
52
+ end
53
+
54
+ it "strips not specified params" do
55
+ @params = {"d" => 3}
56
+
57
+ cleaned.should_not have_key(:d)
58
+ end
59
+
60
+ it "freezes cleaned hash" do
61
+ @params = {}
62
+
63
+ cleaned.should be_frozen
64
+ end
65
+
66
+ it "uses RestrictedHash" do
67
+ @params = {}
68
+
69
+ lambda{cleaned[:does_not_exist]}.should raise_error(InputSanitizer::KeyNotAllowedError)
70
+ end
71
+
72
+ it "includes specified keys and strips rest" do
73
+ @params = {"d" => 3, "x" => "ddd"}
74
+
75
+ cleaned.should have_key(:x)
76
+ cleaned.should_not have_key(:d)
77
+ end
78
+
79
+ it "works with symbols as input keys" do
80
+ @params = {:d => 3, :x => "ddd"}
81
+
82
+ cleaned.should have_key(:x)
83
+ cleaned.should_not have_key(:d)
84
+ end
85
+
86
+ it "silently discards cast errors" do
87
+ @params = {:num => "f"}
88
+
89
+ cleaned.should_not have_key(:num)
90
+ end
91
+
92
+ it "inherits converters from superclass" do
93
+ sanitizer = ExtendedSanitizer.new({:num => "23", :is_nice => 'false'})
94
+ cleaned = sanitizer.cleaned
95
+
96
+ cleaned.should have_key(:num)
97
+ cleaned[:num].should == 23
98
+ cleaned[:is_nice].should be_false
99
+ end
100
+
101
+ it "overrides inherited fields" do
102
+ sanitizer = OverridingSanitizer.new({:is_nice => "42"})
103
+ cleaned = sanitizer.cleaned
104
+
105
+ cleaned.should have_key(:is_nice)
106
+ cleaned[:is_nice].should == 42
107
+ end
108
+
109
+ end
110
+
111
+ describe ".custom" do
112
+ let(:sanitizer) { BasicSanitizer.new(@params) }
113
+ let(:cleaned) { sanitizer.cleaned }
114
+
115
+ it "converts using custom converter" do
116
+ @params = {:cust1 => "cigam"}
117
+
118
+ cleaned.should have_key(:cust1)
119
+ cleaned[:cust1].should == "magic"
120
+ end
121
+
122
+ it "raises an error when converter is not defined" do
123
+ expect do
124
+ BrokenCustomSanitizer.custom(:x)
125
+ end.should raise_error
126
+ end
127
+ end
128
+
129
+ describe ".converters" do
130
+ let(:sanitizer) { InputSanitizer::Sanitizer }
131
+
132
+ it "includes :integer type" do
133
+ sanitizer.converters.should have_key(:integer)
134
+ sanitizer.converters[:integer].should be_a(InputSanitizer::IntegerConverter)
135
+ end
136
+
137
+ it "includes :string type" do
138
+ sanitizer.converters.should have_key(:string)
139
+ sanitizer.converters[:string].should be_a(InputSanitizer::StringConverter)
140
+ end
141
+
142
+ it "includes :date type" do
143
+ sanitizer.converters.should have_key(:date)
144
+ sanitizer.converters[:date].should be_a(InputSanitizer::DateConverter)
145
+ end
146
+
147
+ it "includes :boolean type" do
148
+ sanitizer.converters.should have_key(:boolean)
149
+ sanitizer.converters[:boolean].should be_a(InputSanitizer::BooleanConverter)
150
+ end
151
+ end
152
+
153
+ describe '.extract_options' do
154
+
155
+ it "extracts hash from array if is last" do
156
+ options = { :a => 1}
157
+ array = [1,2, options]
158
+ BasicSanitizer.extract_options(array).should == options
159
+ array.should == [1,2, options]
160
+ end
161
+
162
+ it "does not extract the last element if not a hash and returns default empty hash" do
163
+ array = [1,2]
164
+ BasicSanitizer.extract_options(array).should_not == 2
165
+ BasicSanitizer.extract_options(array).should == {}
166
+ array.should == [1,2]
167
+ end
168
+
169
+ end
170
+
171
+ describe '.extract_options!' do
172
+
173
+ it "extracts hash from array if is last" do
174
+ options = { :a => 1}
175
+ array = [1,2, options]
176
+ BasicSanitizer.extract_options!(array).should == options
177
+ array.should == [1,2]
178
+ end
179
+
180
+ it "leaves other arrays alone" do
181
+ array = [1,2]
182
+ BasicSanitizer.extract_options!(array).should == {}
183
+ array.should == [1,2]
184
+ end
185
+
186
+ end
187
+
188
+ describe "#valid?" do
189
+ it "is valid when params are ok" do
190
+ @params = {:num => "3"}
191
+
192
+ sanitizer.should be_valid
193
+ end
194
+
195
+ it "is not valid when missing params" do
196
+ @params = {:num => "mike"}
197
+
198
+ sanitizer.should_not be_valid
199
+ end
200
+ end
201
+
202
+ describe "#[]" do
203
+ it "accesses cleaned data" do
204
+ @params = {:num => "3"}
205
+
206
+ sanitizer[:num].should == 3
207
+ end
208
+ end
209
+
210
+ describe "#errors" do
211
+ it "returns array containing hashes describing error" do
212
+ @params = {:num => "mike"}
213
+
214
+ errors = sanitizer.errors
215
+ errors.size.should == 1
216
+ errors[0][:field].should == :num
217
+ errors[0][:type].should == :invalid_value
218
+ errors[0][:description].should == "invalid integer"
219
+ errors[0][:value].should == "mike"
220
+ end
221
+
222
+ it "returns error type missing if value is missing" do
223
+ sanitizer = RequiredParameters.new({})
224
+ error = sanitizer.errors[0]
225
+ error[:type].should == :missing
226
+ end
227
+
228
+ it "handles required custom params" do
229
+ sanitizer = RequiredCustom.new({})
230
+
231
+ sanitizer.should_not be_valid
232
+ error = sanitizer.errors[0]
233
+ error[:type].should == :missing
234
+ error[:field].should == :c1
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler.setup(:test)
3
+
4
+ require 'simplecov'
5
+ SimpleCov.start unless ENV['CI'] == 'true'
6
+
7
+ require 'input_sanitizer'
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: input_sanitizer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.8
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tomek Paczkowski
9
+ - Tomasz Werbicki
10
+ - Michal Bugno
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-10-09 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: '0'
24
+ type: :development
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ! '>='
30
+ - !ruby/object:Gem::Version
31
+ version: '0'
32
+ - !ruby/object:Gem::Dependency
33
+ name: simplecov
34
+ requirement: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ description: Gem to sanitize hash of incoming data
49
+ email:
50
+ - tom@futuresimple.com
51
+ - tomasz@futuresimple.com
52
+ - michal@futuresimple.com
53
+ executables: []
54
+ extensions: []
55
+ extra_rdoc_files: []
56
+ files:
57
+ - .gitignore
58
+ - .rspec
59
+ - .travis.yml
60
+ - Gemfile
61
+ - LICENSE
62
+ - README.md
63
+ - Rakefile
64
+ - input_sanitizer.gemspec
65
+ - lib/input_sanitizer.rb
66
+ - lib/input_sanitizer/default_converters.rb
67
+ - lib/input_sanitizer/extended_converters.rb
68
+ - lib/input_sanitizer/restricted_hash.rb
69
+ - lib/input_sanitizer/sanitizer.rb
70
+ - lib/input_sanitizer/version.rb
71
+ - spec/default_converters_spec.rb
72
+ - spec/extended_converters_spec.rb
73
+ - spec/restricted_hash_spec.rb
74
+ - spec/sanitizer_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage: ''
77
+ licenses: []
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 1.8.24
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: Gem to sanitize hash of incoming data
100
+ test_files:
101
+ - spec/default_converters_spec.rb
102
+ - spec/extended_converters_spec.rb
103
+ - spec/restricted_hash_spec.rb
104
+ - spec/sanitizer_spec.rb
105
+ - spec/spec_helper.rb
106
+ has_rdoc: