input_sanitizer 0.1.9 → 0.4.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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gempush.yml +28 -0
  3. data/.gitignore +2 -1
  4. data/.travis.yml +4 -8
  5. data/CHANGELOG +96 -0
  6. data/LICENSE +201 -22
  7. data/README.md +22 -3
  8. data/input_sanitizer.gemspec +10 -4
  9. data/lib/input_sanitizer.rb +5 -2
  10. data/lib/input_sanitizer/errors.rb +142 -0
  11. data/lib/input_sanitizer/extended_converters.rb +5 -52
  12. data/lib/input_sanitizer/extended_converters/comma_joined_integers_converter.rb +15 -0
  13. data/lib/input_sanitizer/extended_converters/comma_joined_strings_converter.rb +15 -0
  14. data/lib/input_sanitizer/extended_converters/positive_integer_converter.rb +12 -0
  15. data/lib/input_sanitizer/extended_converters/specific_values_converter.rb +19 -0
  16. data/lib/input_sanitizer/restricted_hash.rb +49 -8
  17. data/lib/input_sanitizer/v1.rb +22 -0
  18. data/lib/input_sanitizer/v1/clean_field.rb +38 -0
  19. data/lib/input_sanitizer/{default_converters.rb → v1/default_converters.rb} +30 -13
  20. data/lib/input_sanitizer/v1/sanitizer.rb +166 -0
  21. data/lib/input_sanitizer/v2.rb +13 -0
  22. data/lib/input_sanitizer/v2/clean_field.rb +36 -0
  23. data/lib/input_sanitizer/v2/clean_payload_collection_field.rb +41 -0
  24. data/lib/input_sanitizer/v2/clean_query_collection_field.rb +40 -0
  25. data/lib/input_sanitizer/v2/error_collection.rb +49 -0
  26. data/lib/input_sanitizer/v2/nested_sanitizer_factory.rb +19 -0
  27. data/lib/input_sanitizer/v2/payload_sanitizer.rb +130 -0
  28. data/lib/input_sanitizer/v2/payload_transform.rb +42 -0
  29. data/lib/input_sanitizer/v2/query_sanitizer.rb +33 -0
  30. data/lib/input_sanitizer/v2/types.rb +213 -0
  31. data/lib/input_sanitizer/version.rb +1 -1
  32. data/spec/extended_converters/comma_joined_integers_converter_spec.rb +18 -0
  33. data/spec/extended_converters/comma_joined_strings_converter_spec.rb +18 -0
  34. data/spec/extended_converters/positive_integer_converter_spec.rb +18 -0
  35. data/spec/extended_converters/specific_values_converter_spec.rb +27 -0
  36. data/spec/restricted_hash_spec.rb +37 -7
  37. data/spec/sanitizer_spec.rb +129 -26
  38. data/spec/spec_helper.rb +17 -2
  39. data/spec/v1/default_converters_spec.rb +141 -0
  40. data/spec/v2/converters_spec.rb +174 -0
  41. data/spec/v2/payload_sanitizer_spec.rb +460 -0
  42. data/spec/v2/payload_transform_spec.rb +98 -0
  43. data/spec/v2/query_sanitizer_spec.rb +300 -0
  44. data/v2.md +52 -0
  45. metadata +105 -40
  46. data/lib/input_sanitizer/sanitizer.rb +0 -152
  47. data/spec/default_converters_spec.rb +0 -101
  48. data/spec/extended_converters_spec.rb +0 -62
@@ -0,0 +1,42 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+
3
+ class InputSanitizer::V2::PayloadTransform
4
+ attr_reader :original_payload, :context
5
+
6
+ def self.call(original_payload, context = {})
7
+ new(original_payload, context).call
8
+ end
9
+
10
+ def initialize(original_payload, context = {})
11
+ fail "#{self.class} is missing #transform method" unless respond_to?(:transform)
12
+ @original_payload, @context = original_payload, context
13
+ end
14
+
15
+ def call
16
+ transform
17
+ payload
18
+ end
19
+
20
+ private
21
+ def rename(from, to)
22
+ if has?(from)
23
+ data = payload.delete(from)
24
+ payload[to] = data
25
+ end
26
+ end
27
+
28
+ def merge_in(field, options = {})
29
+ if source = payload.delete(field)
30
+ source = options[:using].call(source) if options[:using]
31
+ payload.merge!(source)
32
+ end
33
+ end
34
+
35
+ def has?(key)
36
+ payload.has_key?(key)
37
+ end
38
+
39
+ def payload
40
+ @payload ||= original_payload.with_indifferent_access
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ class InputSanitizer::V2::QuerySanitizer < InputSanitizer::V2::PayloadSanitizer
2
+ def self.converters
3
+ {
4
+ :integer => InputSanitizer::V2::Types::CoercingIntegerCheck.new,
5
+ :float => InputSanitizer::V2::Types::CoercingFloatCheck.new,
6
+ :string => InputSanitizer::V2::Types::StringCheck.new,
7
+ :boolean => InputSanitizer::V2::Types::CoercingBooleanCheck.new,
8
+ :datetime => InputSanitizer::V2::Types::DatetimeCheck.new,
9
+ :date => InputSanitizer::V2::Types::DatetimeCheck.new(:check_date => true),
10
+ :url => InputSanitizer::V2::Types::URLCheck.new,
11
+ }
12
+ end
13
+ initialize_types_dsl
14
+
15
+ def self.sort_by(allowed_values, options = {})
16
+ set_keys_to_converter([:sort_by, { :allow => allowed_values }.merge(options)], InputSanitizer::V2::Types::SortByCheck.new)
17
+ end
18
+
19
+ # allow underscore cache buster by default
20
+ string :_
21
+
22
+ private
23
+ def perform_clean
24
+ super
25
+ @errors.each do |error|
26
+ error.field = error.field[1..-1] if error.field.start_with?('/')
27
+ end
28
+ end
29
+
30
+ def sanitizer_type
31
+ :query
32
+ end
33
+ end
@@ -0,0 +1,213 @@
1
+ require 'active_support/core_ext/object/blank'
2
+
3
+ module InputSanitizer::V2::Types
4
+ class IntegerCheck
5
+ def call(value, options = {})
6
+ if value == nil && (options[:allow_nil] == false || options[:allow_blank] == false || options[:required] == true)
7
+ raise InputSanitizer::BlankValueError
8
+ elsif value == nil
9
+ value
10
+ else
11
+ Integer(value).tap do |integer|
12
+ raise InputSanitizer::TypeMismatchError.new(value, :integer) unless integer == value
13
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:minimum] && integer < options[:minimum]
14
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:maximum] && integer > options[:maximum]
15
+ end
16
+ end
17
+ rescue ArgumentError, TypeError
18
+ raise InputSanitizer::TypeMismatchError.new(value, :integer)
19
+ end
20
+ end
21
+
22
+ class CoercingIntegerCheck
23
+ def call(value, options = {})
24
+ if value == nil || value == 'null'
25
+ if options[:allow_nil] == false || options[:allow_blank] == false || options[:required] == true
26
+ raise InputSanitizer::BlankValueError
27
+ else
28
+ nil
29
+ end
30
+ else
31
+ Integer(value).tap do |integer|
32
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:minimum] && integer < options[:minimum]
33
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:maximum] && integer > options[:maximum]
34
+ end
35
+ end
36
+ rescue ArgumentError
37
+ raise InputSanitizer::TypeMismatchError.new(value, :integer)
38
+ end
39
+ end
40
+
41
+ class FloatCheck
42
+ def call(value, options = {})
43
+ if value == nil && (options[:allow_nil] == false || options[:allow_blank] == false || options[:required] == true)
44
+ raise InputSanitizer::BlankValueError
45
+ elsif value == nil
46
+ value
47
+ else
48
+ Float(value).tap do |float|
49
+ raise InputSanitizer::TypeMismatchError.new(value, :float) unless float == value
50
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:minimum] && float < options[:minimum]
51
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:maximum] && float > options[:maximum]
52
+ end
53
+ end
54
+ rescue ArgumentError, TypeError
55
+ raise InputSanitizer::TypeMismatchError.new(value, :float)
56
+ end
57
+ end
58
+
59
+ class CoercingFloatCheck
60
+ def call(value, options = {})
61
+ if value == nil || value == 'null'
62
+ if options[:allow_nil] == false || options[:allow_blank] == false || options[:required] == true
63
+ raise InputSanitizer::BlankValueError
64
+ else
65
+ nil
66
+ end
67
+ else
68
+ Float(value).tap do |float|
69
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:minimum] && float < options[:minimum]
70
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:maximum] && float > options[:maximum]
71
+ end
72
+ end
73
+ rescue ArgumentError
74
+ raise InputSanitizer::TypeMismatchError.new(value, :float)
75
+ end
76
+ end
77
+
78
+ class StringCheck
79
+ def call(value, options = {})
80
+ if options[:allow] && !options[:allow].include?(value)
81
+ raise InputSanitizer::ValueNotAllowedError.new(value)
82
+ elsif value.blank? && (options[:allow_blank] == false || options[:required] == true)
83
+ raise InputSanitizer::BlankValueError
84
+ elsif options[:regexp] && options[:regexp].match(value).nil?
85
+ raise InputSanitizer::RegexpMismatchError.new
86
+ elsif value == nil && options[:allow_nil] == false
87
+ raise InputSanitizer::BlankValueError
88
+ elsif value.blank?
89
+ value
90
+ else
91
+ value.to_s.tap do |string|
92
+ raise InputSanitizer::TypeMismatchError.new(value, :string) unless string == value
93
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:minimum] && string.length < options[:minimum]
94
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:maximum] && string.length > options[:maximum]
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ class BooleanCheck
101
+ def call(value, options = {})
102
+ if value == nil
103
+ raise InputSanitizer::BlankValueError
104
+ elsif [true, false].include?(value)
105
+ value
106
+ else
107
+ raise InputSanitizer::TypeMismatchError.new(value, :boolean)
108
+ end
109
+ end
110
+ end
111
+
112
+ class CoercingBooleanCheck
113
+ def call(value, options = {})
114
+ if [true, 'true'].include?(value)
115
+ true
116
+ elsif [false, 'false'].include?(value)
117
+ false
118
+ else
119
+ raise InputSanitizer::TypeMismatchError.new(value, :boolean)
120
+ end
121
+ end
122
+ end
123
+
124
+ class DatetimeCheck
125
+ def initialize(options = {})
126
+ @check_date = options && options[:check_date]
127
+ @klass = @check_date ? Date : DateTime
128
+ end
129
+
130
+ def call(value, options = {})
131
+ raise InputSanitizer::TypeMismatchError.new(value, @check_date ? :date : :datetime) unless value == nil || value.is_a?(String)
132
+
133
+ if value.blank? && (options[:allow_blank] == false || options[:required] == true)
134
+ raise InputSanitizer::BlankValueError
135
+ elsif value == nil && options[:allow_nil] == false
136
+ raise InputSanitizer::BlankValueError
137
+ elsif value.blank?
138
+ value
139
+ else
140
+ @klass.parse(value).tap do |datetime|
141
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:minimum] && datetime < options[:minimum]
142
+ raise InputSanitizer::ValueError.new(value, options[:minimum], options[:maximum]) if options[:maximum] && datetime > options[:maximum]
143
+ end
144
+ end
145
+ rescue ArgumentError, TypeError
146
+ raise InputSanitizer::TypeMismatchError.new(value, @check_date ? :date : :datetime)
147
+ end
148
+ end
149
+
150
+ class URLCheck
151
+ def call(value, options = {})
152
+ if value.blank? && (options[:allow_blank] == false || options[:required] == true)
153
+ raise InputSanitizer::BlankValueError
154
+ elsif value == nil && options[:allow_nil] == false
155
+ raise InputSanitizer::BlankValueError
156
+ elsif value.blank?
157
+ value
158
+ else
159
+ unless /\A#{URI.regexp(%w(http https)).to_s}\z/.match(value)
160
+ raise InputSanitizer::TypeMismatchError.new(value, :url)
161
+ end
162
+ value
163
+ end
164
+ end
165
+ end
166
+
167
+ class SortByCheck
168
+ def call(value, options = {})
169
+ check_options!(options)
170
+
171
+ key, direction = split(value)
172
+ direction = 'asc' if direction.blank?
173
+
174
+ # special case when fallback takes care of separator sanitization e.g. custom fields
175
+ if options[:fallback] && !allowed_directions.include?(direction)
176
+ direction = 'asc'
177
+ key = value
178
+ end
179
+
180
+ unless valid?(key, direction, options)
181
+ raise InputSanitizer::ValueNotAllowedError.new(value)
182
+ end
183
+
184
+ [key, direction]
185
+ end
186
+
187
+ private
188
+ def valid?(key, direction, options)
189
+ allowed_keys = options[:allow]
190
+ fallback = options[:fallback]
191
+
192
+ allowed_directions.include?(direction) &&
193
+ ((allowed_keys && allowed_keys.include?(key)) ||
194
+ (fallback && fallback.call(key, direction, options)))
195
+ end
196
+
197
+ def split(value)
198
+ head, _, tail = value.to_s.rpartition(':')
199
+ head.empty? ? [tail, head] : [head, tail]
200
+ end
201
+
202
+ def check_options!(options)
203
+ fallback = options[:fallback]
204
+ if fallback && !fallback.respond_to?(:call)
205
+ raise ArgumentError, ":fallback option must respond to method :call (proc, lambda etc)"
206
+ end
207
+ end
208
+
209
+ def allowed_directions
210
+ ['asc', 'desc']
211
+ end
212
+ end
213
+ end
@@ -1,3 +1,3 @@
1
1
  module InputSanitizer
2
- VERSION = "0.1.9"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'input_sanitizer/extended_converters/comma_joined_integers_converter'
3
+
4
+ describe InputSanitizer::CommaJoinedIntegersConverter do
5
+ let(:converter) { described_class.new }
6
+
7
+ it "parses to array of ids" do
8
+ converter.call("1,2,3,5").should eq([1, 2, 3, 5])
9
+ end
10
+
11
+ it "converts to array if given an integer" do
12
+ converter.call(7).should eq([7])
13
+ end
14
+
15
+ it "raises on invalid character" do
16
+ lambda { converter.call(":") }.should raise_error(InputSanitizer::ConversionError)
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'input_sanitizer/extended_converters/comma_joined_strings_converter'
3
+
4
+ describe InputSanitizer::CommaJoinedStringsConverter do
5
+ let(:converter) { described_class.new }
6
+
7
+ it "parses to array of ids" do
8
+ converter.call("input,Sanitizer,ROCKS").should eq(["input", "Sanitizer", "ROCKS"])
9
+ end
10
+
11
+ it "allows underscores" do
12
+ converter.call("input_sanitizer,rocks").should eq(["input_sanitizer", "rocks"])
13
+ end
14
+
15
+ it "raises on invalid character" do
16
+ lambda { converter.call(":") }.should raise_error(InputSanitizer::ConversionError)
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+ require 'input_sanitizer/extended_converters/positive_integer_converter'
3
+
4
+ describe InputSanitizer::PositiveIntegerConverter do
5
+ let(:converter) { described_class.new }
6
+
7
+ it "casts string to integer" do
8
+ converter.call("3").should == 3
9
+ end
10
+
11
+ it "raises error if integer less than zero" do
12
+ lambda { converter.call("-3") }.should raise_error(InputSanitizer::ConversionError)
13
+ end
14
+
15
+ it "raises error if integer equals zero" do
16
+ lambda { converter.call("0") }.should raise_error(InputSanitizer::ConversionError)
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'input_sanitizer/extended_converters/specific_values_converter'
3
+
4
+ describe InputSanitizer::SpecificValuesConverter do
5
+ let(:converter) { described_class.new(values) }
6
+ let(:values) { [:a, :b] }
7
+
8
+ it "converts valid value to symbol" do
9
+ converter.call("b").should eq(:b)
10
+ end
11
+
12
+ it "raises on invalid value" do
13
+ lambda { converter.call("c") }.should raise_error(InputSanitizer::ConversionError)
14
+ end
15
+
16
+ it "raises on nil value" do
17
+ lambda { converter.call(nil) }.should raise_error(InputSanitizer::ConversionError)
18
+ end
19
+
20
+ context "when specific values are strings" do
21
+ let(:values) { ["a", "b"] }
22
+
23
+ it "keeps given value as string" do
24
+ converter.call("a").should eq("a")
25
+ end
26
+ end
27
+ end
@@ -1,19 +1,49 @@
1
- require "spec_helper"
1
+ require 'spec_helper'
2
2
 
3
3
  describe InputSanitizer::RestrictedHash do
4
- let(:hash) { InputSanitizer::RestrictedHash.new([:a, :b]) }
5
- subject { hash }
4
+ let(:hash) { InputSanitizer::RestrictedHash.new([:a]) }
6
5
 
7
- it "does not allow bad keys" do
8
- lambda{hash[:c]}.should raise_error(InputSanitizer::KeyNotAllowedError)
6
+ it 'does not allow bad keys' do
7
+ lambda{ hash[:b] }.should raise_error(InputSanitizer::KeyNotAllowedError)
9
8
  end
10
9
 
11
- it "does allow correct keys" do
10
+ it 'does allow correct keys' do
12
11
  hash[:a].should be_nil
13
12
  end
14
13
 
15
- it "returns value for correct key" do
14
+ it 'returns value for correct key' do
16
15
  hash[:a] = 'stuff'
17
16
  hash[:a].should == 'stuff'
18
17
  end
18
+
19
+ it 'allows to set value for a new key' do
20
+ hash[:b] = 'other stuff'
21
+ hash[:b].should == 'other stuff'
22
+ hash.key_allowed?(:b).should be_truthy
23
+ end
24
+
25
+ it 'adds new allowed keys after `transform_keys` is done' do
26
+ hash[:a] = 'stuff'
27
+ hash.transform_keys! { |key| key.to_s }
28
+ hash['a'].should == 'stuff'
29
+ hash.key_allowed?('a').should be_truthy
30
+ end
31
+
32
+ it 'removes previous allowed keys after `transform_keys` is done' do
33
+ hash[:a] = 'stuff'
34
+ hash.transform_keys! { |key| key.to_s }
35
+ lambda{ hash[:a] }.should raise_error(InputSanitizer::KeyNotAllowedError)
36
+ end
37
+
38
+ it 'updates allowed keys on merge!' do
39
+ merge_hash = { merged: '1' }
40
+ hash.merge!(merge_hash)
41
+ hash.key_allowed?(:merged).should be_truthy
42
+ end
43
+
44
+ it 'updates allowed keys on merge' do
45
+ merge_hash = { merged: '1' }
46
+ new_hash = hash.merge(some_key: merge_hash)
47
+ new_hash.key_allowed?(:some_key).should be_truthy
48
+ end
19
49
  end
@@ -1,11 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
+ class NestedSanitizer < InputSanitizer::Sanitizer
4
+ integer :foo
5
+ end
6
+
3
7
  class BasicSanitizer < InputSanitizer::Sanitizer
4
8
  string :x, :y, :z
9
+ integer :namespaced, :namespace => :value
10
+ integer :array, :collection => true
11
+ integer :namespaced_array, :collection => true, :namespace => :value
5
12
  integer :num
6
13
  date :birthday
7
14
  time :updated_at
8
15
  custom :cust1, :cust2, :converter => lambda { |v| v.reverse }
16
+ nested :stuff, :sanitizer => NestedSanitizer, :collection => true, :namespace => :nested
17
+ custom :custom3, :provide => :num, :converter => lambda { |v, num|
18
+ num == 1 ? v.reverse : v
19
+ }
9
20
  end
10
21
 
11
22
  class BrokenCustomSanitizer < InputSanitizer::Sanitizer
@@ -28,12 +39,17 @@ class RequiredCustom < BasicSanitizer
28
39
  custom :c1, :required => true, :converter => lambda { |v| v }
29
40
  end
30
41
 
42
+ class DefaultParameters < BasicSanitizer
43
+ integer :funky_number, :default => 5
44
+ custom :fixed_stuff, :converter => lambda {|v| v }, :default => "default string"
45
+ end
46
+
31
47
  describe InputSanitizer::Sanitizer do
32
48
  let(:sanitizer) { BasicSanitizer.new(@params) }
33
49
 
34
50
  describe ".clean" do
35
51
  it "returns cleaned data" do
36
- clean_data = mock()
52
+ clean_data = double
37
53
  BasicSanitizer.any_instance.should_receive(:cleaned).and_return(clean_data)
38
54
  BasicSanitizer.clean({}).should be(clean_data)
39
55
  end
@@ -57,6 +73,12 @@ describe InputSanitizer::Sanitizer do
57
73
  cleaned.should_not have_key(:d)
58
74
  end
59
75
 
76
+ it "does not include ommited fields" do
77
+ @params = { "x" => 1, "z" => 3 }
78
+
79
+ cleaned.should_not have_key(:y)
80
+ end
81
+
60
82
  it "freezes cleaned hash" do
61
83
  @params = {}
62
84
 
@@ -83,6 +105,30 @@ describe InputSanitizer::Sanitizer do
83
105
  cleaned.should_not have_key(:d)
84
106
  end
85
107
 
108
+ it "preserves namespace" do
109
+ value = { :value => 5 }
110
+ @params = { :namespaced => value }
111
+
112
+ cleaned[:namespaced].should eq(value)
113
+ end
114
+
115
+ it "maps values for collection fields" do
116
+ numbers = [3, 5, 6]
117
+ @params = { :array => numbers }
118
+
119
+ cleaned[:array].should eq(numbers)
120
+ end
121
+
122
+ it "maps values for collection fields with namespace" do
123
+ numbers = [
124
+ { :value => 2 },
125
+ { :value => 5 }
126
+ ]
127
+ @params = { :namespaced_array => numbers }
128
+
129
+ cleaned[:namespaced_array].should eq(numbers)
130
+ end
131
+
86
132
  it "silently discards cast errors" do
87
133
  @params = {:num => "f"}
88
134
 
@@ -95,7 +141,7 @@ describe InputSanitizer::Sanitizer do
95
141
 
96
142
  cleaned.should have_key(:num)
97
143
  cleaned[:num].should == 23
98
- cleaned[:is_nice].should be_false
144
+ cleaned[:is_nice].should eq(false)
99
145
  end
100
146
 
101
147
  it "overrides inherited fields" do
@@ -106,6 +152,32 @@ describe InputSanitizer::Sanitizer do
106
152
  cleaned[:is_nice].should == 42
107
153
  end
108
154
 
155
+ context "when sanitizer is initialized with default values" do
156
+ context "when paremeters are not overwriten" do
157
+ let(:sanitizer) { DefaultParameters.new({}) }
158
+
159
+ it "returns default value for non custom key" do
160
+ sanitizer.cleaned[:funky_number].should == 5
161
+ end
162
+
163
+ it "returns default value for custom key" do
164
+ sanitizer.cleaned[:fixed_stuff].should == "default string"
165
+ end
166
+ end
167
+
168
+ context "when parameters are overwriten" do
169
+ let(:sanitizer) { DefaultParameters.new({ :funky_number => 2, :fixed_stuff => "fixed" }) }
170
+
171
+ it "returns default value for non custom key" do
172
+ sanitizer.cleaned[:funky_number].should == 2
173
+ end
174
+
175
+ it "returns default value for custom key" do
176
+ sanitizer.cleaned[:fixed_stuff].should == "fixed"
177
+ end
178
+ end
179
+ end
180
+
109
181
  end
110
182
 
111
183
  describe ".custom" do
@@ -120,9 +192,53 @@ describe InputSanitizer::Sanitizer do
120
192
  end
121
193
 
122
194
  it "raises an error when converter is not defined" do
123
- expect do
195
+ lambda do
124
196
  BrokenCustomSanitizer.custom(:x)
125
- end.to raise_error
197
+ end.should raise_error
198
+ end
199
+
200
+ it "provides the converter with requested value" do
201
+ @params = { :custom3 => 'three', :num => 1 }
202
+ cleaned.should have_key(:custom3)
203
+ cleaned.should have_key(:num)
204
+ cleaned[:custom3].should eq('eerht')
205
+ cleaned[:num].should eq(1)
206
+ end
207
+ end
208
+
209
+ describe ".nested" do
210
+ let(:sanitizer) { BasicSanitizer.new(@params) }
211
+ let(:cleaned) { sanitizer.cleaned }
212
+
213
+ it "sanitizes nested values" do
214
+ nested = [
215
+ { :nested => { :foo => "5" } },
216
+ { :nested => { :foo => 8 } },
217
+ ]
218
+ @params = { :stuff => nested }
219
+
220
+ expected = [
221
+ { :nested => { :foo => 5 } },
222
+ { :nested => { :foo => 8 } },
223
+ ]
224
+ cleaned[:stuff].should eq(expected)
225
+ end
226
+
227
+ it "propagates errors" do
228
+ nested = [ { :nested => { :foo => "INVALID" } } ]
229
+ @params = { :stuff => nested }
230
+
231
+ sanitizer.should_not be_valid
232
+ end
233
+
234
+ it "returns an error when given a nil for a nested value" do
235
+ @params = { :stuff => nil }
236
+ sanitizer.should_not be_valid
237
+ end
238
+
239
+ it "returns an error when given a string for a nested value" do
240
+ @params = { :stuff => 'nope' }
241
+ sanitizer.should_not be_valid
126
242
  end
127
243
  end
128
244
 
@@ -131,43 +247,25 @@ describe InputSanitizer::Sanitizer do
131
247
 
132
248
  it "includes :integer type" do
133
249
  sanitizer.converters.should have_key(:integer)
134
- sanitizer.converters[:integer].should be_a(InputSanitizer::IntegerConverter)
250
+ sanitizer.converters[:integer].should be_a(InputSanitizer::V1::IntegerConverter)
135
251
  end
136
252
 
137
253
  it "includes :string type" do
138
254
  sanitizer.converters.should have_key(:string)
139
- sanitizer.converters[:string].should be_a(InputSanitizer::StringConverter)
255
+ sanitizer.converters[:string].should be_a(InputSanitizer::V1::StringConverter)
140
256
  end
141
257
 
142
258
  it "includes :date type" do
143
259
  sanitizer.converters.should have_key(:date)
144
- sanitizer.converters[:date].should be_a(InputSanitizer::DateConverter)
260
+ sanitizer.converters[:date].should be_a(InputSanitizer::V1::DateConverter)
145
261
  end
146
262
 
147
263
  it "includes :boolean type" do
148
264
  sanitizer.converters.should have_key(:boolean)
149
- sanitizer.converters[:boolean].should be_a(InputSanitizer::BooleanConverter)
265
+ sanitizer.converters[:boolean].should be_a(InputSanitizer::V1::BooleanConverter)
150
266
  end
151
267
  end
152
268
 
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
269
  describe '.extract_options!' do
172
270
 
173
271
  it "extracts hash from array if is last" do
@@ -234,4 +332,9 @@ describe InputSanitizer::Sanitizer do
234
332
  error[:field].should == :c1
235
333
  end
236
334
  end
335
+
336
+ it "is valid when given extra params" do
337
+ @params = { :extra => 'test' }
338
+ sanitizer.should be_valid
339
+ end
237
340
  end