SL Unit-Ii
SL Unit-Ii
Extending Ruby usually refers to adding functionality to the Ruby language itself, typically through
the use of Ruby's C API. This allows developers to write Ruby extensions in C that can then be used
seamlessly within Ruby programs.
Ruby's C API provides a way to interact with Ruby's internal data structures and functions, allowing
developers to create custom classes, modules, and methods that can be called from Ruby code.
Extending Ruby can be useful for integrating with existing C libraries, optimizing performance-critical
code, or adding functionality that isn't available in standard Ruby libraries.
Extending Ruby in scripting languages typically involves leveraging Ruby's ability to interact with
code written in other languages like C, C++, or even languages like Python or JavaScript. This allows
developers to incorporate functionality from these languages into their Ruby scripts.
For example, you can use Ruby's Fiddle library to dynamically load and call functions from shared
libraries written in C. This is particularly useful for interfacing with system-level functionality or
performance-critical operations. Similarly, you can use libraries like Rjb (Ruby Java Bridge) or PyCall
(Python in Ruby) to interact with Java or Python code respectively from within Ruby scripts. By
extending Ruby with scripting languages, developers can take advantage of the strengths of each
language and incorporate diverse functionality into their Ruby applications.
The extension we write will have the same functionality as the following Ruby class:
class MyTest
def initialize
@arr = Array.new
end
def add(obj)
@arr.push(obj)
end
end
That is, we’ll be writing an extension in C that is plug-compatible with that Ruby class. The
#include "ruby.h"
static ID id_push;
VALUE arr;
arr = rb_ary_new();
return self;
VALUE arr;
return arr;
}
VALUE cTest;
void Init_my_test() {
id_push = rb_intern("push");
Let’s go through this example in detail, because it illustrates many of the important concepts in this
chapter. First, we need to include the header file ruby.h to obtain the necessary Ruby definitions.
Now look at the last function, Init_my_test. Every extension defines a C global function named
Init_name. This function will be called when the interpreter first loads the extension name (or on
startup for statically linked extensions). It is used to initialize the extension and to insinuate it into the
Ruby environment. (Exactly how Ruby knows that an extension is called name we’ll cover later.) In
this case, we define a new class named MyTest, which is a subclass of Object (represented by the
external symbol rb_cObject; see ruby.h for others).
Next we define add and initialize as two instance methods for class MyTest. The two calls to
rb_define_method establish a binding between the Ruby method name and the C function that will
implement it. If Ruby code calls the add method on one of our objects, the interpreter will in turn call
the C function t_add with one argument.
Similarly, when new is called for this class, Ruby will construct a basic object and then call initialize,
which we have defined here to call the C function t_init with no (Ruby) arguments.
Now go back and look at the definition of t_init. Even though we said it took no arguments, it has a
parameter here! In addition to any Ruby arguments, every method is passed an initial VALUE
argument that contains the receiver for this method (the equivalent of self in Ruby code).
The first thing we’ll do in t_init is create a Ruby array and set the instance variable @arr to point to
it. Just as you would expect if you were writing Ruby source, referencing an instance variable that
doesn’t exist creates it. We then return a pointer to ourselves.
WARNING: Every C function that is callable from Ruby must return a VALUE, even if it’s just Qnil.
Otherwise, a core dump (or GPF) will be the likely result. Finally, the function t_add gets the instance
variable @arr from the current object and calls Array#push to push the passed value onto that array.
When accessing instance variables in this way, the @ prefix is mandatory—otherwise, the variable is
created but cannot be referenced from Ruby.
As we said earlier, immediate values are not pointers: Fixnum, Symbol, true, false, and nil are stored
directly in VALUE.
Fixnum values are stored as 31-bit numbers1 that are formed by shifting the original number left 1 bit
and then setting the LSB, or least significant bit (bit 0), to 1. When VALUE is used as a pointer to a
specific Ruby structure, it is guaranteed always to have an LSB of zero; the other immediate values
also have LSBs of zero. Thus, a simple bit test can tell you whether you have a Fixnum. This test is
wrapped in a macro, FIXNUM_P. Similar tests let you check for other immediate values.
In Ruby, objects are instances of classes, and they are created and manipulated within the Ruby
interpreter. However, Ruby provides a C API that allows developers to interact with Ruby objects and
the interpreter from C code. To work with Ruby objects in C, you typically include the <ruby.h> header
file, which provides access to the Ruby C API. This API allows you to create Ruby objects, call Ruby
methods, access object attributes, and more from within your C code.
Here's a simple example of how you might create a Ruby object in C:
In this example:
This is just a basic example to demonstrate the concept. In practice, you can do much more with the
Ruby C API, such as creating custom Ruby classes and objects, calling Ruby methods with arguments,
defining Ruby modules and functions, and more.
Working with Ruby objects in C involves understanding the Ruby C API and how Ruby represents
objects internally. Let's delve into a bit more detail about how you can work with Ruby objects in C.
1. Include the Ruby Header File: To work with Ruby objects in C, you need to include the
<ruby.h> header file, which provides access to the Ruby C API.
2. Initialize and Finalize the Ruby Interpreter: Before interacting with Ruby objects, you need
to initialize the Ruby interpreter using ruby_init() and finalize it when you're done using
ruby_cleanup().
3. Create Ruby Objects: Ruby objects in C are represented by the VALUE type, which is a
typedef for a pointer to a Ruby object. You can create Ruby objects using various functions
provided by the Ruby C API.
4. Accessing Ruby Object Attributes and Methods: Once you have a Ruby object, you can
access its attributes and methods using functions provided by the Ruby C API. For example, to
get the length of a string object:
6. Memory Management: When working with Ruby objects in C, you need to be mindful of
memory management. Ruby uses a garbage collector to manage memory, so you typically don't
need to free memory explicitly. However, you should be careful when creating new objects in
C to avoid memory leaks.
7. Error Handling: Error handling in C is crucial when working with Ruby objects. Many
functions in the Ruby C API return special values to indicate errors, so you should always
check return values and handle errors appropriately.
8. Advanced Usage: The Ruby C API provides many more functions and features for working
with Ruby objects, including creating custom Ruby classes and modules, defining methods,
manipulating arrays and hashes, and more. Advanced usage of the Ruby C API requires a deep
understanding of both C and Ruby.
Working with Ruby objects in C can be complex, but it allows you to extend Ruby with high-
performance C code and integrate with existing C libraries. If you're interested in learning more, the
official Ruby documentation and source code are valuable resources.
The Ruby Jukebox Extension! Designed to bring music playback and management seamlessly into
your Ruby projects, this extension empowers developers to create immersive audio experiences with
ease.
Key Features:
1. Music Playback: Enjoy smooth playback of audio files directly within your Ruby applications.
Whether it's your favorite tunes or custom soundtracks, the Jukebox Extension handles it all.
2. Playlist Management: Organize your music collection effortlessly with powerful playlist
management features. Create, edit, and shuffle playlists on the fly, providing users with a
personalized listening experience.
3. User-Friendly Interface: With an intuitive user interface, users can navigate through their
music library, control playback, and adjust volume with ease. The Jukebox Extension provides
a seamless audio experience for users of all levels.
4. Customization Options: Tailor the Jukebox Extension to suit your project's unique
requirements. Customize playback controls, UI elements, and behavior to seamlessly integrate
music functionality into your Ruby applications.
5. Cross-Platform Compatibility: Whether you're developing for web, desktop, or mobile
platforms, the Jukebox Extension ensures consistent performance and compatibility across
different environments.
Getting Started:
Getting started with the Ruby Jukebox Extension is a breeze! Simply install the gem via RubyGems
or include it as a dependency in your Gemfile. With comprehensive documentation and examples
provided, you'll be up and running in no time, ready to elevate your Ruby projects with the magic of
music.
Become part of our vibrant community of developers, sharing ideas, tips, and insights on how to make
the most of the Ruby Jukebox Extension. Connect with fellow enthusiasts, contribute to the project,
and explore new possibilities for integrating music into your Ruby applications.
In January 2022, there isn't a widely known or established Ruby gem or library specifically named
"Jukebox extension." However, it's possible that such a project could have emerged or gained
popularity since then.
If you're referring to a specific Ruby extension or gem related to jukebox functionality, it might be a
custom project or a relatively new development in the Ruby community. Without more specific
information, it's challenging to provide detailed insights into its features or usage.
1. Music Playback: The extension could include features for playing audio files, managing
playlists, and controlling playback within Ruby applications.
2. Integration with Music Services: It might offer integration with online music services or
APIs, allowing developers to build applications that stream or access music content.
3. User Interface: A jukebox extension could include components for creating a user interface
(UI) for interacting with music playback features, such as buttons for controlling playback,
displaying song information, and managing playlists.
4. Customization and Configuration: Developers might be able to customize the behavior and
appearance of the jukebox extension through configuration options or by extending its
functionality with custom code.
5. Documentation and Examples: Good documentation and examples are essential for any Ruby
gem or library. They help developers understand how to use the extension effectively and
provide guidance on common use cases and best practices.
Interfacing C code with Ruby and sharing data and behavior between the two worlds.
Wrapping C Structures
We have the vendor’s library that controls the audio CD jukebox units, and we’re ready to wire it
into Ruby. The vendor’s header file looks like this:
int statusf;
int request;
void *data;
char pending;
int unit_id;
void *stats;
} CDJukebox;
CDJukebox *new_jukebox(void);
int disc,
int track,
// ... others...
// Report a statistic
This vendor has its act together; although they might not admit it, the code is written with an object-
oriented flavor. We don’t know what all those fields mean within the CDJukeBox structure, but that’s
OK—we can treat it as an opaque pile of bits. The vendor’s code knows what to do with it; we just
have to carry it around.
Any time you have a C-only structure that you would like to handle as a Ruby object, you should wrap
it in a special, internal Ruby class called DATA (type T_DATA). Two macros do this wrapping, and
one macro retrieves your structure back out again.
Creating a Jukebox extension in C for Ruby involves defining functions that interact with Ruby objects
related to audio and managing a playlist. Here's a simplified example:
This is a basic structure, and you would need to implement the actual functionality for handling audio
playback, playlist management, etc., in the C functions. Additionally, handling errors, memory
management, and more advanced features would be necessary for a robust extension.
1. Allocate Memory:
Use malloc or related functions to allocate memory for your data structures.
2. Free Memory:
Always free allocated memory when it's no longer needed to prevent memory leaks.
When dealing with Ruby objects, use Ruby memory management functions to allocate and
deallocate memory.
4. Error Handling:
When dealing with Ruby data structures (arrays, hashes, etc.), use Ruby API functions to
manipulate them safely.
Remember to manage memory carefully, handle errors, and free resources appropriately to ensure the
stability and reliability of your Ruby C extension.
You may sometimes need to allocate memory in an extension that won’t be used for object storage—
perhaps you have a giant bitmap for a Bloom filter, an image, or a whole bunch of little structures that
Ruby doesn’t use directly.
To work correctly with the garbage collector, you should use the following memory allocation routines.
These routines do a little bit more work than the standard malloc function. For instance, if ALLOC_N
determines that it cannot allocate the desired amount of memory, it will invoke the garbage collector
to try to reclaim some space. It will raise a NoMemError if it can’t or if the requested amount of
memory is invalid.
type *ALLOC_N(c-type, n)
Allocates n c-type objects, where c-type is the literal name of the C type, not a variable
of that type.
type *ALLOC(c-type)
REALLOC_N(var, c-type, n)
Reallocates n c-types and assigns the result to var, a pointer to a variable of type c-type.
type *ALLOCA_N(c-type, n)
Allocates memory for n objects of c-type on the stack—this memory will be automatically
1. Dynamic Typing:
In Ruby, you don't declare the type of a variable explicitly. The type of a variable is
determined at runtime based on the value it holds.
2. Object-Oriented:
Everything in Ruby is an object, including primitive data types like integers and
booleans. Objects in Ruby have methods associated with them.
3. Duck Typing:
Ruby focuses on the behavior of objects rather than their specific types. If an object
quacks like a duck (responds to the necessary methods), then it's treated as a duck,
regardless of its actual class.
4. Strong Typing:
While Ruby is dynamically typed, it is also strongly typed. This means that the
interpreter enforces type safety during runtime. Operations between incompatible types
typically result in errors.
5. Open Classes:
Ruby allows you to modify or reopen existing classes and add new methods to them.
This feature is known as "monkey patching." It provides a high degree of flexibility but
should be used with caution to avoid unintended consequences.
6. Type Conversion:
Ruby provides methods for explicit type conversion (casting) between different types.
For example, to_i converts an object to an integer, and to_s converts it to a string.
7. Nil:
Ruby has a special object called nil, which represents the absence of a value or undefined state.
Methods can return nil if there's no meaningful result.
These aspects helps you write more flexible and expressive code in Ruby, taking advantage of the
language's dynamic and object-oriented nature.
We can either do this as a shared object, which is dynamically loaded at runtime, or statically link the
extension into the main Ruby interpreter itself. The basic procedure is the same:
3. Create extconf.rb.
5. Run make.
The overall workflow when building an extension is shown in Figure below, on the next page. The key
to the whole process is the extconf.rb program that you, as a developer, create. extconf.rb is simple
program that determines what features are available on the user’s system and where those features may
be located.
Building an Extension
Executing extconf.rb builds a customized Makefile, tailored for both your application and the system
on which it’s being compiled. When you run the make command against this Makefile, your extension
is built and (optionally) installed. If you have multiple versions of Ruby installed on your system, the
one used when you run extconf.rb is the one your extension is built and installed against.
The simplest extconf.rb may be just two lines long, and for many extensions this is sufficient:
require 'mkmf'
create_makefile("Test")
The first line brings in the mkmf library module. This contains all the commands we’ll be using. The
second line creates a Makefile for an extension called “Test.” (Note that “Test” is the name of the
extension; the file will always be called Makefile.)
Test will be built from all the C source files in the current directory. When your code is loaded,
Let’s say that we run this extconf.rb program in a directory containing a single source file, main.c. The
result is a Makefile that will build our extension. On a Linux box, this executes the following
commands (your commands will likely be different):
-c main.c -o main.o
The result of this compilation is Test.so, which may be dynamically linked into Ruby at runtime with
require. Under Mac OS X, the commands are different, but the result is the same; a shared object (a
bundle on the Mac) is created:
Although this basic extconf.rb program works for many simple extensions, you may have to do some
more work if your extension needs header files or libraries that aren’t included in the default
compilation environment or if you conditionally compile code based on the presence of libraries or
functions.
A common requirement is to specify nonstandard directories where include files and libraries may be
found. This is a two-step process. First, your extconf.rb should contain one or more dir_config
commands. This specifies a tag for a set of directories. Then, when you run the extconf.rb program,
you tell mkmf where the corresponding physical directories are on the current system.
If extconf.rb contains the line dir_config(name), then you give the location of the corresponding
directories with the command-line options:
--with-name-include=directory
--with-name-lib=directory
If (as is common) your include and library directories are subdirectories called include and lib
of the same directory, you can take a shortcut:
--with-name-dir=directory
Adds directory/lib and directory/include to the link command and compile command, respectively. As
well as specifying all these --with options when you run extconf.rb, you can also use the –with options
that were specified when Ruby was built for your machine. This means you can discover and use the
locations of libraries that are used by Ruby itself. It also means that you can specify the locations of
all libraries just once and then rebuild extensions as many times as you like.
To make all this concrete, let’s say you need to use the vendor’s CDJukebox libraries and include files
for the CD player we’re developing. Your extconf.rb may contain this:
require 'mkmf'
dir_config('cdjukebox')
# .. more stuff
create_makefile("CDPlayer")
The generated Makefile would assume that the directory /usr/local/cdjb/lib contained the libraries and
the directory /usr/local/cdjb/include the include files.
The dir_config command adds to the list of places to search for libraries and include files. It does not,
however, link the libraries into your application. To do that, you’ll need to use one or more have_library
or find_library commands.
have_library looks for a given entry point in a named library. If it finds the entry point, it adds the
library to the list of libraries to be used when linking your extension. find_library is similar but allows
you to specify a list of directories to search for the library.
Here are the contents of the extconf.rb that we use to link our CD player:
require 'mkmf'
dir_config("cdjukebox")
have_library("cdjukebox", "new_jukebox")
create_makefile("CDPlayer")
A particular library may be in different places depending on the host system. The X Window system,
for example, is notorious for living in different directories on different systems. The find_library
command will search a list of supplied directories to find the right one (this is different from
have_library, which uses only configuration information for the search).
For example, to create a Makefile that uses X Windows and a JPEG library, you might craft an
extconf.rb containing the following:
require 'mkmf'
if have_library("jpeg","jpeg_mem_init") and
find_library("X11", "XOpenDisplay",
"/usr/X11R6/lib", # to check
then
create_makefile("XThing")
else
end
We’ve added some functionality to this program. All the mkmf commands return false if they fail. This
means we can write an extconf.rb that generates a Makefile only if everything it needs is present. The
Ruby distribution does this so that it will try to compile only those extensions that are supported on
your system.
You also may want your extension code to be able to configure the features it uses depending on the
target environment. For example, our CD jukebox may be able to use a high-performance MP3 decoder
if the end user has one installed.
require 'mkmf'
dir_config('cdjukebox')
have_library('cdjb', 'CDPlayerNew')
have_header('hp_mp3.h')
create_makefile("CDJukeBox")
We can also check to see whether the target environment has a particular function in any of the libraries
we’ll be using. For example, the setpriority call would be useful but isn’t always available.
require 'mkmf'
dir_config('cdjukebox')
have_func('setpriority')
create_makefile("CDJukeBox")
Both have_header and have_func define preprocessor constants if they find their targets. The names
are formed by converting the target name to uppercase and prepending HAVE_.
Your C code can take advantage of this using constructs such as the following:
#if defined(HAVE_HP_MP3_H)
# include <hp_mp3.h>
#endif
#if defined(HAVE_SETPRIORITY)
#endif
If you have special requirements that can’t be met with all these mkmf commands, your program can
directly add to the global variables $CFLAGS and $LDFLAGS, which are passed to the compiler and
linker, respectively.
Sometimes you’ll create an extconf.rb and it just doesn’t seem to work. You give it the name of a
library, and it swears that no such library has ever existed on the entire planet. You tweak and tweak,
but mkmf still can’t find the library you need. It would be nice if you could find out exactly what it’s
doing behind the scenes. Well, you can. Each time you run your extconf.rb script, mkmf generates a
log file containing details of what it did. If you look in mkmf.log, you’ll be able to see what steps the
program used to try to find the libraries you requested. Sometimes trying these steps manually will
help you track down the problem.
2.1.6 Embedding a Ruby Interpreter
In addition to extending Ruby by adding C code, you can also turn the problem around and embed
Ruby itself within your application. Here's an example.
#include "ruby.h"
main() {
/* ... our own application stuff ... */
ruby_init();
ruby_script("embedded");
rb_load_file("start.rb");
while (1) {
if (need_to_do_ruby) {
ruby_run();
}
/* ... run our app stuff */
}
}
To initialize the Ruby interpreter, you need to call ruby_init(). But on some platforms, you may need
to take special steps before that:
#if defined(NT)
NtInitialize(&argc, &argv);
#endif
#if defined(__MACOS__) && defined(__MWERKS__)
argc = ccommand(&argv);
#endif
See main.c in the Ruby distribution for any other special defines or setup needed for your platform.
Sets up and initializes the interpreter. This function should be called before any other Ruby-
related functions.
void ruby_options(int argc, char **argv")
void ruby_run(")
You need to take some special care with exception handling; any Ruby calls you make at this top level
should be protected to catch exceptions and handle them cleanly.