Class: Barrister::Contract

Inherits:
Object
  • Object
show all
Includes:
Barrister
Defined in:
lib/barrister.rb

Overview

Represents a single parsed IDL definition

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Barrister

contract_from_file, #err_resp, #ok_resp, parse_method, rand_str

Constructor Details

#initialize(idl) ⇒ Contract

‘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



579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
# 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

Instance Attribute Details

#idlObject

Returns the value of attribute idl.



573
574
575
# File 'lib/barrister.rb', line 573

def idl
  @idl
end

#metaObject

Returns the value of attribute meta.



573
574
575
# File 'lib/barrister.rb', line 573

def meta
  @meta
end

Instance Method Details

#all_struct_fields(arr, struct) ⇒ Object

Recursively resolves all fields for the struct and its ancestors

Returns an Array with all the fields



817
818
819
820
821
822
823
824
825
826
827
828
829
830
# 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) ⇒ Object

Returns the Interface instance for the given name



605
606
607
# File 'lib/barrister.rb', line 605

def interface(name)
  return @interfaces[name]
end

#interfacesObject

Returns all Interfaces defined on this Contract



610
611
612
# File 'lib/barrister.rb', line 610

def interfaces
  return @interfaces.values
end

#resolve_method(req) ⇒ Object

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`



620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
# 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) ⇒ Object

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



833
834
835
836
# 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) ⇒ Object

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



700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
# 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) ⇒ Object

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.

  • ‘req` - JSON-RPC request hash

  • ‘func` - Barrister::Function instance



648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
# 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) ⇒ Object

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



681
682
683
684
685
686
687
688
# 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