tainted_hash 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/tainted_hash.rb +55 -17
- data/tainted_hash.gemspec +2 -2
- data/test/tainted_hash_test.rb +47 -4
- metadata +8 -12
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ea0d328f383629e28c37a106bc53f7f320fdb827
|
4
|
+
data.tar.gz: 2099a31bf32527f818449a86a9b5774cc24fa24a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 78e0d10bc3b4247e96e172672f1e6f625e7539c298910317ba4ba8bec9f6e88bcf64c2eef690ffe4fa3b025c9e202f819eafa28df1c8a7fcb877ed540288b522
|
7
|
+
data.tar.gz: fd2e58e7e2548c5fc1cdc2c2770219c445f4d4a0663e7e84812e5e0aea7ea02df868b5bfc106d0fa54d307b76805c19ad65a83990e9eb20d5773201e2c9ef89c
|
data/lib/tainted_hash.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
1
|
class TaintedHash < Hash
|
4
|
-
VERSION = "0.
|
2
|
+
VERSION = "0.3.0"
|
5
3
|
|
6
4
|
class UnexposedError < StandardError
|
7
5
|
# Builds an exception when a TaintedHash has some unexposed keys. Useful
|
@@ -30,7 +28,9 @@ class TaintedHash < Hash
|
|
30
28
|
# Public: Gets the original hash that is being wrapped.
|
31
29
|
#
|
32
30
|
# Returns a Hash.
|
33
|
-
|
31
|
+
def original_hash
|
32
|
+
untaint_original_hash(@original_hash)
|
33
|
+
end
|
34
34
|
|
35
35
|
# A Tainted Hash only exposes expected keys. You can either expose them
|
36
36
|
# manually, or through common Hash methods like #values_at or #slice. Once
|
@@ -41,7 +41,7 @@ class TaintedHash < Hash
|
|
41
41
|
#
|
42
42
|
def initialize(hash = nil, new_class = nil)
|
43
43
|
@new_class = new_class || (hash && hash.class) || self.class.default_hash_class
|
44
|
-
@original_hash = hash || @new_class.new
|
44
|
+
@original_hash = (hash && hash.dup) || @new_class.new
|
45
45
|
@exposed_nothing = true
|
46
46
|
|
47
47
|
@original_hash.keys.each do |key|
|
@@ -89,12 +89,22 @@ class TaintedHash < Hash
|
|
89
89
|
# default - A sensible default.
|
90
90
|
#
|
91
91
|
# Returns the value of the key, or the default.
|
92
|
-
def fetch(key, default
|
92
|
+
def fetch(key, *default)
|
93
|
+
raise ArgumentError, "wrong number of arguments (#{default.size + 1} for 1..2)" if default.size > 1
|
94
|
+
|
93
95
|
key_s = key.to_s
|
94
|
-
@original_hash.
|
96
|
+
if @original_hash.key?(key_s)
|
97
|
+
self[key_s]
|
98
|
+
elsif block_given?
|
99
|
+
yield
|
100
|
+
elsif !default.empty?
|
101
|
+
default[0]
|
102
|
+
else
|
103
|
+
raise KeyError, "key not found: #{key}"
|
104
|
+
end
|
95
105
|
end
|
96
106
|
|
97
|
-
# Public: Gets the value for the key
|
107
|
+
# Public: Gets the value for the key but does not expose the key for the Hash.
|
98
108
|
#
|
99
109
|
# key - A String key to retrieve.
|
100
110
|
#
|
@@ -102,7 +112,6 @@ class TaintedHash < Hash
|
|
102
112
|
def [](key)
|
103
113
|
key_s = key.to_s
|
104
114
|
return if !@original_hash.key?(key_s)
|
105
|
-
|
106
115
|
get_original_hash_value(key_s)
|
107
116
|
end
|
108
117
|
|
@@ -138,6 +147,7 @@ class TaintedHash < Hash
|
|
138
147
|
end
|
139
148
|
|
140
149
|
alias key? include?
|
150
|
+
alias has_key? include?
|
141
151
|
|
142
152
|
# Public: Returns the values for the given keys, and exposes the keys.
|
143
153
|
#
|
@@ -145,7 +155,17 @@ class TaintedHash < Hash
|
|
145
155
|
#
|
146
156
|
# Returns an Array of the values (or nil if there is no value) for the keys.
|
147
157
|
def values_at(*keys)
|
148
|
-
|
158
|
+
keys.map { |k| self[k] }
|
159
|
+
end
|
160
|
+
|
161
|
+
# Public: Produces a copy of the current Hash with the same set of exposed
|
162
|
+
# keys as the original Hash.
|
163
|
+
#
|
164
|
+
# Returns a dup of this TaintedHash.
|
165
|
+
def dup
|
166
|
+
super.tap do |duplicate|
|
167
|
+
duplicate.instance_variable_set :@original_hash, @original_hash.dup
|
168
|
+
end
|
149
169
|
end
|
150
170
|
|
151
171
|
# Public: Merges the given hash with the internal and a dup of the current
|
@@ -165,7 +185,7 @@ class TaintedHash < Hash
|
|
165
185
|
# Returns this TaintedHash.
|
166
186
|
def update(hash)
|
167
187
|
hash.each do |key, value|
|
168
|
-
|
188
|
+
self[key.to_s] = value
|
169
189
|
end
|
170
190
|
self
|
171
191
|
end
|
@@ -218,6 +238,24 @@ private
|
|
218
238
|
@original_hash[key_s] = value
|
219
239
|
end
|
220
240
|
|
241
|
+
# Private: Returns a regular Hash, transforming all embedded TaintedHash
|
242
|
+
# objects into regular Hash objects with all keys exposed.
|
243
|
+
#
|
244
|
+
# original_hash - The @original_hash you want to untaint
|
245
|
+
#
|
246
|
+
#
|
247
|
+
# Returns a Hash
|
248
|
+
def untaint_original_hash(original_hash)
|
249
|
+
hash = @new_class.new
|
250
|
+
original_hash.each do |key, value|
|
251
|
+
hash[key] = case value
|
252
|
+
when TaintedHash then untaint_original_hash(value.instance_variable_get :@original_hash)
|
253
|
+
else value
|
254
|
+
end
|
255
|
+
end
|
256
|
+
hash
|
257
|
+
end
|
258
|
+
|
221
259
|
module RailsMethods
|
222
260
|
def self.included(base)
|
223
261
|
base.send :alias_method, :stringify_keys!, :stringify_keys
|
@@ -229,16 +267,16 @@ private
|
|
229
267
|
#
|
230
268
|
# Returns a Hash of the requested keys and values.
|
231
269
|
def slice(*keys)
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
hash[key] = @original_hash[key]
|
270
|
+
keys.each_with_object(self.class.new) do |key, hash|
|
271
|
+
key_s = key.to_s
|
272
|
+
hash[key_s] = @original_hash[key_s] if @original_hash.key?(key_s)
|
236
273
|
end
|
237
|
-
hash
|
238
274
|
end
|
239
275
|
|
240
276
|
def slice!(*keys)
|
241
|
-
|
277
|
+
str_keys = keys.map { |k| k.to_s }
|
278
|
+
(@original_hash.keys - str_keys).each { |key| delete(key) }
|
279
|
+
expose(*str_keys)
|
242
280
|
end
|
243
281
|
|
244
282
|
def stringify_keys
|
data/tainted_hash.gemspec
CHANGED
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
## If your rubyforge_project name is different, then edit it and comment out
|
13
13
|
## the sub! line in the Rakefile
|
14
14
|
s.name = 'tainted_hash'
|
15
|
-
s.version = '0.
|
16
|
-
s.date = '2013-
|
15
|
+
s.version = '0.3.0'
|
16
|
+
s.date = '2013-10-09'
|
17
17
|
s.rubyforge_project = 'tainted_hash'
|
18
18
|
|
19
19
|
## Make sure your summary is short. The description may be as long
|
data/test/tainted_hash_test.rb
CHANGED
@@ -29,12 +29,36 @@ class TaintedHashTest < Test::Unit::TestCase
|
|
29
29
|
assert @hash.include?('c')
|
30
30
|
end
|
31
31
|
|
32
|
+
def test_merge
|
33
|
+
@tainted.expose(:a)
|
34
|
+
merged = @tainted.merge('d' => 3)
|
35
|
+
assert_equal %w(a), @tainted.keys.sort
|
36
|
+
assert_equal %w(a d), merged.keys.sort
|
37
|
+
assert_equal %w(a b c), @tainted.original_hash.keys.sort
|
38
|
+
assert_equal %w(a b c d), merged.original_hash.keys.sort
|
39
|
+
end
|
40
|
+
|
32
41
|
def test_dup
|
33
|
-
@tainted[:c].expose :name
|
34
42
|
@tainted.expose :a
|
35
|
-
dup = @tainted.dup
|
43
|
+
dup = @tainted.dup
|
44
|
+
dup.expose :b
|
36
45
|
assert_equal %w(a), @tainted.keys.sort
|
37
46
|
assert_equal %w(a b), dup.keys.sort
|
47
|
+
@tainted[:d] = 3
|
48
|
+
dup[:e] = 4
|
49
|
+
assert_equal %w(a d), @tainted.keys.sort
|
50
|
+
assert_equal %w(a b e), dup.keys.sort
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_expose_all
|
54
|
+
@tainted.expose_all
|
55
|
+
assert_equal %w(a b c), @tainted.keys.sort
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_original_hash
|
59
|
+
assert_equal @hash, @tainted.original_hash
|
60
|
+
assert_equal @hash.merge('d' => 3), @tainted.merge('d' => 3).original_hash
|
61
|
+
assert_equal @hash, @tainted.expose_all.original_hash
|
38
62
|
end
|
39
63
|
|
40
64
|
def test_expose_keys
|
@@ -50,6 +74,10 @@ class TaintedHashTest < Test::Unit::TestCase
|
|
50
74
|
assert !@tainted.include?(:d)
|
51
75
|
assert_equal 1, @tainted.fetch(:a, :default)
|
52
76
|
assert_equal :default, @tainted.fetch(:d, :default)
|
77
|
+
assert_raises KeyError do
|
78
|
+
@tainted.fetch(:d)
|
79
|
+
end
|
80
|
+
assert_equal :default, @tainted.fetch(:d) { :default }
|
53
81
|
assert !@tainted.include?(:a)
|
54
82
|
assert !@tainted.include?(:d)
|
55
83
|
end
|
@@ -137,8 +165,8 @@ class TaintedHashTest < Test::Unit::TestCase
|
|
137
165
|
assert !@tainted.include?(:a)
|
138
166
|
assert !@tainted.include?(:d)
|
139
167
|
@tainted.update :a => 2, :d => 1
|
140
|
-
assert
|
141
|
-
assert
|
168
|
+
assert @tainted.include?(:a)
|
169
|
+
assert @tainted.include?(:d)
|
142
170
|
assert_equal 2, @tainted[:a]
|
143
171
|
assert_equal 1, @tainted[:d]
|
144
172
|
end
|
@@ -188,4 +216,19 @@ class TaintedHashTest < Test::Unit::TestCase
|
|
188
216
|
slice = @tainted.slice :a, :d
|
189
217
|
assert_equal({'a' => 1}, slice.to_hash)
|
190
218
|
end
|
219
|
+
|
220
|
+
def test_slice_bang_modifies_self
|
221
|
+
@tainted.slice! :a, :d
|
222
|
+
assert_equal({'a' => 1}, @tainted.to_hash)
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_include_aliases
|
226
|
+
assert !@tainted.include?(:a)
|
227
|
+
assert !@tainted.has_key?(:a)
|
228
|
+
assert !@tainted.key?(:a)
|
229
|
+
@tainted[:a] = 2
|
230
|
+
assert @tainted.include?(:a)
|
231
|
+
assert @tainted.has_key?(:a)
|
232
|
+
assert @tainted.key?(:a)
|
233
|
+
end
|
191
234
|
end
|
metadata
CHANGED
@@ -1,30 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tainted_hash
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.3.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Rick Olson
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-10-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: test-unit
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
description: Hash wrapper.
|
@@ -42,25 +39,24 @@ files:
|
|
42
39
|
- test/tainted_hash_test.rb
|
43
40
|
homepage: https://github.com/technoweenie/tainted_hash
|
44
41
|
licenses: []
|
42
|
+
metadata: {}
|
45
43
|
post_install_message:
|
46
44
|
rdoc_options: []
|
47
45
|
require_paths:
|
48
46
|
- lib
|
49
47
|
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
48
|
requirements:
|
52
|
-
- -
|
49
|
+
- - '>='
|
53
50
|
- !ruby/object:Gem::Version
|
54
51
|
version: '0'
|
55
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
-
none: false
|
57
53
|
requirements:
|
58
|
-
- -
|
54
|
+
- - '>='
|
59
55
|
- !ruby/object:Gem::Version
|
60
56
|
version: 1.3.5
|
61
57
|
requirements: []
|
62
58
|
rubyforge_project: tainted_hash
|
63
|
-
rubygems_version:
|
59
|
+
rubygems_version: 2.0.3
|
64
60
|
signing_key:
|
65
61
|
specification_version: 2
|
66
62
|
summary: Hash wrapper.
|