Method: Puppet::Functions::Function3x.create_function

Defined in:
lib/puppet/functions.rb

.create_function(func_name, func_info, loader) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Creates an anonymous Function3x class that wraps a 3x function



697
698
699
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
# File 'lib/puppet/functions.rb', line 697

def self.create_function(func_name, func_info, loader)
  func_name = func_name.to_s

  # Creates an anonymous class to represent the function
  # The idea being that it is garbage collected when there are no more
  # references to it.
  #
  # (Do not give the class the block here, as instance variables should be set first)
  the_class = Class.new(Function3x)

  unless loader.nil?
    the_class.instance_variable_set(:'@loader', loader.private_loader)
  end

  the_class.instance_variable_set(:'@func_name', func_name)
  the_class.instance_variable_set(:'@method3x', :"function_#{func_name}")

  # Make the anonymous class appear to have the class-name <func_name>
  # Even if this class is not bound to such a symbol in a global ruby scope and
  # must be resolved via the loader.
  # This also overrides any attempt to define a name method in the given block
  # (Since it redefines it)
  #
  the_class.instance_eval do
    def name
      @func_name
    end

    def loader
      @loader
    end

    def method3x
      @method3x
    end
  end

  # Add the method that is called - it simply delegates to
  # the 3.x function by calling it via the calling scope using the @method3x symbol
  # :"function_#{name}".
  #
  # When function is not an rvalue function, make sure it produces nil
  #
  the_class.class_eval do
    # Bypasses making the  call via the dispatcher to make sure errors
    # are reported exactly the same way as in 3x. The dispatcher is still needed as it is
    # used to support other features than calling.
    #
    def call(scope, *args, &block)
      result = catch(:return) do
        mapped_args = Puppet::Pops::Evaluator::Runtime3FunctionArgumentConverter.map_args(args, scope, '')
        # this is the scope.function_xxx(...) call
        return scope.send(self.class.method3x, mapped_args)
      end
      result.value
    rescue Puppet::Pops::Evaluator::Next => jumper
      begin
        throw :next, jumper.value
      rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION
        raise Puppet::ParseError.new("next() from context where this is illegal", jumper.file, jumper.line)
      end
    rescue Puppet::Pops::Evaluator::Return => jumper
      begin
        throw :return, jumper
      rescue Puppet::Parser::Scope::UNCAUGHT_THROW_EXCEPTION
        raise Puppet::ParseError.new("return() from context where this is illegal", jumper.file, jumper.line)
      end
    end
  end

  # Create a dispatcher based on func_info
  type, names = Puppet::Functions.any_signature(*from_to_names(func_info))
  last_captures_rest = (type.size_range[1] == Float::INFINITY)

  # The method '3x_function' here is a dummy as the dispatcher is not used for calling, only for information.
  the_class.dispatcher.add(Puppet::Pops::Functions::Dispatch.new(type, '3x_function', names, last_captures_rest))
  # The function class is returned as the result of the create function method
  the_class
end