tainted_hash 0.2.0 → 0.3.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.
@@ -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
@@ -1,7 +1,5 @@
1
- require 'set'
2
-
3
1
  class TaintedHash < Hash
4
- VERSION = "0.2.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
- attr_reader :original_hash
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 = nil)
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.fetch key_s, default
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, and exposes the key for the Hash.
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
- @original_hash.values_at *keys.map { |k| k.to_s }
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
- @original_hash[key.to_s] = value
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
- str_keys = @original_hash.keys & keys.map { |k| k.to_s }
233
- hash = self.class.new
234
- str_keys.each do |key|
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
- raise NotImplementedError
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
@@ -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.2.0'
16
- s.date = '2013-07-16'
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
@@ -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.expose :b
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 !@tainted.include?(:a)
141
- assert !@tainted.include?(:d)
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.2.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-07-16 00:00:00.000000000 Z
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: 1.8.23
59
+ rubygems_version: 2.0.3
64
60
  signing_key:
65
61
  specification_version: 2
66
62
  summary: Hash wrapper.