Class: Octothorpe

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/octothorpe.rb

Overview

A very simple hash-like class that borrows a little from OpenStruct, etc.

  • Treats string and symbol keys as equal

  • Access member objects with ot.>>.keyname

  • Guard conditions allow you to control what returns if key is not present

  • Pretty much read-only, for better or worse

Meant to facilitate message-passing between classes.

Simple example:

ot = Octothorpe.new(one: 1, "two" => 2, "weird key" => 3)
ot.>>.one            # -> 1
ot.>>.two            # -> 2
ot.get("weird key")  # -> 3

With guard conditions:

ot = Octotghorpe.new(one: 1, "two" => 2)
ot.guard(Array, :three)
ot.freeze       # optional step - makes OT truly read-only
ot.>>.three     # -> [] 
ot.>>.three[9]  # valid (of course; returns nil)

Octothorpe additionally responds to the following methods exactly as a Hash would:

empty?, has_key?, has_value?, include?
each,   each_key, each_value, keys,    values
select, map,      reject,     inject
merge,  <, >< ==, >+, <=

Defined Under Namespace

Classes: BadHash, Frozen, OctoError, Storage

Constant Summary collapse

VERSION =

Gem version number

'0.4.1'

Instance Method Summary collapse

Constructor Details

#initialize(hash = nil) ⇒ Octothorpe

:call-seq:

ot = Octothrpe.new(hash)

Initialise an Octothorpe object by passing it a hash.

You can create an empty OT by calling Octothorpe.new, but there’s probably little utility in that, given that it is read-only.

If you pass anything other than nil or something OT can treat as a Hash, you will cause an Octothorpe::BadHash exception.



89
90
91
92
# File 'lib/octothorpe.rb', line 89

def initialize(hash=nil)
  @store = Storage.new( symbol_hash(hash || {}) )
  @inner_hash = @store.octothorpe_store
end

Instance Method Details

#<(other) ⇒ Object



208
# File 'lib/octothorpe.rb', line 208

def <(other);  compare_as_hash(other, :<);  end

#<=(other) ⇒ Object



211
# File 'lib/octothorpe.rb', line 211

def <=(other); compare_as_hash(other, :<=); end

#==(other) ⇒ Object

Resolve some of the standard comparisons (with an OT or a hash)



207
# File 'lib/octothorpe.rb', line 207

def ==(other); compare_as_hash(other, :==); end

#>(other) ⇒ Object



209
# File 'lib/octothorpe.rb', line 209

def >(other);  compare_as_hash(other, :>);  end

#>=(other) ⇒ Object



210
# File 'lib/octothorpe.rb', line 210

def >=(other); compare_as_hash(other, :>=); end

#>>Object

:call-seq:

ot.>>.keyname

You can use >> to access member objects in somewhat the same way as an OpenStruct.

ot = Octothorpe.new(one: 1, "two" => 2)
ot.>>.one  # -> 1

This will not work for members that have keys with spaces in, keys which have the same name as methods on Object, or keys that aren’t String or Symbol. Use get for those.



107
# File 'lib/octothorpe.rb', line 107

def >>; @store; end

#get(key) ⇒ Object Also known as: send, []

:call-seq:

ot.get(key)
ot.send(key)
ot[key]

You can use get to access member object values instead of the >> syntax.

Unlike >>, this works for keys with spaces, or keys that have the same name as methods on Object.



121
# File 'lib/octothorpe.rb', line 121

def get(key); @store.octothorpe_store[octokey key]; end

#guard(*args) ⇒ Object

:call-seq:

ot.guard( class, key [, key, ...] )
ot.guard( key, [,key, ...] ) {|k| ... }

Guarantees the initial state of a memnber. Each key that is not already present will be set to <class>.new. Has no effect if key is already present. Class must be some class Thing that can respond to a vanilla Thing.new.

Alternatively, for the block form, the key is passed to the block, and the value of the key becomes the return value of the block … but again, ONLY if the key is not already set.

Note that this is the only time that you can modify an Octothorpe object once it is created. If you call freeze on an it, it will become genuinely read-only, and any call to guard from then on will raise Octothorpe::Frozen.

Raises:



149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/octothorpe.rb', line 149

def guard(*args)
  raise Frozen if self.frozen?

  klass = args.shift unless block_given?
  keys  = args.map{|k| octokey k}

  if block_given?
    keys.each{|k| @store.octothorpe_store[k] ||= yield k }
  else
    keys.each{|k| @store.octothorpe_store[k] ||= klass.new }
  end

  self
end

#inspectObject

Inspect exposes a view of the inner hash



217
218
219
# File 'lib/octothorpe.rb', line 217

def inspect
  "#<Octothorpe#{@store.octothorpe_store.inspect}>"
end

#merge(other) ⇒ Object

:call-seq:

ot.merge(other)                              -> new_ot
ot.merge(other){|key, oldval, newval| block} -> new_ot

Exactly as Hash.merge, but returns a new Octothorpe object.

You may pass a hash or an octothorpe. Raises Octothorpe::BadHash if it is anything else.



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/octothorpe.rb', line 174

def merge(other)
  thisHash  = @store.octothorpe_store
  otherHash = symbol_hash(other)

  merged = 
    if block_given?
      thisHash.merge(otherHash) {|key,old,new| yield key, old, new }
    else
      thisHash.merge(otherHash)
    end

  Octothorpe.new(merged)
end

#to_hObject

Returns a hash of the object.



130
# File 'lib/octothorpe.rb', line 130

def to_h; @store.octothorpe_store; end

#whitelist(*keys) ⇒ Object

:call-seq:

ot.whitelist(:one, :two, :three) -> new_ot

Return an Octothorpe containing only these keys.

If you name a key that is missing, that key will also be missing in the output; use Guard if that’s not what you want.



198
199
200
# File 'lib/octothorpe.rb', line 198

def whitelist(*keys)
  Octothorpe.new @inner_hash.select{|k,_| symbol_hash(keys).include? k}
end