class Kielce::KielceData

By extending BasicObject instead of Object, we don't have to worry about user data conflicting with method names in Object or Kernel

@xx_kielce_data is the hash passed to the constructor. @xx_kielce_obj_name is the key that maps to the value. @xx_kielce_root refers to the root object. It is used so that lambdas have access to the data.

(The strange “xx_kielce_” naming is to avoid conflicts with user-chosen names)

Public Class Methods

error_output() click to toggle source
# File lib/kielce/kielce_data.rb, line 58
def self.error_output
  @@error_output
end
error_output=(val) click to toggle source
# File lib/kielce/kielce_data.rb, line 62
def self.error_output=(val)
  @@error_output=val
end
new(data, root = nil, obj_name = nil) click to toggle source
# File lib/kielce/kielce_data.rb, line 66
def initialize(data, root = nil, obj_name = nil)
  
  INVALID_KEYS.each do |key|
    ::Kernel.send(:raise, InvalidKeyError.new("Invalid Key: #{key} may not be used as a key.", key)) if data.has_key?(key)
  end

  @xx_kielce_obj_name = obj_name.nil? ? "" : obj_name

  @xx_kielce_data = data

  # Remember, root may be a BasicObject and, therefore, not define .nil
  @xx_kielce_root = (root == nil) ? self : root

  @xx_kielce_data.each do |key, value|
    if value.is_a?(::Hash)
      @xx_kielce_data[key] = ::Kielce::KielceData.new(value, @xx_kielce_root, "#{@xx_kielce_obj_name}#{key}.")
    end
  end
end

Public Instance Methods

inspect() click to toggle source
# File lib/kielce/kielce_data.rb, line 90
def inspect
  "KielceData: #{@xx_kielce_obj_name} #{@xx_kielce_data.inspect}"
end
is_a?(klass) click to toggle source
# File lib/kielce/kielce_data.rb, line 94
def is_a?(klass)
  klass == ::Kielce::KielceData
end
method_missing(name, *args, **keyword_args, &block) click to toggle source

Provides the “magic” that allows users to access data using method syntax. This method is called whenever a method that doesn't exist is invoked. It then looks through the hash for a key with the same name as the method.

# File lib/kielce/kielce_data.rb, line 101
def method_missing(name, *args, **keyword_args, &block)

  # $stderr.puts "Processing #{name} in #{@xx_kielce_obj_name}"

  # Convert the name to a symbol. (It is probably already a symbol, but the extra .to_sym won't hurt)
  # Then complian if there isn't a data object by that name.
  name_sym = name.to_sym
  full_name = "#{@xx_kielce_obj_name}#{name}"
  unless @xx_kielce_data.has_key?(name_sym)
    # The first ("message") parameter is currently unused.  The message can be changed, if desired.
    ::Kernel.send(:raise, NoKeyError.new("Unrecognized Key: #{full_name}", full_name))
  end

  # Get the requested data object.
  # If the object is a lambda, execute the lambda.
  # Otherwise, just return it.
  item = @xx_kielce_data[name_sym]
  if item.is_a?(::Proc)
    if item.parameters.any? { |i| i.last == :root }
      @@error_output.puts 'WARNING! Lambda parameter named root shadows instance method root.'
    end

    #$stderr.puts item.parameters.inspect
    keyword_params = item.parameters.select { |i| i.first == :keyreq || i.first == :key }
    num_keyword = keyword_params.size
    num_args = item.parameters.size - num_keyword

    #$stderr.puts "-----------"
    #$stderr.puts args.inspect
    #$stderr.puts keyword_args.inspect
    #$stderr.puts "Num each: #{num_args} #{num_keyword}"

    if num_args == 0 && num_keyword == 0
      return instance_exec(&item)
    elsif num_args > 0 && num_keyword == 0
      return instance_exec(*args, &item)
    elsif num_keyword > 0
      return instance_exec(*args, **keyword_args, &item)
    else
      $stderr.puts "FAIL.  Shouldn't get here!"
    end
  end

  if args.length != 0 || keyword_args.size != 0
    @@error_output.puts "WARNING! #{full_name} is not a function and doesn't expect parameters."
  end

  @xx_kielce_data[name_sym]
end
root() click to toggle source
# File lib/kielce/kielce_data.rb, line 86
def root
  @xx_kielce_root
end