© Carleton DiLeo, Peter Cooper 2021
C. DiLeo, P. CooperBeginning Ruby 3https://doi.org/10.1007/978-1-4842-6324-2_10

10. Distributing Ruby Code and Libraries

Carleton DiLeo1   and Peter Cooper2
(1)
Boulder, CO, USA
(2)
Louth, UK
 

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.

Note

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).

To see how you can distribute Ruby source code, let’s take an example Ruby file and call it test.rb:
puts "Your program works!"
If you copy test.rb to another computer that has the Ruby interpreter installed on it, you can run the program directly with the Ruby interpreter as you would normally:
ruby test.rb
Your program works!

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 .

Note

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.

For example, say your script were to look like this:
#!/usr/bin/ruby
puts "Your program works!"

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 (!).

Note

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.

In this case, /usr/bin/ruby, the Ruby interpreter, is used to interpret the rest of the file. One problem you might run into, though, is that your Ruby interpreter might be located in /usr/bin/local/ruby or have a different name entirely. However, there’s a reasonably portable way to work around this problem. Many UNIX-related operating systems (including most Linuxes and OS X) have a tool called env that stores the location of certain applications and settings. You can use this tool to load Ruby without knowing its exact location, for example:
#!/usr/bin/env ruby
puts "Your program works!"

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).

If this script were called test.rb and located in the current working directory, you could simply run it from a command line, like so:
./test.rb
Note

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.

Naturally, if you copied the script elsewhere (e.g., /usr/bin), you could access it directly:
/usr/bin/test.rb
Or if the script’s location is in the path, it’s even easier:
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.

On my Windows machine, RUBY_PLATFORM contains i386-mswin32, on my OS X machine it contains x86_64-darwin13, and on my Linux machine it contains i686-linux. This gives you the immediate power to segregate features and settings by operating system:
if RUBY_PLATFORM =~ /win32/
  puts "We're in Windows!"
elsif RUBY_PLATFORM =~ /linux/
  puts "We're in Linux!"
elsif RUBY_PLATFORM =~ /darwin/
  puts "We're in Mac OS X!"
elsif RUBY_PLATFORM =~ /freebsd/
  puts "We're in FreeBSD!"
else
  puts "We're running under an unknown operating system."
end

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 quickly and easily inspect the environment variables (as supplied by your operating system) on your current machine with irb by using the special ENV hash:
irb(main):001:0> pp ENV.each {|e| puts e.join(': ') }
TERM: vt100
SHELL: /bin/bash
USER: carleton
PATH: /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/opt/local/bin:/usr/local/sbin
PWD: /Users/carleton
SHLVL: 1
HOME: /Users/carleton
LOGNAME: carleton
SECURITYSESSIONID: 51bbd0
_: /usr/bin/irb
LINES: 32
COLUMNS: 120
Specifically, these are the results from my machine, and yours will probably be quite different. For example, when I try the same code on a Windows machine, I get results such as these:
ALLUSERSPROFILE: F:Documents and SettingsAll Users
APPDATA: F:Documents and SettingscarletonApplication Data
CLIENTNAME: Console
HOMEDRIVE: F:
HOMEPATH: Documents and Settingscarleton
LOGONSERVER: \PSHUTTLE
NUMBER_OF_PROCESSORS: 2
OS: Windows_NT
Path: F: ubyin;F:WINDOWSsystem32;F:WINDOWS
PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.RB;.RBW
ProgramFiles: F:Program Files
SystemDrive: F:
SystemRoot: F:WINDOWS
TEMP: F:DOCUME~1CarletonLOCALS~1Temp
TMP: F:DOCUME~1CarletonLOCALS~1Temp
USERDOMAIN: PSHUTTLE
USERNAME: Carleton
USERPROFILE: F:Documents and Settingscarleton
windir: F:WINDOWS
You can use these environment variables to decide where to store temporary files or to find out what sort of features your operating system offers, in real time, much as you did with RUBY_PLATFORM:
tmp_dir = '/tmp'
if ENV['OS'] =~ /Windows_NT/
  puts "This program is running under Windows NT/2000/XP!"
  tmp_dir = ENV['TMP']
elsif ENV['PATH'] =~ //usr/
  puts "This program has access to a UNIX-style file system!"
else
  puts "I cannot figure out what environment I'm running in!"
  exit
end
# [.. do something here ..]
Note

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

In Chapter 4, you used a special array called ARGV . ARGV is an array automatically created by the Ruby interpreter that contains the parameters passed to the Ruby program (whether on the command line or by other means). For example, say you created a script called argvtest.rb:
p ARGV
You could run it like so:
ruby argvtest.rb these are command line parameters
["these", "are", "command", "line", "parameters"]

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.

Using ARGV also works if you call a script directly. On UNIX operating systems, you could adjust argvtest.rb to be like this:
#!/usr/bin/env ruby
p ARGV
And you could call it in this way:
./argvtest.rb these are command line parameters
["these", "are", "command", "line", "parameters"]
You generally use command-line arguments to pass options, settings, and data fragments that might change between executions of a program. For example, a common utility found on most operating systems is copy or cp, which is used to copy files. It’s used like so:
cp /directory1/from_filename /directory2/destination_filename
This would copy a file from one place to another (and rename it along the way) within the filesystem. The two filenames are both command-line arguments, and a Ruby script could receive data in the same way, like so:
#!/usr/bin/env ruby
from_filename = ARGV[0]
destination_filename = ARGV[1]

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.

Let’s first create a simple library that extends the String class and puts it in a file called string_extend.rb:
class String
  def vowels
    scan(/[aeiou]/i)
  end
end
This code adds a vowels method to the String class, which returns an array of all the vowels in a string:
"This is a test".vowels
["i", "i", "a", "e"]
As a local library within the scope of a larger application, it could be loaded with require or require_relative:
require_relative 'string_extend'

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.

First, it’s necessary to create a folder to contain all the gem’s folders, so you create a folder called string_extend. Under this folder, you create several other folders as follows:
  • 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.

Note

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

Once your files are organized, it’s time to create a specification file that describes the gem and provides RubyGems with enough information to create the final gem. Create a text file called string_extend.gemspec (or a filename that matches your own project name) in the main string_extend folder, and fill it out like so:
Gem::Specification.new do |s|
  s.name = 'string_extend'
  s.version = '0.0.1'
  s.summary = "StringExtend adds useful features to the String class"
  s.platform = Gem::Platform::RUBY
  s.files = Dir.glob("**/**/**")
  s.test_files = Dir.glob("test/*_test.rb")
  s.authors = ["Your Name"]
  s.email = "[email protected]"
  s.required_ruby_version = '>= 2.0.0'
end

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.

First, you define the name of the gem, setting it to 'string_extend':
s.name = 'string_extend'
Next, you define the version number. Typically, version numbers for Ruby projects (and for Ruby itself) contain three parts in order of significance. Early versions of software—before an official release, perhaps—often begin with 0, as in 0.0.1 here:
s.version = '0.0.1'
The summary line is displayed by gem list, and can be useful to people prior to installing the gem. Simply put together a short description of your library/gem here:
s.summary = "StringExtend adds useful features to the String class"
The files attribute accepts an array of all the files to include within the gem. In this case, you use Dir.glob to get an array of all the files under the current directory:
s.files = Dir.glob("**/**/**")

However, you could explicitly reference every file in an array in the preceding line.

The test_files attribute, like the files attribute, accepts an array of files, in this case associated with tests. You can leave this line intact even if you have no test folder, as Dir.glob will just return an empty array, for example:
s.test_files = Dir.glob("test/*_test.rb")
or
s.test_files = Dir.glob("spec/*_spec.rb")
Last, sometimes libraries rely on features in certain versions of Ruby. You can specify the required version of Ruby with the require_ruby_version parameter. If there’s no required version, you can simply omit this line:
s.required_ruby_version = '>= 2.0.0'
Note

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

Once the specifications file is complete, building the final .gem file is as simple as this:
gem build <spec file>
Note

gem build should be run from the directory that the spec file is in.

In your case:
gem build string_extend.gemspec

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.

Note

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.

To create a new gem using Bundler is as simple as
bundle gem string_extend
Note

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.

You will be asked to select a testing framework, if you want to us the MIT license and if you want to include the code of conduct. In the following output, I’m using RSpec, the MIT license and the code of conduct. The result is a directory and a set of files, as well as the initialization of a Git repository:
      create  string_extend/Gemfile
      create  string_extend/lib/string_extend.rb
      create  string_extend/lib/string_extend/version.rb
      create  string_extend/string_extend.gemspec
      create  string_extend/Rakefile
      create  string_extend/README.md
      create  string_extend/bin/console
      create  string_extend/bin/setup
      create  string_extend/.gitignore
      create  string_extend/.travis.yml
      create  string_extend/.rspec
      create  string_extend/spec/spec_helper.rb
      create  string_extend/spec/string_extend_spec.rb
      create  string_extend/LICENSE.txt
      create  string_extend/CODE_OF_CONDUCT.md
Initializing git repo in /users/jane/ruby/string_extend
Due to Bundler's boilerplate code needing to cope with almost any example of creating a gem out of the box, its gem specification file is slightly more complex than the one we created earlier but follows the same structure:
require_relative 'lib/string_extend/version'
Gem::Specification.new do |spec|
  spec.name          = "string_extend"
  spec.version       = StringExtend::VERSION
  spec.authors       = ["Carleton DiLeo"]
  spec.email         = ["[email protected]"]
  spec.summary       = %q{TODO: Write a short summary, because RubyGems requires one.}
  spec.description   = %q{TODO: Write a longer description or delete this line.}
  spec.homepage      = "TODO: Put your gem's website or public repo URL here."
  spec.license       = "MIT"
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
  spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
  spec.metadata["homepage_uri"] = spec.homepage
  spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
  spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
  # Specify which files should be added to the gem when it is released.
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
  spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
    `git ls-files -z`.split("x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  end
  spec.bindir        = "exe"
  spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
  spec.require_paths = ["lib"]
end

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.

The best way to distribute gems, however, is in a form where they can be installed over the Internet without specifying a source, for example:
gem install gem_name

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.

To host a project on RubyGems.org, you first need an account, but once you're set up you'll be able to push any valid gem you've created on your local machine up to the RubyGems.org site like so:
gem push your_gems_filename-0.0.1.gem
Note

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.

If you use the Bundler approach to create a gem, as explained in the previous section, you can use Rake instead:
rake release

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.

Note

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

The most basic Ruby CGI script looks like this:
#!/usr/bin/ruby
puts "Content-type: text/html "
puts "<html><body>This is a test</body></html>"

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”.

Note

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.

Ruby comes with a special library called cgi that enables more sophisticated interactions than those with the preceding CGI script. Let’s create a basic CGI script that uses cgi:
#!/usr/bin/env ruby
require 'cgi'
cgi = CGI.new
puts cgi.header
puts "<html><body>This is a test</body></html>"

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

A benefit of CGI scripts is that they can process information passed to them from a form on an HTML page or merely specified within the URL. For example, if you had a web form with an <input> element with a name of “text” that posted to test.cgi, you can access the data passed to it like this:
#!/usr/bin/env ruby
require 'cgi'
cgi = CGI.new
text = cgi['text']
puts cgi.header
puts "<html><body>#{text.reverse}</body></html>"

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.

Here’s a more complete example:
#!/usr/bin/env ruby
require 'cgi'
cgi = CGI.new
from = cgi['from'].to_i
to = cgi['to'].to_i
number = rand(to-from+1) + from
puts cgi.header
puts "<html><body>#{number}</body></html>"
This CGI script responds with a random number that’s between the number supplied in the from CGI variable and the to CGI variable. An associated but basic form that could send the correct data would have HTML code like so:
<form method="POST" action="http://www.example.com/test.cgi">
For a number between <input type="text" name="from" value="" /> and
<input type="text" name="to" value="" /> <input type="submit"
value="Click here!" /></form>

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.

Tip

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.

Note

In this section, we’re creating scripts that are HTTP servers themselves and do not rely on established HTTP servers such as Apache.

WEBrick

WEBrick is a Ruby library that makes it easy to build an HTTP server with Ruby. It comes with most installations of Ruby by default (it’s part of the standard library), so you can usually create a basic web/HTTP server with only several lines of code:
require 'webrick'
server = WEBrick::GenericServer.new( :Port => 1234 )
trap("INT"){ server.shutdown }
server.start do |socket|
   socket.puts Time.now
end

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/.

Caution

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.

However, a more powerful technique is when you create servlets that exist in their own class and have more control over the requests and responses made to them:
require 'webrick'
class MyServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_GET(request, response)
     response.status = 200
     response.content_type = "text/plain"
     response.body = "Hello, world!"
  end
end
server = WEBrick::HTTPServer.new( :Port => 1234 )
server.mount "/", MyServlet
trap("INT"){ server.shutdown }
server.start

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.

For example, you can now find out what URL the user tried to access in his or her browser with such a line:
response.body = "You are trying to load #{request.path}"

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.

Here’s a more elaborate example:
require 'webrick'
class MyNormalClass
  def MyNormalClass.add(a, b)
    a.to_i + b.to_i
  end
  def MyNormalClass.subtract(a,b)
    a.to_i - b.to_i
  end
end
class MyServlet < WEBrick::HTTPServlet::AbstractServlet
  def do_GET(request, response)
    if request.query['a'] && request.query['b']
      a = request.query['a']
      b = request.query['b']
      response.status = 200
      response.content_type = 'text/plain'
      result = nil
      case request.path
        when '/add'
          result = MyNormalClass.add(a,b)
        when '/subtract'
          result = MyNormalClass.subtract(a,b)
        else
          result = "No such method"
      end
      response.body = result.to_s + " "
    else
      response.status = 400
      response.body = "You did not provide the correct parameters"
    end
  end
end
server = WEBrick::HTTPServer.new(:Port => 1234)
server.mount '/', MyServlet
trap('INT'){ server.shutdown }
server.start

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.

To use the preceding script, you’d use URLs such as these:
http://127.0.0.1:1234/add?a=10&b=20
30
http://127.0.0.1:1234/subtract?a=100&b=10
90
http://127.0.0.1:1234/subtract
You did not provide the correct parameters.
http://127.0.0.1:1234/abcd?a=10&b=20
No such method.

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.

Let’s reflect on the main concepts covered in this chapter:
  • 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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.25.74