In this chapter, we’re going to look at how to distribute the Ruby code you write to other developers and users.
Developing Ruby applications and libraries is so simple that you’ll soon want to release them to the world. As covered in Chapter 5, Ruby has a proud history of community and sharing, and nearly every Ruby developer will release code or completed applications at some point.
This chapter will walk you through the considerations and processes of deploying Ruby applications, libraries, and remotely accessible services using HTTP daemons and CGI scripts.
Distributing Basic Ruby Programs
Ruby is an interpreted language, so to distribute Ruby programs you can simply distribute the source code files you’ve written. Anyone else who has Ruby installed can then run the files in the same way that you do.
This process of distributing the actual source code for a program is typically how most programs developed using a scripting language, such as Ruby, are shared; but more traditionally, software has been distributed without the source code included. Popular desktop application development languages such as C and C++ are compiled languages whose source code is converted directly into machine code that runs on a certain platform. This software can be distributed by copying the resulting compiled machine code files, rather than the source, from machine to machine. However, this technique is not possible with Ruby, as there is currently no Ruby compiler available (with the exception of that in JRuby, but this is still a nascent area), so you have to distribute your source code in one sense or another for other people to be able to run your programs.
Later in this chapter, we’ll look at making the functionality of your Ruby programs available across a network. This technique does not require you to make your source code available, although it does require you to maintain a running copy of your program on a machine that’s network accessible (such as a web server).
This technique works well if you’re passing programs between your own machines or servers or if you’re distributing your programs to other developers. As long as the other users and machines have the same Ruby libraries or gems that your program uses, your program should run fine. For example, if you develop something to work with the standard version of Ruby that comes with Mac OS X, your program should work just fine on other Mac OS X machines (assuming they are running the same or a later version of OS X that includes Ruby).
This ability to interpret the code in the same way on varying machines is one benefit of interpreted languages over compiled languages. If the same version of the Ruby interpreter is available on a different platform, it should run the same programs that your Ruby interpreter does. With compiled code (code that is specifically compiled down to machine code for a specific platform), it is not the case that it will run identically on all platforms; in fact, it usually won’t!
What if you want to distribute your Ruby program to people who aren’t au fait with the Ruby interpreter? Depending on the target operating system (i.e., the operating system the user is running), there are several ways to make deploying Ruby applications simpler.
The Shebang Line
On UNIX-related operating systems (Linux, OS X, BSD, etc.), you can engineer your program to run more simply by using a shebang line .
In certain situations, such as when using the Apache HTTP server, shebang lines can work in Windows. You can use shebang lines such as #!ruby and #!c: ubyin uby.exe to make Ruby CGI scripts work under Apache on Windows.
UNIX-related operating systems support putting the name of the interpreter of a file on the first line of the file with a shebang line, where the “shebang” is simply the pound (#) sign and the exclamation mark (!).
The shebang line only needs to be in the file that’s initially run. It doesn’t need to be in library or support files used by the main program.
You could copy this example to many different Linux or OS X machines, for example, and it would work on the majority (env is not universal).
On most UNIX-like operating systems (including Mac OS X), as well as adding a shebang line, it’s necessary to make the Ruby script “executable” by using chmod for the preceding example to work, as in chmod +x test.rb.
You could even remove the .rb suffix and make it look like a regular executable if you wished.
Associated File Types in Windows
Whereas shebang lines are used on UNIX-like operating systems, Windows users are more familiar with file extensions (such as DOC, EXE, JPG, MP3, or TXT) dictating how a file is processed.
If you use My Computer or Windows Explorer to find a folder containing a Ruby file, the file might or might not already be associated with the Ruby interpreter (depending on which Ruby package you installed). Alternatively, Ruby files might be associated with your text editor. In any case, if you want to be able to double-click Ruby files in Windows and have them run directly as regular Ruby programs, you can do this by changing the default action for files with an extension of RB (or any other arbitrary extension you wish to use).
The easiest way to set an association is to right-click the icon representing a Ruby file and choose the Open With option from the menu (or Open, if it’s currently not associated with any program). Associate the program with the ruby.exe Ruby interpreter on your computer and check the Always Use the Selected Program to Open This Kind of File option. This will cause Ruby files to be executed directly by the Ruby interpreter in the future.
Detecting Ruby’s Runtime Environment
Deploying Ruby programs can be made easier with the tools covered in the previous section, but you can use a number of techniques directly within Ruby to make Ruby’s interactions with its surrounding environment even better.
For example, it’s possible to detect information about the machine upon which a Ruby script is running and then change the way the program operates on the fly. You can also retrieve parameters passed to the program via the command line.
Detecting the runtime environment while the program is running can be useful to restrict access to users on specific platforms if your program isn’t relevant to other users, or to tailor internal settings in your program so that your program will work better on the user’s operating system. It can also be a useful way to get system-specific information (rather than operating system–specific information) that’s relevant directly to the machine the program is running on, as it could affect the operation of your program. A common example of this is retrieving the current user’s path: a string of various directory names on the system that can be searched as default locations for files. There are also environment variables dictating where to store temporary files, and so forth.
Easy OS Detection with RUBY_PLATFORM
Among the myriad special variables Ruby makes accessible, a variable called RUBY_PLATFORM contains the name of the current environment (operating system) you’re running under. You can easily query this variable to detect what operating system your program is running under. This can be useful if you want to use a certain filesystem notation or features that are implemented differently under different operating systems.
Environment Variables
Whenever a program is run on a computer, it’s contained with a certain environment, whether that’s the command line or a GUI. The operating system sets a number of special variables called environment variables that contain information about the environment. They vary by operating system, but can be a good way of detecting things that could be useful in your programs.
You can also set environment variables with ENV['variable_name'] = value. However, setting environment variables from within a program only applies to the local process and any child processes.
Although ENV acts like a hash, it’s technically a special object, but you can convert it to a true hash using its .to_hash method, as in ENV.to_hash.
Accessing Command-Line Arguments
The parameters are passed into the program and become present in the ARGV array, where they can be processed as you wish. Use of ARGV is ideal for command-line tools where filenames and options are passed in this way.
Distributing Ruby Libraries As Gems
Over time, it’s likely you’ll develop your own libraries to solve various problems with Ruby so that you don’t need to write the same code over and over in different programs, but can call on the library for support.
Usually you’ll want to make these libraries available to use on other machines, on servers upon which you deploy applications, or to other developers. You might even open source your libraries to get community input and a larger developer base.
If you’ve read Chapter 5, you’ll have a good feel for Ruby’s commitment to open source and how open source is important to Ruby developers. This section looks at how to release your code and libraries in such a way that other developers can find them useful.
Luckily, deploying libraries is generally less problematic than deploying entire applications, as the target audience is made up of other developers who are usually familiar with installing libraries.
In Chapter 7, we looked at RubyGems, a library installation and management system for Ruby. We looked at how RubyGems makes it easy to install libraries, but RubyGems also makes it easy to create “gems” of your own from your own code.
Creating a Gem
There are easy ways to create gems and slightly less easy ways. I’m going to take a “raw” approach by showing how to create a gem from the ground up. Later, we’ll look at a library that will do most of the grunt work for you.
However, you want to turn it into a gem that you can use anywhere. Building a gem involves three steps. The first is to organize your code and other files into a structure that can be turned into a gem. The second is to create a specification file that lists information about the gem. The third is to use the gem program to build the gem from the source files and the specification.
Structuring Your Files
Before you can build a gem, it’s necessary to collect all the files you want to make up the gem. This is usually done using a standard structure. So far, you have your string_extend.rb file, and this is the only file you want within your gem.
lib: This directory will contain the Ruby code related to the library.
test or spec: This directory will contain any unit tests or other testing scripts related to the library.
doc: This is an optional directory that could contain documentation about the library, particularly documentation created with or by RDoc.
bin: This is another optional directory that can contain system tools and command-line scripts that are related to the library. For example, RubyGems itself installs the gem command-line tool; such a tool would be placed into bin.
At a minimum, you should end up with string_extend/lib and string_extend/test.
In this example, you should place string_extend.rb within the string_extend/lib directory. If you have tests, documentation, or command-line scripts, place them into the respective directories.
The preceding directory names are written in UNIX style, but on Windows would be represented similarly to this: c:gemsstring_extend, c:gemsstring_extendlib, and so on. Take this into account throughout this entire section.
Creating a Specification File
This is a basic specification file. The specification file is effectively a simple Ruby script that passes information through to Gem::Specification. The information it provides is mostly simple, but let’s look at a few key areas.
However, you could explicitly reference every file in an array in the preceding line.
A full list of the parameters you can use in a RubyGems specification file is available at https://guides.rubygems.org/specification-reference/. Also, you can learn more about versioning by visiting https://semver.org/.
Building the Gem
gem build should be run from the directory that the spec file is in.
This makes gem create the final gem file, called string_extend-0.0.1.gem. You may receive some warnings if there is any missing information. Read the warning carefully to determine how to remove it.
In the future, once you change and update your library, simply update the version numbers and rebuild, and you’ll have a new gem ready to go that can be installed to upgrade the existing installed gem.
Beyond this point, you could install your own gem with gem install string_extend, and then use it from other scripts using require 'string_extend'. It’s that simple.
Easier Gem Creation
In Chapter 7, we looked at a popular tool within the Ruby world called Bundler. Bundler makes it easy to manage the dependencies of your Ruby programs, but it also has a feature to create all of the boilerplate code that you saw in the past few pages. Knowing how this code operates is important, which is why we covered it, but once you're up to speed, using Bundler to automatically generate the files will save you time.
Bear in mind if you followed the previous section and created a gem by hand, what we're doing here will conflict with that, so consider moving to a different directory or creating something with a different name.
All that's left now is to fill out the blanks and carry on developing your library.
Installing Your Gem
Distributing a gem is easy. You can upload it to a website or transfer it in any way you would normally transfer a file. You can then install the gem with the command gem install and refer to the local file.
This command installs the gem gem_name by looking for it on the Internet and downloading it to the local machine. But how does gem know where to download gems? By default, RubyGems searches a Ruby project repository called RubyGems.org for gems if no source is specified. We’ll look at how to make gems available in the default database using RubyGems.org next.
RubyGems.org
RubyGems.org (https://rubygems.org/) is the largest community repository for Ruby projects and libraries. It contains thousands of projects and acts as a centralized location for the hosting of gems. Nearly all the major Ruby libraries are available from or hosted there, including Ruby on Rails.
If you want your gem to be installed easily by users, hosting it on RubyGems.org is key. And, happily, it's entirely free.
You will get an error if you push up a gem that has the same name as a gem that already exists on the RubyGems.org site, so you might want to check if your name conflicts before you even start to build your library, or at least be prepared to rename or namespace it.
Deploying Ruby Applications As Remote Services
An alternative to giving people your source or packaging it up to be run locally on a user’s machine is making a program’s functionality available as a remote service over a network. This only works for a small subset of functionality, but providing functionality remotely gives you more control over your code and how it is used.
Ruby’s networking and web features will be covered in more depth in Chapters 14 and 15, but in this section, we’ll look at how to put together basic services with Ruby that allow users to access a program’s functionality over a network.
If you want to build a true web application, refer to Chapter 13. This section is about building small, ad hoc services.
CGI Scripts
A common way to make scripts available online is to upload them to web hosting providers as CGI scripts. Common Gateway Interface (CGI) is a standard that allows web server software (such as Apache or Microsoft IIS) to launch programs and send data back and forth between them and the web client.
Many people associate the term CGI with the Perl language, as Perl has been the most common language with which to write CGI scripts. However, CGI is language agnostic, and you can just as easily write CGI scripts with Ruby (more easily, in fact!).
A Basic CGI Script
If you called this script test.cgi and uploaded it to a UNIX-based web hosting provider (the most common type) with the right permissions, you could use it as a CGI script. For example, if you have the website www.example.com/ hosted with a Linux web hosting provider and you upload test.cgi to the main directory and give it execute permissions, then visiting www.example.com/test.cgi should return an HTML page saying, “This is a test”.
Although /usr/bin/ruby is referenced in the previous example, for many users or web hosting providers, Ruby might be located at /usr/local/bin/ruby. Make sure to check or try using /usr/bin/env ruby.
When test.cgi is requested from a web browser, the web server looks for test.cgi on the website and then executes it using the Ruby interpreter (due to the shebang line—as covered earlier in this chapter). The Ruby script returns a basic HTTP header (specifying the content type as HTML) and then returns a basic HTML document.
In this example, you created a CGI object and used it to print the header line for you. This is easier than remembering what header to output, and it can be tailored. However, the real benefit of using the cgi library is so that you can do things such as accept data coming from a web browser (or an HTML form) and return more complex data to the user.
Accepting CGI Variables
In this case, the user would see the text he or she entered on the form reversed. You could also test this CGI script by passing the text directly within the URL, such as with www.example.com/test.cgi?text=this+is+a+test.
In Chapter 16, the cgi library is covered in more depth, along with information about using HTTP cookies and sessions, so if this mode of deployment is of interest to you, refer there for extended information and longer examples.
In general, however, CGI execution isn’t a popular option due to its lack of speed and the need for a Ruby interpreter to be executed on every request. This makes CGI unsuitable for high-use or heavy-load situations.
Depending on your setup (or hosting environment), you might find that Sinatra offers a nicer way to do what we’ve looked at in this section. See the Sinatra section in Chapter 13.
Generic HTTP Servers
HTTP is the communications protocol of the World Wide Web. Even though it’s commonly used to shuttle web pages from one place to another, it can also be used on an internal network or even to communicate between services on a single machine.
Creating an HTTP server from your Ruby program can provide a way for users (or even other programs) to make requests to your Ruby program, meaning you don’t need to distribute the source code, but can instead make your program’s functionality available over a network (such as the Internet).
In this section, we’re going to look directly at creating a basic HTTP server using WEBrick, part of Ruby's standard library. It's useful to have experience building servers directly so that you can see how things work at a low level, even though, ultimately, you will almost certainly choose to use a web app framework (as covered in Chapter 13) to make life easier. Given this, if you find any code in this section intimidating, skip to the Sinatra-based approach demonstrated in Chapter 13 as it will be a lot more straightforward and hide many of the details covered here.
In this section, we’re creating scripts that are HTTP servers themselves and do not rely on established HTTP servers such as Apache.
WEBrick
This code creates a generic WEBrick server on the local machine on port 1234, shuts the server down if the process is interrupted (often done with Ctrl+C), and for each new connection prints the current date and time. If you run this code, you could try to view the results in your web browser by visiting http://127.0.0.1:1234/ or http://localhost:1234/.
Because your test program doesn’t output valid HTTP, it might fail with some particularly sensitive web browsers. However, if you understand how to use the telnet program, you can use telnet 127.0.0.1 1234 to see the result. Otherwise, continue to the next example, where valid HTTP is returned for web browsers to view.
This code is more elaborate, but you now have access to request and response objects that represent both the incoming request and the outgoing response.
request.path contains the path within the URL (e.g., /abcd from http://127.0.0.1:1234/abcd), meaning you can interpret what the user was trying to request, call a different method, and provide the correct output.
In this example, you have a regular, basic Ruby class called MyNormalClass that implements two basic arithmetic methods. The WEBrick servlet uses the request object to retrieve parameters from the URL, as well as get the Ruby method requested from request.path. If the parameters aren’t passed, an HTTP error is returned.
Summary
In this chapter, we looked at how to deploy Ruby programs and libraries, as well as how to make their functions available to web browsers and other applications over a network. We also interrogated the environment so we can pursue different techniques on a per-operating system basis if we choose.
Shebang line: A special line at the start of a source code file that determines which interpreter is used to process the file. Used primarily on UNIX-based operating systems, shebang lines can also work on Windows when used with the Apache web server.
RUBY_PLATFORM: A special variable preset by Ruby that contains the name of the current platform (environment).
Environment variables: Special variables set by the operating system or other processes that contain information relevant to the current execution environment and information about the operating system.
RubyGems.org: A centralized repository and website dedicated to hosting and distributing Ruby projects and libraries.
GitHub: A popular hub and community site for users of the Git source code management system—now popular in the Ruby world. You can find it at https://github.com/.
CGI: Common Gateway Interface. A standard that enables web servers to execute scripts and provide an interface between web users and scripts located on that server.
WEBrick: A simple and easy HTTP server library for Ruby that comes with Ruby as standard.
In Chapter 15, we’re going to return to looking at network servers, albeit in a different fashion; but first, in Chapter 11, we’re going to take a look at some more advanced Ruby topics to flesh out the ideas we’ve covered so far.