Skip to content

Commit

Permalink
Wrap Api#attributes with indifferent key access
Browse files Browse the repository at this point in the history
A number of bugs have been introduced or missed due to the way
attributes are exposed by fog. Fog transforms the top level of
attributes from the JSON sources (String) to Symbols, but does not act
recursively.

This results in accessing nested structures with a mix of Symbol and
String keys which inevitably is missed or incorrectly specced out.

This adds a simple wrapper around `Api#attributes` that takes the fog
attributes and allows indifferent access with Symbols or Strings.
  • Loading branch information
tokengeek committed Dec 19, 2024
1 parent 50c3986 commit 70f172c
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 0 deletions.
4 changes: 4 additions & 0 deletions lib/brightbox-cli/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ def initialize(model = nil)
Brightbox.config.cache_id(@id) if Brightbox.config.respond_to?(:cache_id)
end

def attributes
IndifferentAccessHash.new(fog_model.attributes)
end

def fog_model
@fog_model ||= self.class.find(@id)
end
Expand Down
50 changes: 50 additions & 0 deletions lib/brightbox-cli/indifferent_access_hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# A simpler wrapper to allows either String or Symbol keys to be used
# when accessing attributes since fog applies a change on the top
# level resulting in a mix of both which has introduced issues.
class IndifferentAccessHash
def initialize(hash)
@hash = hash
end

# @param key [String, Symbol] the key to look up
# @return [Object] the value of the key
def [](key)
value = @hash[key.to_s] || @hash[key.to_sym]
wrap(value)
end

# @param key [String, Symbol] the key to set
# @param value [Object] the value to set
# @return [Object] the value of the key
def []=(key, value)
@hash[key.to_s] = value
end

# @param other [Object] the object to compare
# @return [Object] the result of the comparison
def ==(other)
@hash == (other.is_a?(IndifferentAccessHash) ? other.to_h : other)
end

def method_missing(method, *args, &block)
@hash.send(method, *args, &block)
end

def to_h
@hash
end

private

# This is to handle nested hashes to avoid the original issue again
def wrap(value)
case value
when Hash
IndifferentAccessHash.new(value)
when Array
value.map { |v| wrap(v) }
else
value
end
end
end
1 change: 1 addition & 0 deletions lib/brightbox_cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ module Config
require_relative "brightbox-cli/connection_manager"
require_relative "brightbox-cli/tables"
require_relative "brightbox-cli/logging"
require_relative "brightbox-cli/indifferent_access_hash"
require_relative "brightbox-cli/api"
require_relative "brightbox-cli/config/cache"
require_relative "brightbox-cli/config/gpg_encrypted_passwords"
Expand Down

0 comments on commit 70f172c

Please sign in to comment.