input_sanitizer 0.1.8

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.
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: