class Barrister::Contract

Represents a single parsed IDL definition

Attributes

idl[RW]
meta[RW]

Public Class Methods

new(idl) click to toggle source

‘idl` must be an Array loaded from a Barrister IDL JSON file

‘initialize` iterates through the IDL and stores the interfaces, structs, and enums specified in the IDL

# File lib/barrister.rb, line 579
def initialize(idl)
  @idl = idl
  @interfaces = { }
  @structs    = { }
  @enums      = { }
  @meta       = { }

  idl.each do |item|
    type = item["type"]
    if type == "interface"
      @interfaces[item["name"]] = Interface.new(item)
    elsif type == "struct"
      @structs[item["name"]] = item
    elsif type == "enum"
      @enums[item["name"]] = item
    elsif type == "meta"
      item.keys.each do |key|
        if key != "type"
          @meta[key] = item[key]
        end
      end
    end
  end
end

Public Instance Methods

all_struct_fields(arr, struct) click to toggle source

Recursively resolves all fields for the struct and its ancestors

Returns an Array with all the fields

# File lib/barrister.rb, line 817
def all_struct_fields(arr, struct)
  struct["fields"].each do |f|
    arr << f
  end

  if struct["extends"]
    parent = @structs[struct["extends"]]
    if parent
      return all_struct_fields(arr, parent)
    end
  end

  return arr
end
interface(name) click to toggle source

Returns the Interface instance for the given name

# File lib/barrister.rb, line 605
def interface(name)
  return @interfaces[name]
end
interfaces() click to toggle source

Returns all Interfaces defined on this Contract

# File lib/barrister.rb, line 610
def interfaces
  return @interfaces.values
end
resolve_method(req) click to toggle source

Takes a JSON-RPC request hash, and returns a 3 element tuple. This is called as part of the request validation sequence.

‘0` - JSON-RPC response hash representing an error. nil if valid. `1` - Interface instance on this Contract that matches `req` `2` - Function instance on the Interface that matches `req`

# File lib/barrister.rb, line 620
def resolve_method(req)
  method = req["method"]
  iface_name, func_name = Barrister::parse_method(method)
  if iface_name == nil
    return err_resp(req, -32601, "Method not found: #{method}")
  end
  
  iface = interface(iface_name)
  if !iface
    return err_resp(req, -32601, "Interface not found on IDL: #{iface_name}")
  end

  func = iface.function(func_name)
  if !func
    return err_resp(req, -32601, "Function #{func_name} does not exist on interface #{iface_name}")
  end
  
  return nil, iface, func
end
type_err(name, exp_type, val) click to toggle source

Helper function that returns a formatted string for a type mismatch error

# File lib/barrister.rb, line 833
def type_err(name, exp_type, val)
  actual = val.class.name
  return "#{name} expects type '#{exp_type}' but got type '#{actual}'"
end
validate(name, expected, expect_array, val) click to toggle source

Validates the type for a single value. This method is recursive when validating arrays or structs.

Returns a string describing the validation error if invalid, or nil if valid

  • ‘name` - string to prefix onto the validation error

  • ‘expected` - expected type (hash)

  • ‘expect_array` - if true, we expect val to be an Array

  • ‘val` - value to validate

# File lib/barrister.rb, line 700
def validate(name, expected, expect_array, val)
  # If val is nil, then check if the IDL allows this type to be optional
  if val == nil
    if expected["optional"]
      return nil
    else
      return "#{name} cannot be null"
    end
  else
    exp_type = expected["type"]

    # If we expect an array, make sure that val is an Array, and then
    # recursively validate the elements in the array
    if expect_array
      if val.kind_of?(Array)
        stop = val.length - 1
        for i in (0..stop)
          invalid = validate("#{name}[#{i}]", expected, false, val[i])
          if invalid != nil
            return invalid
          end
        end

        return nil
      else
        return type_err(name, "[]"+expected["type"], val)
      end
      
    # Check the built in Barrister primitive types
    elsif exp_type == "string"
      if val.class == String
        return nil
      else
        return type_err(name, exp_type, val)
      end
    elsif exp_type == "bool"
      if val.class == TrueClass || val.class == FalseClass
        return nil
      else
        return type_err(name, exp_type, val)
      end
    elsif exp_type == "int" || exp_type == "float"
      if val.class == Integer || val.class == Fixnum || val.class == Bignum
        return nil
      elsif val.class == Float && exp_type == "float"
        return nil
      else
        return type_err(name, exp_type, val)
      end
      
    # Expected type is not an array or a Barrister primitive.
    # It must be a struct or an enum.
    else
      
      # Try to find a struct
      struct = @structs[exp_type]
      if struct
        if !val.kind_of?(Hash)
          return "#{name} #{exp_type} value must be a map/hash. not: " + val.class.name
        end
        
        s_field_keys = { }
        
        # Resolve all fields on the struct and its ancestors
        s_fields = all_struct_fields([], struct)
        
        # Validate that each field on the struct has a valid value
        s_fields.each do |f|
          fname = f["name"]
          invalid = validate("#{name}.#{fname}", f, f["is_array"], val[fname])
          if invalid != nil
            return invalid
          end
          s_field_keys[fname] = 1
        end
        
        # Validate that there are no extraneous elements on the value
        val.keys.each do |k|
          if !s_field_keys.key?(k)
            return "#{name}.#{k} is not a field in struct '#{exp_type}'"
          end
        end
        
        # Struct is valid
        return nil
      end

      # Try to find an enum
      enum = @enums[exp_type]
      if enum
        if val.class != String
          return "#{name} enum value must be a string. got: " + val.class.name
        end

        # Try to find an enum value that matches this val
        enum["values"].each do |en|
          if en["value"] == val
            return nil
          end
        end

        # Invalid
        return "#{name} #{val} is not a value in enum '#{exp_type}'"
      end

      # Unlikely branch - suggests the IDL is internally inconsistent
      return "#{name} unknown type: #{exp_type}"
    end

    # Panic if we have a branch unaccounted for. Indicates a Barrister bug.
    raise "Barrister ERROR: validate did not return for: #{name} #{expected}"
  end
end
validate_params(req, func) click to toggle source

Validates that the parameters on the JSON-RPC request match the types specified for this function

Returns a JSON-RPC response hash if invalid, or nil if valid.

# File lib/barrister.rb, line 648
def validate_params(req, func)
  params = req["params"]
  if !params
    params = []
  end
  e_params  = func.params.length
  r_params  = params.length
  if e_params != r_params
    msg = "Function #{func.name}: Param length #{r_params} != expected length: #{e_params}"
    return err_resp(req, -32602, msg)
  end

  for i in (0..(e_params-1))
    expected = func.params[i]
    invalid = validate("Param[#{i}]", expected, expected["is_array"], params[i])
    if invalid != nil
      return err_resp(req, -32602, invalid)
    end
  end

  # valid
  return nil
end
validate_result(req, result, func) click to toggle source

Validates that the result from a handler method invocation match the return type for this function

Returns a JSON-RPC response hash if invalid, or nil if valid.

  • ‘req` - JSON-RPC request hash

  • ‘result` - Result object from the handler method call

  • ‘func` - Barrister::Function instance

# File lib/barrister.rb, line 681
def validate_result(req, result, func)
  invalid = validate("", func.returns, func.returns["is_array"], result)
  if invalid == nil
    return nil
  else
    return err_resp(req, -32001, invalid)
  end
end