Ruby script execution through external interpreter

Last edited on

Overview

Some test scenarios require to execute Ruby code through an external Ruby installation, for example if it makes use of a Gem that can't be installed using Squish's own Ruby installation.

Prerequisite

Install Ruby side by side with Squish, for example using an installer from https://rubyinstaller.org/ . In addition, install any Ruby Gem that is required by your script using this Ruby installation.

For example, assuming a Ruby installation at C:\Ruby, to install the Ruby Gem rubyzip, execute the following command (with a cmd.exe instance runs as Administrator/elevated):

C:\Ruby\bin\gem install rubyzip

Simple script execution through external interpreter

Save the Ruby code that should be executed through the external Ruby installation in a separate script file. In this article, we are assuming that the script that should be executed exists at C:\Scripts\externalScript.rb:

require 'rubygems'
require 'zip'

folder = "C:\\filesToZip"
input_filenames = ['Gesture1.gesture', 'Gesture2.gesture', 'Gesture3.gesture']

zipfile_name = "C:\\Windows\\Temp\\Archive.zip"

Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
  input_filenames.each do |filename|
    zipfile.add(filename, folder + '/' + filename)
  end
end
externalScript.rb

The script can be executed from within a Ruby Test Case by executing the external Ruby interpreter at C:\Ruby\bin\ruby. In the following example, backticks ("`") are used to call the script.

require 'squish'
include Squish

def main
  command = 'C:\\Ruby\\bin\\ruby C:\\Scripts\\externalScript.rb'
  `#{command}`
end
test.rb

Passing arguments to external scripts

Passing simple arguments

You can pass simple arguments the the external Ruby script. To do this, use the arguments command-line option of the Ruby interpreter. For example, this script passes two strings to the external script:

require 'squish'
include Squish

def main
  arg1 = "Hello"
  arg2 = "World"
  command = 'C:\\Ruby\\bin\\ruby C:\\Scripts\\externalScriptSimpleArgs.rb ' + arg1 + ' ' + arg2
  `#{commmand}`
end
test.rb

The external script can finally access the arguments:

ARGV.each do|a|
  puts "Argument: #{a}"
end
externalScriptSimpleArgs.rb

Passing complex arguments

In case you want to pass complex data to the external script, serialization can be used. For example, the following script makes use of the YAML module to serialize an instance of the Container class:

class Container
  def initialize(string, number)
    @string = string
    @number = number
  end

  def to_s
    "#{@string}, #{@number}\n"
  end
end
container.rb
require "yaml"
require "C:\\Scripts\\container.rb"
include Squish

def main
  complexData = Container.new("Hello world!", 42)
  serializedObject = YAML::dump(complexData)
  command = 'C:\\Ruby\\bin\\ruby C:\\Scripts\\externalScriptComplexArgs.rb "' + serializedObject + '"'
  `#{command}`
end
test.rb

To get an object out of the serialized object, the external script has to call the load method:

require "yaml"
require "C:\\Scripts\\container.rb"

serializedObject = ARGV[0]
complexData = YAML::load(serializedObject)
externalScriptComplexArgs.rb

Returning data from external script

Returning simple data

There are use cases where resulting data of the external script should be used in the calling Test Case. To do this, the external script can simply print the data.

data = "This string should be returned"
puts data
externalScriptSimpleReturnValue.rb

In the Test Case that calls the external script, we can fetch the return value as shown below:

include Squish

def main
  command = 'C:\\Ruby\\bin\\ruby C:\\Scripts\\externalSimpleReturnValue.rb'
  rv = `#{command}`
end
test.rb

Returning complex data

There are use cases where resulting data of the external script should be used in the calling Test Case. To return complex data, YAML object serialization can be used. For example, we can return an instance of the following class:

class Container
  def initialize(string, number)
    @string = string
    @number = number
  end

  def to_s
    "#{@string}, #{@number}\n"
  end
end
container.rb

To do this, we need to serialize the instance in the external script and print the resulting string represenation:

require "yaml"
require "C:\\Scripts\\container.rb"

complexData = Container.new("Hello world!", 42)
serializedObject = YAML::dump(complexData)

puts serializedObject
externalScriptComplexReturnValue.rb

The external script can now be executed as shown earlier. Finally, the object can be restored with the help of YAML.

require "yaml"
require "C:\\Scripts\\container.rb"

include Squish

def main
  command = 'C:\\Ruby\\bin\\ruby C:\\Scripts\\externalComplexReturnValue.rb'
  rv = `#{command}`
  complexData = YAML::load(rv)
end
test.rb