Introduction to Embedded Methods
A useful new feature with CloudForms 4.6 (ManageIQ Gaprindashvili) is the ability to be able to create reusable libraries of Ruby automation methods, and include (embed) these into other Ruby methods to use. This promotes code re-use, makes methods smaller, avoids code duplication, and simplifies testing.

Calling an Embedded Method

An embedded method must be added to a calling method's definition in the WebUI before it can be used. In the Embedded Methods section of the Automate method editor screen, click the Add Method button and browse to the location of the method to embed (see screenshot Adding an Embedded Method).
Adding an Embedded Method
Once the embedded method has been added, any of its contained methods are available for the calling method to use.

Writing an Embedded Method

Embedded methods (often called library methods) can be written in several different ways, depending on their intended use. Some use the nested module structure described in Defining Automate Methods as Classes, and as classes containing class methods, although this is not mandatory. Each style of writing has its advantages. The most common styles are as follows.

Class With Initializer

Encapsulating the embedded methods in a class with its own initializer allows the class to be unit tested by allowing the injection of a mock $evm into the @handle variable, as follows:
module Bit63
module Automate
module Library
module Utils
class Logger
def initialize(handle = $evm)
@handle = handle
end
def log(level, msg)
@handle.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
end
def dump_root
log("info", "Listing $evm.root Attributes - Begin")
@handle.root.attributes.sort.each { |k, v| log("info", " Attribute - #{k}: #{v}") }
log("info", "Listing $evm.root Attributes - End")
end
end
end
end
end
end
Embedded methods written in this way can be invoked in a calling method as follows:
logger = Bit63::Automate::Library::Utils::Logger.new()
logger.log(:info, "Some text")
logger.dump_root

Class Without Initializer

Alternatively the embedded method can be written as a straightforward class method, as follows:
module Bit63
module Automate
module Library
module Utils
class Logger
def self.log(level, msg)
$evm.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
end
def self.dump_root
log("info", "Listing $evm.root Attributes - Begin")
$evm.root.attributes.sort.each { |k, v| log("info", " Attribute - #{k}: #{v}") }
log("info", "Listing $evm.root Attributes - End")
end
end
end
end
end
end
These embedded methods can be invoked in the following way in a calling method:
Bit63::Automate::Library::Utils::Logger.log(:info, "Some text")
Bit63::Automate::Library::Utils::Logger.dump_root
Note
For unit testing, class methods can also optionally take a handle, for example:
def self.log_and_exit(msg, exit_code, handle = $evm)

Mixin

The code could be written as a mixin without a class, as follows:
module Bit63
module Automate
module Library
module Utils
def log(level, msg)
$evm.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
end
def dump_root
log("info", "Listing $evm.root Attributes - Begin")
$evm.root.attributes.sort.each { |k, v| log("info", " Attribute - #{k}: #{v}") }
log("info", "Listing $evm.root Attributes - End")
end
end
end
end
end
The calling method must include the embedded module's module path, which imports the embedded methods into its own namespace. The embedded methods can then be invoked without specifying their module path, for example:
include Bit63::Automate::Library::Utils
log(:info, "Some text")
dump_root

Bare Methods

Alternatively the embedded methods can be written as simple bare method definitions, as follows:
def log(level, msg)
$evm.log(level, "(location: #{caller_locations(1,1)[0].label}) #{msg}")
end
def self.dump_root
log("info", "Listing $evm.root Attributes - Begin")
$evm.root.attributes.sort.each { |k, v| log("info", " Attribute - #{k}: #{v}") }
log("info", "Listing $evm.root Attributes - End")
end
These embedded methods can be invoked in the following way in a calling method:
log(:info, "Some text")
dump_root

Multiple Embedded Methods

More than one embedded method can be added to an Automate method definition, allowing various library methods to be written in separate modules. The Automation Engine combines all of the embedded methods into a single preamble injected into to the Automate method at run-time. Code will execute in the order that it was listed in the embedded methods list (see screenshot Multiple Embedded Methods).
Multiple Embedded Methods
Note
Embedded methods cannot themselves use embedded methods as there is currently no support for nesting (as of CloudForms 4.6.4 and ManageIQ Gaprindashvili-4). This capability will however be added in a future release.

Logging

The use of an embedded method can be traced in automation.log. Before an Automate method is launched the Automation Engine will log Loading embedded method if any are defined, as follows:
... INFO -- : Invoking [inline] method [/Bit63/Stuff/Methods/test30] with inputs [{}]
--> INFO -- : Updated namespace [Library/Utils/logger Bit63/Library]
--> INFO -- : Loading embedded method Bit63/Library/Utils/logger
... INFO -- : <AEMethod [/Bit63/Stuff/Methods/test]> Starting
... INFO -- : <AEMethod test> (location: <main>) Some text
... INFO -- : <AEMethod test> (location: dump_root) Listing $evm.root Attributes - Begin
... INFO -- : <AEMethod test> (location: block in dump_root) Attribute - ae_provider_category: unknown
If an embedded method throws an error, the fully qualified method name (Domain/Namespace/Class/Method) and the line number in the method that threw the exception is logged for ease of debugging.

Summary

The chapter has introduced embedded methods, and shown how useful they can be to promote code re-use. They can be written in several different styles to suit the coding and unit testing standards in use in an organisation.
The next chapter shows an example of an embedded method that defines two small reusable methods that each use the RHV SDK to connect to a Red Hat Virtualization Manager.

Further Reading