Showing posts with label Ruby Advanced. Show all posts
Showing posts with label Ruby Advanced. Show all posts

Monday, November 23, 2015

Ruby - Multithreading

Traditional programs have a single thread of execution: the statements or instructions that comprise the program are executed sequentially until the program terminates.

A multithreaded program has more than one thread of execution. Within each thread, statements are executed sequentially, but the threads themselves may be executed in parallel on a multicore CPU, for example. Often on a single CPU machine, multiple threads are not actually executed in parallel, but parallelism is simulated by interleaving the execution of the threads.

Ruby makes it easy to write multi-threaded programs with the Thread class. Ruby threads are a lightweight and efficient way to achieve concurrency in your code.

Creating Ruby Threads:


To start a new thread, just associate a block with a call to Thread.new. A new thread will be created to execute the code in the block, and the original thread will return from Thread.new immediately and resume execution with the next statement:

# Thread #1 is running here
Thread.new {
  # Thread #2 runs this code
}
# Thread #1 runs this code

Example:


Here is an example, which shows how we can use multi-threaded Ruby program.

#!/usr/bin/ruby

def func1
   i=0
   while i<=2
      puts "func1 at: #{Time.now}"
      sleep(2)
      i=i+1
   end
end

def func2
   j=0
   while j<=2
      puts "func2 at: #{Time.now}"
      sleep(1)
      j=j+1
   end
end

puts "Started At #{Time.now}"
t1=Thread.new{func1()}
t2=Thread.new{func2()}
t1.join
t2.join
puts "End at #{Time.now}"

This will produce following result:

Started At Wed May 14 08:21:54 -0700 2008
func1 at: Wed May 14 08:21:54 -0700 2008
func2 at: Wed May 14 08:21:54 -0700 2008
func2 at: Wed May 14 08:21:55 -0700 2008
func1 at: Wed May 14 08:21:56 -0700 2008
func2 at: Wed May 14 08:21:56 -0700 2008
func1 at: Wed May 14 08:21:58 -0700 2008
End at Wed May 14 08:22:00 -0700 2008

Thread Lifecycle:


A new threads are created with Thread.new. You can also use the synonyms Thread.start and Thread.fork.

There is no need to start a thread after creating it, it begins running automatically when CPU resources become available.

The Thread class defines a number of methods to query and manipulate the thread while it is running. A thread runs the code in the block associated with the call to Thread.new and then it stops running.

The value of the last expression in that block is the value of the thread, and can be obtained by calling the value method of the Thread object. If the thread has run to completion, then the value returns the thread's value right away. Otherwise, the value method blocks and does not return until the thread has completed.

The class method Thread.current returns the Thread object that represents the current thread. This allows threads to manipulate themselves. The class method Thread.main returns the Thread object that represents the main thread. This is the initial thread of execution that began when the Ruby program was started.

You can wait for a particular thread to finish by calling that thread's Thread.join method. The calling thread will block until the given thread is finished.

Threads and Exceptions:


If an exception is raised in the main thread, and is not handled anywhere, the Ruby interpreter prints a message and exits. In threads other than the main thread, unhandled exceptions cause the thread to stop running.

If a thread t exits because of an unhandled exception, and another thread s calls t.join or t.value, then the exception that occurred in t is raised in the thread s.

If Thread.abort_on_exception is false, the default condition, an unhandled exception simply kills the current thread and all the rest continue to run.

If you would like any unhandled exception in any thread to cause the interpreter to exit, set the class method Thread.abort_on_exception to true.

t = Thread.new { ... }
t.abort_on_exception = true

Thread Variables:


A thread can normally access any variables that are in scope when the thread is created. Variables local to the block of a thread are local to the thread, and are not shared.

Thread class features a special facility that allows thread-local variables to be created and accessed by name. You simply treat the thread object as if it were a Hash, writing to elements using []= and reading them back using [].

In this example, each thread records the current value of the variable count in a threadlocal variable with the key mycount.

#!/usr/bin/ruby

count = 0
arr = []

10.times do |i|
   arr[i] = Thread.new {
      sleep(rand(0)/10.0)
      Thread.current["mycount"] = count
      count += 1
   }
end

arr.each {|t| t.join; print t["mycount"], ", " }
puts "count = #{count}"

This produces the following result:

8, 0, 3, 7, 2, 1, 6, 5, 4, 9, count = 10

The main thread waits for the subthreads to finish and then prints out the value of count captured by each.

Thread Priorities:


The first factor that affects thread scheduling is thread priority: high-priority threads are scheduled before low-priority threads. More precisely, a thread will only get CPU time if there are no higher-priority threads waiting to run.

You can set and query the priority of a Ruby Thread object with priority= and priority. A newly created thread starts at the same priority as the thread that created it. The main thread starts off at priority 0.

There is no way to set the priority of a thread before it starts running. A thread can, however, raise or lower its own priority as the first action it takes.

Thread Exclusion:


If two threads share access to the same data, and at least one of the threads modifies that data, you must take special care to ensure that no thread can ever see the data in an inconsistent state. This is called thread exclusion.

Mutex is a class that implements a simple semaphore lock for mutually exclusive access to some shared resource. That is, only one thread may hold the lock at a given time. Other threads may choose to wait in line for the lock to become available, or may simply choose to get an immediate error indicating that the lock is not available.

By placing all accesses to the shared data under control of a mutex, we ensure consistency and atomic operation. Let's try to examples, first one without mutax and second one with mutax:

Example without Mutax:


#!/usr/bin/ruby
require 'thread'

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      count1 += 1
      count2 += 1
   end
end
spy = Thread.new do
   loop do
      difference += (count1 - count2).abs
   end
end
sleep 1
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

This will produce the following result:

count1 :  1583766
count2 :  1583766
difference : 637992
#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

count1 = count2 = 0
difference = 0
counter = Thread.new do
   loop do
      mutex.synchronize do
         count1 += 1
         count2 += 1
      end
    end
end
spy = Thread.new do
   loop do
       mutex.synchronize do
          difference += (count1 - count2).abs
       end
   end
end
sleep 1
mutex.lock
puts "count1 :  #{count1}"
puts "count2 :  #{count2}"
puts "difference : #{difference}"

This will produce the following result:

count1 :  696591
count2 :  696591
difference : 0

Handling Deadlock:


When we start using Mutex objects for thread exclusion we must be careful to avoid deadlock. Deadlock is the condition that occurs when all threads are waiting to acquire a resource held by another thread. Because all threads are blocked, they cannot release the locks they hold. And because they cannot release the locks, no other thread can acquire those locks.

This is where condition variables come into picture. A condition variable is simply a semaphore that is associated with a resource and is used within the protection of a particular mutex. When you need a resource that's unavailable, you wait on a condition variable. That action releases the lock on the corresponding mutex. When some other thread signals that the resource is available, the original thread comes off the wait and simultaneously regains the lock on the critical region.

Example:


#!/usr/bin/ruby
require 'thread'
mutex = Mutex.new

cv = ConditionVariable.new
a = Thread.new {
   mutex.synchronize {
      puts "A: I have critical section, but will wait for cv"
      cv.wait(mutex)
      puts "A: I have critical section again! I rule!"
   }
}

puts "(Later, back at the ranch...)"

b = Thread.new {
   mutex.synchronize {
      puts "B: Now I am critical, but am done with cv"
      cv.signal
      puts "B: I am still critical, finishing up"
   }
}
a.join
b.join

This will produce the following result:

A: I have critical section, but will wait for cv
(Later, back at the ranch...)
B: Now I am critical, but am done with cv
B: I am still critical, finishing up
A: I have critical section again! I rule!

Thread States:


There are five possible return values corresponding to the five possible states as shown in the following table. The status method returns the state of the thread.
Thread stateReturn value
Runnablerun
SleepingSleeping
Abortingaborting
Terminated normallyfalse
Terminated with exceptionnil

Thread Class Methods:


Following methods are provided by Thread class and they are applicable to all the threads available in the program. These methods will be called as using Thread class name as follows:

Thread.abort_on_exception = true

Here is complete list of all the class methods available:
SNMethods with Description
1Thread.abort_on_exceptionReturns the status of the global abort on exception condition. The default is false. When set to true, will cause all threads to abort (the process will exit(0)) if an exception is raised in any thread.
2Thread.abort_on_exception=When set to true, all threads will abort if an exception is raised. Returns the new state.
3Thread.criticalReturns the status of the global thread critical condition.
4Thread.critical=Sets the status of the global thread critical condition and returns it. When set to true, prohibits scheduling of any existing thread. Does not block new threads from being created and run. Certain thread operations (such as stopping or killing a thread, sleeping in the current thread, and raising an exception) may cause a thread to be scheduled even when in a critical section.
5Thread.currentReturns the currently executing thread.
6Thread.exitTerminates the currently running thread and schedules another thread to be run. If this thread is already marked to be killed, exit returns the Thread. If this is the main thread, or the last thread, exit the process.
7Thread.fork { block }Synonym for Thread.new .
8Thread.kill( aThread )Causes the given aThread to exit
9Thread.listReturns an array of Thread objects for all threads that are either runnable or stopped. Thread.
10Thread.mainReturns the main thread for the process.
11Thread.new( [ arg ]* ) {| args | block }Creates a new thread to execute the instructions given in block, and begins running it. Any arguments passed to Thread.new are passed into the block.
12Thread.passInvokes the thread scheduler to pass execution to another thread.
13Thread.start( [ args ]* ) {| args | block }Basically the same as Thread.new . However, if class Thread is subclassed, then calling start in that subclass will not invoke the subclass's initialize method.
14Thread.stopStops execution of the current thread, putting it into a sleep state, and schedules execution of another thread. Resets the critical condition to false.

Thread Instance Methods:


These methods are applicable to an instance of a thread. These methods will be called as using an instance of a Thread as follows:

#!/usr/bin/ruby

thr = Thread.new do   # Calling a class method new
   puts "In second thread"
   raise "Raise exception"
end
thr.join   # Calling an instance method join

Here is complete list of all the instance methods available:
SNMethods with Description
1thr[ aSymbol ]Attribute Reference - Returns the value of a thread-local variable, using either a symbol or a aSymbol name. If the specified variable does not exist, returns nil.
2thr[ aSymbol ] =Attribute Assignment - Sets or creates the value of a thread-local variable, using either a symbol or a string.
3thr.abort_on_exceptionReturns the status of the abort on exception condition for thr. The default is false.
4thr.abort_on_exception=When set to true, causes all threads (including the main program) to abort if an exception is raised in thr. The process will effectively exit(0).
5thr.alive?Returns true if thr is running or sleeping.
6thr.exitTerminates thr and schedules another thread to be run. If this thread is already marked to be killed, exit returns the Thread. If this is the main thread, or the last thread, exits the process.
7thr.joinThe calling thread will suspend execution and run thr. Does not return until thr exits. Any threads not joined will be killed when the main program exits.
8thr.key?Returns true if the given string (or symbol) exists as a thread-local variable.
9thr.killSynonym for Thread.exit .
10thr.priorityReturns the priority of thr. Default is zero; higher-priority threads will run before lower priority threads.
11thr.priority=Sets the priority of thr to an Integer. Higher-priority threads will run before lower priority threads.
12thr.raise( anException )Raises an exception from thr. The caller does not have to be thr.
13thr.runWakes up thr, making it eligible for scheduling. If not in a critical section, then invokes the scheduler.
14thr.safe_levelReturns the safe level in effect for thr.
15thr.statusReturns the status of thr: sleep if thr is sleeping or waiting on I/O, run if thr is executing, false if thr terminated normally, and nil if thr terminated with an exception.
16thr.stop?Returns true if thr is dead or sleeping.
17thr.valueWaits for thr to complete via Thread.join and returns its value.
18thr.wakeupMarks thr as eligible for scheduling, it may still remain blocked on I/O, however.

Ruby/LDAP Tutorial

Ruby/LDAP is an extension library for Ruby. It provides the interface to some LDAP libraries like OpenLDAP, UMich LDAP, Netscape SDK, ActiveDirectory.
The common API for application development is described in RFC1823 and is supported by Ruby/LDAP.

Ruby/LDAP Installation:

You can download and install a complete Ruby/LDAP package from SOURCEFORGE.NET.
Before installing Ruby/LDAP, make sure you have the following components:
  • Ruby 1.8.x (at least 1.8.2 if you want to use ldap/control).
  • OpenLDAP, Netscape SDK, Windows 2003 or Windows XP.
Now, you can use standard Ruby Installation method. Before starting, if you'd like to see the available options for extconf.rb, run it with '--help' option.
$ ruby extconf.rb [--with-openldap1|--with-openldap2| \
                   --with-netscape|--with-wldap32]
$ make
$ make install
NOTE: If you're building the software on Windows, you may need to use nmake instead of make.

Establish LDAP Connection:

This is a two-step process:

Step 1: Create Connection Object

Following is the syntax to create a connection to a LDAP directory.
LDAP::Conn.new(host='localhost', port=LDAP_PORT)
  • host: This is the host ID running LDAP directory. We will take it as localhost.
  • port: This is the port being used for LDAP service. Standard LDAP ports are 636 and 389. Make sure which port is being used at your server otherwise you can use LDAP::LDAP_PORT.
This call returns a new LDAP::Conn connection to the server, host, on port port.

Step 2: Binding

This is where we usually specify the username and password we will use for the rest of the session.
Following is the syntax to bind an LDAP connection, using the DN, dn, the credential, pwd, and the bind method, method
conn.bind(dn=nil, password=nil, method=LDAP::LDAP_AUTH_SIMPLE)do
....
end
You can use same method without a code block. In this case, you would need to unbind the connection explicitly as follows:
conn.bind(dn=nil, password=nil, method=LDAP::LDAP_AUTH_SIMPLE)
....
conn.unbind
If a code block is given, self is yielded to the block.
We can now perform search, add, modify or delete operations inside the block of the bind method (between bind and unbind), provided we have the proper permissions.

Example:

Assuming we are working on a local server, let's put things together with appropriate host, domain, user id and password, etc.
#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn=root, dc=localhost, dc=localdomain','secret')
....
conn.unbind

Adding an LDAP Entry:

Adding an LDPA entry is a two step process:

Step 1: Creating LDAP::Mod object

We need LDAP::Mod object pass to conn.add method to create an entry. Here is a simple syntax to create LDAP::Mod object:
Mod.new(mod_type, attr, vals)
  • mod_type: One or more option LDAP_MOD_ADD, LDAP_MOD_REPLACE or LDAP_MOD_DELETE.
  • attr: should be the name of the attribute on which to operate.
  • vals: is an array of values pertaining to attr. If vals contains binary data, mod_type should be logically OR'ed (|) with LDAP_MOD_BVALUES.
This call returns LDAP::Mod object, which can be passed to methods in the LDAP::Conn class, such as Conn#add, Conn#add_ext, Conn#modify and Conn#modify_ext.

Step 2: Calling conn.add Method

Once we are ready with LDAP::Mod object, we can call conn.add method to create an entry. Here is a syntax to call this method:
conn.add(dn, attrs)
This method adds an entry with the DN, dn, and the attributes, attrs. Here, attrs should be either an array of LDAP::Mod objects or a hash of attribute/value array pairs.

Example:

Here is a complete example, which will create two directory entries:
#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn=root, dc=localhost, dc=localdomain','secret')

conn.perror("bind")
entry1 = [
  LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['top','domain']),
  LDAP.mod(LDAP::LDAP_MOD_ADD,'o',['TTSKY.NET']),
  LDAP.mod(LDAP::LDAP_MOD_ADD,'dc',['localhost']),
}

entry2 = [
  LDAP.mod(LDAP::LDAP_MOD_ADD,'objectclass',['top','person']),
  LDAP.mod(LDAP::LDAP_MOD_ADD, 'cn', ['Zara Ali']),
  LDAP.mod(LDAP::LDAP_MOD_ADD | LDAP::LDAP_MOD_BVALUES, 'sn', 
                     ['ttate','ALI', "zero\000zero"]),
]

begin
  conn.add("dc=localhost, dc=localdomain", entry1)
  conn.add("cn=Zara Ali, dc=localhost, dc=localdomain", entry2)
rescue LDAP::ResultError
  conn.perror("add")
  exit
end
conn.perror("add")
conn.unbind

Modifying an LDAP Entry:

Modifying an entry is similar to adding one. Just call the modify method instead of add with the attributes to modify. Here is a simple syntax of modify method.
conn.modify(dn, mods)
This method modifies an entry with the DN, dn, and the attributes, mods. Here, mods should be either an array of LDAP::Mod objects or a hash of attribute/value array pairs.

Example:

To modify the surname of the entry, which we added in the previous section, we would write:
#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn=root, dc=localhost, dc=localdomain','secret')

conn.perror("bind")
entry1 = [
  LDAP.mod(LDAP::LDAP_MOD_REPLACE, 'sn', ['Mohtashim']),
]

begin
  conn.modify("cn=Zara Ali, dc=localhost, dc=localdomain", entry1)
rescue LDAP::ResultError
  conn.perror("modify")
  exit
end
conn.perror("modify")
conn.unbind

Deleting an LDAP Entry:

To delete an entry, call the delete method with the distinguished name as parameter. Here is a simple syntax of delete method.
conn.delete(dn)
This method deletes an entry with the DN, dn.

Example:

To delete Zara Mohtashim entry, which we added in the previous section, we would write:
#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn=root, dc=localhost, dc=localdomain','secret')

conn.perror("bind")
begin
  conn.delete("cn=Zara-Mohtashim, dc=localhost, dc=localdomain")
rescue LDAP::ResultError
  conn.perror("delete")
  exit
end
conn.perror("delete")
conn.unbind

Modifying the Distinguished Name:

It's not possible to modify the distinguished name of an entry with the modify method. Instead, use the modrdn method. Here is simple syntax of modrdn method:
conn.modrdn(dn, new_rdn, delete_old_rdn)
This method modifies the RDN of the entry with DN, dn, giving it the new RDN, new_rdn. If delete_old_rdn is true, the old RDN value will be deleted from the entry.

Example:

Suppose we have the following entry:
dn: cn=Zara Ali,dc=localhost,dc=localdomain
cn: Zara Ali
sn: Ali
objectclass: person
Then, we can modify its distinguished name with the following code:
#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn=root, dc=localhost, dc=localdomain','secret')

conn.perror("bind")
begin
  conn.modrdn("cn=Zara Ali, dc=localhost, dc=localdomain",
              "cn=Zara Mohtashim", true)
rescue LDAP::ResultError
  conn.perror("modrdn")
  exit
end
conn.perror("modrdn")
conn.unbind

Performing a Search:

To perform a search on a LDAP directory, use the search method with one of three different search modes:
  • LDAP_SCOPE_BASEM: Search only the base node.
  • LDAP_SCOPE_ONELEVEL: Search all children of the base node.
  • LDAP_SCOPE_SUBTREE: Search the whole subtree including the base node.

Example:

Here, we are going to search the whole subtree of entry dc=localhost, dc=localdomain for person objects:
#/usr/bin/ruby -w

require 'ldap'

$HOST =    'localhost'
$PORT =    LDAP::LDAP_PORT
$SSLPORT = LDAP::LDAPS_PORT

base = 'dc=localhost,dc=localdomain'
scope = LDAP::LDAP_SCOPE_SUBTREE
filter = '(objectclass=person)'
attrs = ['sn', 'cn']

conn = LDAP::Conn.new($HOST, $PORT)
conn.bind('cn=root, dc=localhost, dc=localdomain','secret')

conn.perror("bind")
begin
  conn.search(base, scope, filter, attrs) { |entry|
     # print distinguished name
     p entry.dn
     # print all attribute names
     p entry.attrs
     # print values of attribute 'sn'
     p entry.vals('sn')
     # print entry as Hash
     p entry.to_hash
  }
rescue LDAP::ResultError
  conn.perror("search")
  exit
end
conn.perror("search")
conn.unbind
This invokes the given code block for each matching entry where the LDAP entry is represented by an instance of the LDAP::Entry class. With the last parameter of search, you can specify the attributes in which you are interested, omitting all others. If you pass nil here, all attributes are returned same as "SELECT *" in relational databases.
The dn method(alias for get_dn) of the LDAP::Entry class returns the distinguished name of the entry, and with the to_hash method, you can get a hash representation of its attributes (including the distinguished name). To get a list of an entry's attributes, use the attrs method (alias for get_attributes). Also, to get the list of one specific attribute's values, use the vals method (alias for get_values)

Handling Errors:

Ruby/LDAP defines two different exception classes:
  • In case of an error, the new, bind or unbind methods raise an LDAP::Error exception.
  • In case of add, modify, delete or searching an LDAP directory raise an LDAP::ResultError.

Further Reading:

For complete details on LDAP methods, please refer to standard documentation for LDAP Documentation.

Ruby/Tk Guide

Introduction:

The standard graphical user interface (GUI) for Ruby is Tk. Tk started out as the GUI for the Tcl scripting language developed by John Ousterhout.
Tk has the unique distinction of being the only cross-platform GUI. Tk runs on Windows, Mac, and Linux and provides a native look-and-feel on each operating system.
The basic component of a Tk-based application is called a widget. A component is also sometimes called a window, since, in Tk, "window" and "widget" are often used interchangeably.
Tk applications follow a widget hierarchy where any number of widgets may be placed within another widget, and those widgets within another widget, ad infinitum. The main widget in a Tk program is referred to as the root widget and can be created by making a new instance of the TkRoot class.
  • Most Tk-based applications follow the same cycle: create the widgets, place them in the interface, and finally, bind the events associated with each widget to a method.
  • There are three geometry managers; place, grid and pack that are responsible for controlling the size and location of each of the widgets in the interface.

Installation:

The Ruby Tk bindings are distributed with Ruby but Tk is a separate installation. Windows users can download a single click Tk installation from ActiveState's ActiveTcl.
Mac and Linux users may not need to install it because there is a great chance that its already installed along with OS but if not, you can download prebuilt packages or get the source from the Tcl Developer Xchange.

Simple Tk Application:

A typical structure for Ruby/Tk programs is to create the main or root window (an instance of TkRoot), add widgets to it to build up the user interface, and then start the main event loop by calling Tk.mainloop.
The traditional Hello, World! example for Ruby/Tk looks something like this:
require 'tk'

root = TkRoot.new { title "Hello, World!" }
TkLabel.new(root) do
   text 'Hello, World!'
   pack { padx 15 ; pady 15; side 'left' }
end
Tk.mainloop
Here, after loading the tk extension module, we create a root-level frame using TkRoot.new. We then make a TkLabel widget as a child of the root frame, setting several options for the label. Finally, we pack the root frame and enter the main GUI event loop.
If you would run this script, it would produce the following result:
Ruby/Tk Hello World

Ruby/Tk Widget Classes:

There is a list of various Ruby/Tk classes, which can be used to create a desired GUI using Ruby/Tk.

Standard Configuration Options:

All widgets have a number of different configuration options, which generally control how they are displayed or how they behave. The options that are available depend upon the widget class of course.
Here is a list of all the standard configuration options which could be applicable to any Ruby/Tk widget. There are other widget specific options also which would be explained along with widgets.
SN Options with Description
1 activebackground => String Specifies background color to use when drawing active elements. An element is active if the mouse cursor is positioned over the element and pressing a mouse button will cause some action to occur. You can use color names like "red", "blue", "pink", "yellow" etc.
2 activeborderwidth => Integer Specifies a non-negative value indicating the width of the 3-D border drawn around active elements.
3 activeforeground => String Specifies foreground color to use when drawing active elements.
4 anchor => String Specifies how the information in a widget (e.g. text or a bitmap) is to be displayed in the widget. Must be one of the values n, ne, e, se, s, sw, w, nw, or center. For example, nw means display the information such that its top-left corner is at the top-left corner of the widget.
5 background or bg => String Specifies the normal background color to use when displaying the widget.
6 bitmap => Bitmap Specifies a bitmap to display in the widget. The exact way in which the bitmap is displayed may be affected by other options such as anchor or justify.
7 borderwidth or bd => Integer Specifies a non-negative value indicating the width of the 3-D border to draw around the outside of the widget.
8 compound => String Specifies if the widget should display text and bitmaps/images at the same time, and if so, where the bitmap/image should be placed relative to the text. Must be one of the values none, bottom, top, left, right, or center.
9 cursor => String Specifies the mouse cursor to be used for the widget. Possible values could be "watch", "arrow" etc.
10 disabledforeground => String Specifies foreground color to use when drawing a disabled element.
11 exportselection => Boolean Specifies whether or not a selection in the widget should also be the X selection. The value may have any of the true, false, 0, 1, yes, or no. If the selection is exported, then selecting in the widget deselects the current X selection, selecting outside the widget deselects any widget selection, and the widget will respond to selection retrieval requests when it has a selection.
12 font => String Specifies the font to use when drawing text inside the widget.
13 foreground or fg => String Specifies the normal foreground color to use when displaying the widget.
14 highlightbackground => String Specifies the color to display in the traversal highlight region when the widget does not have the input focus.
15 highlightcolor => String Specifies the color to use for the traversal highlight rectangle that is drawn around the widget when it has the input focus.
16 highlightthickness => Integer Specifies a non-negative value indicating the width of the highlight rectangle to draw around the outside of the widget when it has the input focus.
17 image => Image Specifies an image to display in the widget, which must have been created with an image create. Typically, if the image option is specified then it overrides other options that specify a bitmap or textual value to display in the widget; the image option may be reset to an empty string to re-enable a bitmap or text display.
18 jump => String For widgets with a slider that can be dragged to adjust a value, such as scrollbars and scales, this option determines when notifications are made about changes in the value. The option's value must be a boolean. If the value is false, updates are made continuously as the slider is dragged. If the value is true, updates are delayed until the mouse button is released to end the drag; at that point a single notification is made.
19 justify => String When there are multiple lines of text displayed in a widget, this option determines how the lines line up with each other. Must be one of left, center, or right. Left means that the lines' left edges all line up, center means that the lines' centers are aligned, and right means that the lines' right edges line up.
20 offset => String Specifies the offset of tiles (see also tile option). It can have two different formats offset x,y or offset side, where side can be n, ne, e, se, s, sw, w, nw, or center.
21 orient => String For widgets that can lay themselves out with either a horizontal or vertical orientation, such as scrollbars, this option specifies which orientation should be used. Must be either horizontal or vertical or an abbreviation of one of these.
22 padx => Integer Specifies a non-negative value indicating how much extra space to request for the widget in the X-direction.
23 pady => Integer Specifies a non-negative value indicating how much extra space to request for the widget in the Y-direction.
24 relief => Integer Specifies the 3-D effect desired for the widget. Acceptable values are raised, sunken, flat, ridge, and groove.
25 repeatdelay => Integer Specifies the number of milliseconds a button or key must be held down before it begins to auto-repeat. Used, for example, on the up- and down-arrows in scrollbars.
26 repeatinterval => Integer Used in conjunction with repeatdelay: once auto-repeat begins, this option determines the number of milliseconds between auto-repeats.
27 selectbackground => String Specifies the background color to use when displaying selected items.
28 selectborderwidth => Integer Specifies a non-negative value indicating the width of the 3-D border to draw around selected items.
29 selectforeground => String Specifies the foreground color to use when displaying selected items.
30 setgrid => Boolean Specifies a boolean value that determines whether this widget controls the resizing grid for its top-level window. This option is typically used in text widgets, where the information in the widget has a natural size (the size of a character) and it makes sense for the window's dimensions to be integral numbers of these units.
31 takefocus => Integer Provides information used when moving the focus from window to window via keyboard traversal (e.g., Tab and Shift-Tab). Before setting the focus to a window, the traversal scripts first check whether the window is viewable (it and all its ancestors are mapped); if not, the window is skipped. A value of 0 means that this window should be skipped entirely during keyboard traversal. 1 means that the this window should always receive the input focus.
32 text => String Specifies a string to be displayed inside the widget. The way in which the string is displayed depends on the particular widget and may be determined by other options, such as anchor or justify.
33 textvariable => Variable Specifies the name of a variable. The value of the variable is a text string to be displayed inside the widget; if the variable value changes then the widget will automatically update itself to reflect the new value. The way in which the string is displayed in the widget depends on the particular widget and may be determined by other options, such as anchor or justify.
34 tile => Image Specifies image used to display the widget. If image is the empty string, then the normal background color is displayed.
35 troughcolor => String Specifies the color to use for the rectangular trough areas in widgets such as scrollbars and scales.
36 troughtile => Image Specifies image used to display in the rectangular trough areas in widgets such as scrollbars and scales.
37 underline => Integer Specifies the integer index of a character to underline in the widget. This option is used by the default bindings to implement keyboard traversal for menu buttons and menu entries. 0 corresponds to the first character of the text displayed in the widget, 1 to the next character, and so on.
38 wraplength => Integer For widgets that can perform word-wrapping, this option specifies the maximum line length.
39 xscrollcommand => function Specifies a callback used to communicate with horizontal scrollbars.
40 yscrollcommand => function Specifies a callback used to communicate with vertical scrollbars.

Ruby/Tk Geometry Management:

Geometry Management deals with positioning different widgets as per requirement. Geometry management in Tk relies on the concept of master and slave widgets.
A master is a widget, typically a top-level window or a frame, which will contain other widgets, which are called slaves. You can think of a geometry manager as taking control of the master widget, and deciding what will be displayed within.
The geometry manager will ask each slave widget for its natural size, or how large it would ideally like to be displayed. It then takes that information and combines it with any parameters provided by the program when it asks the geometry manager to manage that particular slave widget.
There are three geometry managers place, grid and pack that are responsible for controlling the size and location of each of the widgets in the interface.

Ruby/Tk Event Handling:

Ruby/Tk supports event loop which receives events from the operating system. These are things like button presses, keystrokes, mouse movement, window resizing, and so on.
Ruby/Tk takes care of managing this event loop for you. It will figure out what widget the event applies to (did the user click on this button? if a key was pressed, which textbox had the focus?), and dispatch it accordingly. Individual widgets know how to respond to events, so for example a button might change color when the mouse moves over it, and revert back when the mouse leaves.
At a higher level, Ruby/Tk invokes callbacks in your program to indicate that something significant happened to a widget For either case, you can provide a code block or a Ruby Proc object that specifies how the application responds to the event or callback.
Let's take a look at how to use the bind method to associate basic window system events with the Ruby procedures that handle them. The simplest form of bind takes as its inputs a string indicating the event name and a code block that Tk uses to handle the event.
For example, to catch the ButtonRelease event for the first mouse button on some widget, you'd write:
someWidget.bind('ButtonRelease-1') {
    ....code block to handle this event...
}
An event name can include additional modifiers and details. A modifier is a string like Shift, Control or Alt, indicating that one of the modifier keys was pressed.
So, for example, to catch the event that's generated when the user holds down the Ctrl key and clicks the right mouse button
someWidget.bind('Control-ButtonPress-3', proc { puts "Ouch!" })
Many Ruby/Tk widgets can trigger callbacks when the user activates them, and you can use the command callback to specify that a certain code block or procedure is invoked when that happens. As seen earlier, you can specify the command callback procedure when you create the widget:
helpButton = TkButton.new(buttonFrame) {
   text "Help"
   command proc { showHelp }
}
or you can assign it later, using the widget's command method:
helpButton.command proc { showHelp }
Since the command method accepts either procedures or code blocks, you could also write the previous code example as:
helpButton = TkButton.new(buttonFrame) {
   text "Help"
   command { showHelp }
}
There are following basic event types, which you can use in your Ruby/Tk application:
TagEvent Description
"1" (one)Clicked left mouse button
"ButtonPress-1"Clicked left mouse button
"Enter"Moved mouse inside
"Leave"Moved mouse outside
"Double-1"Double clicked
"B3-Motion"Right button drag from one position to another
Control-ButtonPress-3Right button is pressed along with Ctrl Key.
Alt-ButtonPress-1Let button is pressed along with Alt Key.

The configure Method

The configure method can be used to set and retrieve any widget configuration values. For example, to change the width of a button you can call configure method any time as follows:
require "tk"

button = TkButton.new {
  text 'Hello World!'
  pack
}
button.configure('activebackground', 'blue')
Tk.mainloop
To get the value for a current widget, just supply it without a value as follows:
color = button.configure('activebackground')
You can also call configure without any options at all, which will give you a listing of all options and their values.

The cget Method:

For simply retrieving the value of an option, configure returns more information than you generally want. The cget method returns just the current value.
color = button.cget('activebackground')

Web Services with Ruby - SOAP4R

What is SOAP ?

The Simple Object Access Protocol (SOAP) is a cross-platform and language-independent RPC protocol based on XML and, usually (but not necessarily) HTTP.
It uses XML to encode the information that makes the remote procedure call, and HTTP to transport that information across a network from clients to servers and vice versa.
SOAP has several advantages over other technologies like COM, CORBA etc: for example, its relatively cheap deployment and debugging costs, its extensibility and ease-of-use, and the existence of several implementations for different languages and platforms.
Please refer to out simple tutorial to understand SOAP.
This tutorial will make you familiar to the SOAP implementation for Ruby (SOAP4R). This is a basic tutorial, so if you need a deep detail, you would need to refer other resources.

Installing SOAP4R:

SOAP4R is the SOAP implementation for Ruby developed by Hiroshi Nakamura and can be downloaded from:
NOTE: There may be a great chance that you already have installed this component.
Download SOAP
If you are aware of gem utility then you can use following command to install SOAP4R and related packages.
$ gem install soap4r --include-dependencies
If you are working on Windows then you need to download a zipped file from the above location and need to install it using standard installation method by running ruby install.rb.

Writing SOAP4R Servers:

SOAP4R supports two different types of servers:
  • CGI/FastCGI based (SOAP::RPC::CGIStub)
  • Standalone (SOAP::RPC:StandaloneServer)
This tutorial will give deatail on writing a stand alone server. There are following steps involved in writing a SOAP server:

Step 1 - Inherit SOAP::RPC::StandaloneServer Class:

To implement your own stand alone server you need to write a new class which will be child of SOAP::StandaloneServer as follows:
class MyServer < SOAP::RPC::StandaloneServer
  ...............
end
NOTE: If you want to write a FastCGI based server then you need to take SOAP::RPC::CGIStub as parent class, rest of the procedure will remain same.

Step 2 - Define Handler Methods:

Second step is to write your Web Services methods, which you would like to expose to the outside world.
They can be written as simple Ruby methods. For example, let's write two methods to add two numbers and divide two numbers:
class MyServer < SOAP::RPC::StandaloneServer
   ...............

   # Handler methods
   def add(a, b)
      return a + b
   end
   def div(a, b) 
      return a / b 
   end
end

Step 3 - Expose Handler Methods:

Next step is to add our defined methods to our server. The initialize method is used to expose service methods with one of the two following methods:
class MyServer < SOAP::RPC::StandaloneServer
   def initialize(*args)
      add_method(receiver, methodName, *paramArg)
   end
end
Here is the description of the parameters:
ParamterDescription
receiverThe object that contains the methodName method. you define the service methods in the same class as the methodDef method, this parameter is self.
methodNameThe name of the method that is called due to a RPC request.
paramArgSpecifies, when given, the parameter names and parameter modes.
To understand the usage of inout or out parameters, consider the following service method that takes two parameters (inParam and inoutParam), returns one normal return value (retVal) and two further parameters: inoutParam and outParam:
def aMeth(inParam, inoutParam)
   retVal = inParam + inoutParam
   outParam = inParam . inoutParam
   inoutParam = inParam * inoutParam
   return retVal, inoutParam, outParam
end
Now, we can expose this method as follows:
add_method(self, 'aMeth', [
    %w(in inParam),
    %w(inout inoutParam),
    %w(out outParam),
    %w(retval return)
])

Step 4 - Start the Server:

The final step is to start your server by instantiating one instance of the derived class and calling start method.
myServer = MyServer.new('ServerName',
                        'urn:ruby:ServiceName', hostname, port)

myServer.start
Here is the description of required parameters :
ParamterDescription
ServerNameA server name, you can give what you like most.
urn:ruby:ServiceNameHere urn:ruby is constant but you can give a unique ServiceName name for this server.
hostnameSpecifies the hostname on which this server will listen.
portAn available port number to be used for the web service.

Example:

Now, using above steps, let us write one standalone server:
require "soap/rpc/standaloneserver"

begin
   class MyServer < SOAP::RPC::StandaloneServer

      # Expose our services
      def initialize(*args)
         add_method(self, 'add', 'a', 'b')
         add_method(self, 'div', 'a', 'b')
      end

      # Handler methods
      def add(a, b)
         return a + b
      end
      def div(a, b) 
         return a / b 
      end
  end
  server = MyServer.new("MyServer", 
            'urn:ruby:calculation', 'localhost', 8080)
  trap('INT){
     server.shutdown
  }
  server.start
rescue => err
  puts err.message
end
When executed, this server application starts a standalone SOAP server on localhost and listens for requests on port 8080. It exposes one service methods, add and div, which takes two parameters and return the result.
Now, you can run this server in background as follows:
$ ruby MyServer.rb&

Writing SOAP4R Clients:

The SOAP::RPC::Driver class provides support for writing SOAP client applications. This tutorial will describe this class and demonstrate its usage on the basis of an application.
Following is the bare minimum information you would need to call a SOAP service:
  • The URL of the SOAP service (SOAP Endpoint URL)
  • The namespace of the service methods (Method Namespace URI)
  • The names of the service methods and their parameters
Now, we will write a SOAP client which would call service methods defined in above example, named add and div.
Here are the main steps to create a SOAP client:

Step 1 - Create a SOAP Driver Instance:

We create an instance of SOAP::RPC::Driver by calling its new method as follows:
SOAP::RPC::Driver.new(endPoint, nameSpace, soapAction)
Here is the description of required parameters :
ParamterDescription
endPointURL of the SOAP server to connect with.
nameSpace The namespace to use for all RPCs done with this SOAP::RPC::Driver object.
soapActionA value for the SOAPAction field of the HTTP header. If nil this defaults to the empty string ""

Step 2 - Add Service Methods:

To add a SOAP service method to a SOAP::RPC::Driver we can call the following method using SOAP::RPC::Driver instance:
driver.add_method(name, *paramArg)
Here is the description of the parameters:
ParamterDescription
nameThe name of the remote web service method.
paramArg Specifies the names of the remote procedures' parameters.

Step 3 - Invoke SOAP service:

The final step is to invoice SOAP service using SOAP::RPC::Driver instance as follows:
result = driver.serviceMethod(paramArg...)
Here serviceMethod is the actual web service method and paramArg... is the list parameters required to pass in the service method.

Example:

Based on the above steps, we will write a SOAP client as follows:
#!/usr/bin/ruby -w

require 'soap/rpc/driver'

NAMESPACE = 'urn:ruby:calculation'
URL = 'http://localhost:8080/'

begin
   driver = SOAP::RPC::Driver.new(URL, NAMESPACE)
   
   # Add remote sevice methods
   driver.add_method('add', 'a', 'b')

   # Call remote service methods
   puts driver.add(20, 30)
rescue => err
   puts err.message
end

Further Readings:

I have explained you just very basic concepts of Web Services with Ruby. If you want to drill down it further, then there is following link to find more details on Web Services with Ruby.