Class: TaintedHash

Inherits:
Hash
  • Object
show all
Defined in:
lib/tainted_hash.rb

Defined Under Namespace

Modules: Controller, RailsMethods Classes: UnexposedError

Constant Summary collapse

VERSION =
"0.3.2"

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = nil, new_class = nil) ⇒ TaintedHash

A Tainted Hash only exposes expected keys. You can either expose them manually, or through common Hash methods like #values_at or #slice. Once created, the internal Hash is frozen from future updates.

hash - Optional Hash used internally. new_class - Optional class used to create basic Hashes. Default: Hash.


42
43
44
45
46
47
48
49
50
51
52
# File 'lib/tainted_hash.rb', line 42

def initialize(hash = nil, new_class = nil)
  @new_class = new_class || (hash && hash.class) || self.class.default_hash_class
  @original_hash = (hash && hash.dup) || @new_class.new
  @exposed_nothing = true

  @original_hash.keys.each do |key|
    key_s = key.to_s
    next if key_s == key
    @original_hash[key_s] = @original_hash.delete(key)
  end
end

Class Attribute Details

.default_hash_classObject

Returns the value of attribute default_hash_class


15
16
17
# File 'lib/tainted_hash.rb', line 15

def default_hash_class
  @default_hash_class
end

Class Method Details

.on_no_expose(&block) ⇒ Object


20
21
22
# File 'lib/tainted_hash.rb', line 20

def self.on_no_expose(&block)
  @on_no_expose = block
end

.trigger_no_expose(hash) ⇒ Object


24
25
26
# File 'lib/tainted_hash.rb', line 24

def self.trigger_no_expose(hash)
  @on_no_expose.call hash if @on_no_expose && (!block_given? || yield)
end

Instance Method Details

#[](key) ⇒ Object

Public: Gets the value for the key but does not expose the key for the Hash.

key - A String key to retrieve.

Returns the value of at the key in Hash.


112
113
114
115
116
# File 'lib/tainted_hash.rb', line 112

def [](key)
  key_s = key.to_s
  return if !@original_hash.key?(key_s)
  get_original_hash_value(key_s)
end

#[]=(key, value) ⇒ Object

Public: Attempts to set the key of a frozen hash.

key - String key to set. value - Value of the key.

Returns nothing


124
125
126
127
# File 'lib/tainted_hash.rb', line 124

def []=(key, value)
  key_s = key.to_s
  super(key_s, set_original_hash_value(key_s, value))
end

#delete(key) ⇒ Object

Public: Deletes the value from both the internal and current Hash.

key - A String key to delete.

Returns the value from the key.


134
135
136
137
138
# File 'lib/tainted_hash.rb', line 134

def delete(key)
  key_s = key.to_s
  super(key_s)
  @original_hash.delete key_s
end

#dupObject

Public: Produces a copy of the current Hash with the same set of exposed keys as the original Hash.

Returns a dup of this TaintedHash.


165
166
167
168
169
# File 'lib/tainted_hash.rb', line 165

def dup
  super.tap do |duplicate|
    duplicate.instance_variable_set :@original_hash, @original_hash.dup
  end
end

#eachObject

Public: Enumerates through the exposed keys and valuesfor the hash.

Yields the String key, and the value.

Returns nothing.


200
201
202
203
204
# File 'lib/tainted_hash.rb', line 200

def each
  self.class.trigger_no_expose(self) { @exposed_nothing && size.zero? }
  block = block_given? ? Proc.new : nil
  super(&block)
end

#expose(*keys) ⇒ Object

Public: Exposes one or more keys for the hash.

*keys - One or more String keys.

Returns this TaintedHash.


59
60
61
62
63
64
65
66
# File 'lib/tainted_hash.rb', line 59

def expose(*keys)
  @exposed_nothing = false
  keys.each do |key|
    key_s = key.to_s
    self[key_s] = @original_hash[key_s] if @original_hash.key?(key_s)
  end
  self
end

#expose_allObject

Public: Exposes every key of the hash.

Returns this TaintedHash.


71
72
73
74
75
76
77
# File 'lib/tainted_hash.rb', line 71

def expose_all
  @exposed_nothing = false
  @original_hash.each do |key, value|
    self[key] = value
  end
  self
end

#extra_keysObject

Public: Gets the unexposed keys from the original Hash.

Returns an Array of String keys.


82
83
84
# File 'lib/tainted_hash.rb', line 82

def extra_keys
  @original_hash.keys - self.keys
end

#fetch(key, *default) ⇒ Object

Public: Fetches the value in the hash at key, or a sensible default.

key - A String key to retrieve. default - A sensible default.

Returns the value of the key, or the default.

Raises:

  • (ArgumentError)

92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/tainted_hash.rb', line 92

def fetch(key, *default)
  raise ArgumentError, "wrong number of arguments (#{default.size + 1} for 1..2)" if default.size > 1

  key_s = key.to_s
  if @original_hash.key?(key_s)
    self[key_s]
  elsif block_given?
    yield
  elsif !default.empty?
    default[0]
  else
    raise KeyError, "key not found: #{key}"
  end
end

#include?(key) ⇒ Boolean Also known as: key?, has_key?

Public: Checks whether the given key has been exposed or not.

key - A String key.

Returns true if exposed, or false.

Returns:

  • (Boolean)

145
146
147
# File 'lib/tainted_hash.rb', line 145

def include?(key)
  super(key.to_s)
end

#inspectObject


228
229
230
# File 'lib/tainted_hash.rb', line 228

def inspect
  %(#<#{self.class}:#{object_id} @hash=#{@original_hash.inspect} @exposed=#{keys.inspect}>)
end

#merge(hash) ⇒ Object

Public: Merges the given hash with the internal and a dup of the current Hash.

hash - A Hash with String keys.

Returns a dup of this TaintedHash.


177
178
179
# File 'lib/tainted_hash.rb', line 177

def merge(hash)
  dup.update(hash)
end

#nested_under_indifferent_accessObject


224
225
226
# File 'lib/tainted_hash.rb', line 224

def nested_under_indifferent_access
  self
end

#original_hashObject

Public: Gets the original hash that is being wrapped.

Returns a Hash.


31
32
33
# File 'lib/tainted_hash.rb', line 31

def original_hash
  untaint_original_hash(@original_hash)
end

#to_hashObject

Public: Builds a normal Hash of the exposed values from this hash.

Returns a Hash.


209
210
211
212
213
214
215
216
217
218
# File 'lib/tainted_hash.rb', line 209

def to_hash
  hash = @new_class.new
  each do |key, value|
    hash[key] = case value
      when TaintedHash then value.to_hash
      else value
      end
  end
  hash
end

#update(hash) ⇒ Object Also known as: merge!

Public: Updates the internal and current Hash with the given Hash.

hash - A Hash with String keys.

Returns this TaintedHash.


186
187
188
189
190
191
# File 'lib/tainted_hash.rb', line 186

def update(hash)
  hash.each do |key, value|
    self[key.to_s] = value
  end
  self
end

#values_at(*keys) ⇒ Object

Public: Returns the values for the given keys, and exposes the keys.

*keys - One or more String keys.

Returns an Array of the values (or nil if there is no value) for the keys.


157
158
159
# File 'lib/tainted_hash.rb', line 157

def values_at(*keys)
  keys.map { |k| self[k] }
end

#with_indifferent_accessObject


220
221
222
# File 'lib/tainted_hash.rb', line 220

def with_indifferent_access
  self
end