PL/Ruby

PL/Ruby is a loadable procedural language for the Postgres database system that enable the Ruby language to create functions and trigger procedures

Functions and triggers are singleton methods of the module PLtemp.

WARNING

if PL/Ruby was compiled with --disable-conversion, all arguments (to the function or the triggers) are passed as string values, except for NULL values represented by Qnil. In this case you must explicitely call a conversion function (like to_i) if you want to use an argument as an integer

Defining function in PL Ruby

To create a function in the PL/Ruby language use the syntax

CREATE FUNCTION funcname(arguments_type) RETURNS type AS '

 # PL/Ruby function body

' LANGUAGE 'plruby';

when calling the function in a query, the arguments are given as string values in the array args. To create a little max function returning the higher of two int4 values write :

CREATE FUNCTION ruby_max(int4, int4) RETURNS int4 AS '
    if args[0].to_i > args[1].to_i
        return args[0]
    else
        return args[1]
    end
' LANGUAGE 'plruby';

Tuple arguments are given as hash. Here is an example that defines the overpaid_2 function (as found in the older Postgres documentation) in PL/Ruby.

CREATE FUNCTION overpaid_2 (EMP) RETURNS bool AS '
    args[0]["salary"].to_f > 200000 || 
       (args[0]["salary"].to_f > 100000 && args[0]["age"].to_i < 30)
' LANGUAGE 'plruby';

Warning : with PostgreSQL >= 7.4 "array" are given as a ruby Array

For example to define a function (int4[], int4) and return int4[], in version < 7.4 you write

CREATE FUNCTION ruby_int4_accum(_int4, int4) RETURNS _int4 AS '
    if /\\{(\\d+),(\\d+)\\}/ =~ args[0]
        a, b = $1, $2
        newsum = a.to_i + args[1].to_i
        newcnt = b.to_i + 1
    else
        raise "unexpected value #{args[0]}"
    end
    "{#{newsum},#{newcnt}}"
' LANGUAGE 'plruby';

This must now (>= 7.4) be written

CREATE FUNCTION ruby_int4_accum(_int4, int4) RETURNS _int4 AS '
   a = args[0]
   [a[0].to_i + args[1].to_i, a[1].to_i + 1]
' LANGUAGE 'plruby';

Release PostgreSQL 8.0

With this version, plruby can have named arguments and the previous functions can be written

CREATE FUNCTION ruby_max(a int4, b int4) RETURNS int4 AS '
    if a > b
        a
    else
        b
    end
' LANGUAGE 'plruby';


CREATE FUNCTION overpaid_2 (emp EMP) RETURNS bool AS '
    emp["salary"] > 200000 || 
       (emp["salary"] > 100000 && emp["age"] < 30)
' LANGUAGE 'plruby';

With this version, you can also use transaction. For example

plruby_test=# create table tu (a int, b int);
CREATE TABLE
plruby_test=# create or replace function tt(abort bool) returns bool as '
plruby_test'#    transaction do |txn|
plruby_test'#       PL.exec("insert into tu values (1, 2)")
plruby_test'#       transaction do |txn1|
plruby_test'#          PL.exec("insert into tu values (3, 4)")
plruby_test'#          txn1.abort
plruby_test'#       end
plruby_test'#       PL.exec("insert into tu values (5, 6)")
plruby_test'#       txn.abort if abort
plruby_test'#    end
plruby_test'#    abort
plruby_test'# ' language 'plruby';
CREATE FUNCTION
plruby_test=# 
plruby_test=# select tt(true);
 tt 
----
 t
(1 row)

plruby_test=# select * from tu;
 a | b 
---+---
(0 rows)

plruby_test=# select tt(false);
 tt 
----
 f
(1 row)

plruby_test=# select * from tu;
 a | b 
---+---
 1 | 2
 5 | 6
(2 rows)

plruby_test=# 

Function returning SET (SFRM Materialize)

The return type must be declared as SETOF

The function must call yield to return rows or return a String which must be a valid SELECT statement

For example to concatenate 2 rows create the function

plruby_test=# CREATE FUNCTION tu(varchar) RETURNS setof record
plruby_test-# AS '
plruby_test'#    size = PL.column_name(args[0]).size
plruby_test'#    res = nil
plruby_test'#    PL::Plan.new("select * from #{args[0]}", 
plruby_test'#                 "block" => 50).each do |row|
plruby_test'#       if res.nil?
plruby_test'#          res = row.values
plruby_test'#       else
plruby_test'#          res.concat row.values
plruby_test'#          yield res
plruby_test'#          res = nil
plruby_test'#       end
plruby_test'#    end
plruby_test'#    if res
plruby_test'#       res.concat Array.new(size)
plruby_test'#       yield res
plruby_test'#    end
plruby_test'# ' language 'plruby';
CREATE FUNCTION
plruby_test=# 
plruby_test=# select * from tt;
 a | b  
---+----
 1 |  2
 3 |  4
 5 |  6
 7 |  8
 9 | 10
(5 rows)

plruby_test=# select * from tu('tt') as tbl(a int, b int, c int, d int);
 a | b  | c | d 
---+----+---+---
 1 |  2 | 3 | 4
 5 |  6 | 7 | 8
 9 | 10 |   |  
(3 rows)

plruby_test=# 

Function returning SET (ExprMultiResult)

The return type must be declared as SETOF

The function is called until it returns nil

The method PL#context and PL#context= give the possibility to store information between the call

For example

plruby_test=# create or replace function vv(int) returns setof int as '
plruby_test'#    i = PL.context || 0
plruby_test'#    if i >= args[0].to_i
plruby_test'#       nil
plruby_test'#    else
plruby_test'#       PL.context = i + 1
plruby_test'#    end
plruby_test'# ' language plruby;
CREATE FUNCTION
plruby_test=# 
plruby_test=# select * from uu;
 b 
---
 2
(1 row)

plruby_test=# 
plruby_test=# select *,vv(3) from uu;
 b | vv 
---+----
 2 |  1
 2 |  2
 2 |  3
(3 rows)

plruby_test=# 

Trigger procedures in PL Ruby

Trigger procedures are defined in Postgres as functions without arguments and a return type of trigger. In PL/Ruby the procedure is called with 4 arguments :

new (hash, tainted)
an hash containing the values of the new table row on INSERT/UPDATE actions, or empty on DELETE.
old (hash, tainted)
an hash containing the values of the old table row on UPDATE/DELETE actions, or empty on INSERT
args (array, tainted, frozen)
An array of the arguments to the procedure as given in the CREATE TRIGGER statement
tg (hash, tainted, frozen)
The following keys are defined
name
The name of the trigger from the CREATE TRIGGER statement.
relname
The name of the relation who has fired the trigger
relid
The object ID of the table that caused the trigger procedure to be invoked.
relatts
An array containing the name of the tables field.
when
The constant PL::BEFORE, PL::AFTER or PL::UNKNOWN depending on the event of the trigger call.
level
The constant PL::ROW or PL::STATEMENT depending on the event of the trigger call.
op
The constant PL::INSERT, PL::UPDATE or PL::DELETE depending on the event of the trigger call.

The return value from a trigger procedure is one of the constant PL::OK or PL::SKIP, or an hash. If the return value is PL::OK, the normal operation (INSERT/UPDATE/DELETE) that fired this trigger will take place. Obviously, PL::SKIP tells the trigger manager to silently suppress the operation. The hash tells PL/Ruby to return a modified row to the trigger manager that will be inserted instead of the one given in new (INSERT/UPDATE only). Needless to say that all this is only meaningful when the trigger is BEFORE and FOR EACH ROW.

Here's a little example trigger procedure that forces an integer value in a table to keep track of the # of updates that are performed on the row. For new row's inserted, the value is initialized to 0 and then incremented on every update operation :

CREATE FUNCTION trigfunc_modcount() RETURNS TRIGGER AS '
    case tg["op"]
    when PL::INSERT
        new[args[0]] = 0
      when PL::UPDATE
          new[args[0]] = old[args[0]].to_i + 1
      else
          return PL::OK
      end
      new
  ' LANGUAGE 'plruby';

  CREATE TABLE mytab (num int4, modcnt int4, descr text);

  CREATE TRIGGER trig_mytab_modcount BEFORE INSERT OR UPDATE ON mytab
      FOR EACH ROW EXECUTE PROCEDURE trigfunc_modcount('modcnt');

A more complex example (extract from test_setup.sql in the distribution) which use the global variable $Plans to store a prepared plan

create function trig_pkey2_after() returns trigger as '
   if ! $Plans.key?("plan_dta2_upd")
       $Plans["plan_dta2_upd"] = 
            PL::Plan.new("update T_dta2 
                          set ref1 = $3, ref2 = $4
                          where ref1 = $1 and ref2 = $2",
                         ["int4", "varchar", "int4", "varchar" ]).save
       $Plans["plan_dta2_del"] = 
            PL::Plan.new("delete from T_dta2 
                          where ref1 = $1 and ref2 = $2", 
                         ["int4", "varchar"]).save
   end

   old_ref_follow = false
   old_ref_delete = false

   case tg["op"]
   when PL::UPDATE
       new["key2"] = new["key2"].upcase
       old_ref_follow = (new["key1"] != old["key1"]) || 
                        (new["key2"] != old["key2"])
   when PL::DELETE
       old_ref_delete = true
   end

   if old_ref_follow
       n = $Plans["plan_dta2_upd"].exec([old["key1"], old["key2"], new["key1"],
new["key2"]])
       warn "updated #{n} entries in T_dta2 for new key in T_pkey2" if n != 0
   end

   if old_ref_delete
       n = $Plans["plan_dta2_del"].exec([old["key1"], old["key2"]])
       warn "deleted #{n} entries from T_dta2" if n != 0
   end

   PL::OK
' language 'plruby';

create trigger pkey2_after after update or delete on T_pkey2
 for each row execute procedure
 trig_pkey2_after();

plruby_singleton_methods

Sometime it can be usefull to define methods (in pure Ruby) which can be called from a PL/Ruby function or a PL/Ruby trigger.

In this case, you have 2 possibilities

just close the current definition of the function (or trigger) with a end and define your singleton method without the final end

Here a small and useless example

plruby_test=# CREATE FUNCTION tutu() RETURNS int4 AS '
plruby_test'#     toto(1, 3) + toto(4, 4)
plruby_test'# end
plruby_test'# 
plruby_test'# def PLtemp.toto(a, b)
plruby_test'#     a + b
plruby_test'# ' LANGUAGE 'plruby';
CREATE
plruby_test=# select tutu();
tutu
----
  12
(1 row)

plruby_test=#

At load time, PL/Ruby look if it exist a table plruby_singleton_methods and if found try, for each row, to define singleton methods with the template :

def PLtemp.#{name} (#{args})
    #{body}
end

The previous example can be written (you have a more complete example in test/plp/test_setup.sql)

plruby_test=# SELECT * FROM plruby_singleton_methods;
name|args|body 
----+----+-----
toto|a, b|a + b
(1 row)

plruby_test=# CREATE FUNCTION tutu() RETURNS int4 AS '
plruby_test'#     toto(1, 3) + toto(4, 4)
plruby_test'# ' LANGUAGE 'plruby';
CREATE
plruby_test=# select tutu();
tutu
----
  12
(1 row)

plruby_test=#

Conversion

If the conversions was not disabled (--disable-conversion), the following conversions are made

PostgreSQL             Ruby
----------             ----
OID                    Fixnum
INT2OID                Fixnum
INT4OID                Fixnum
INT8OID                Fixnum (or Bignum)
FLOAT4OID              Float
FLOAT8OID              Float
CASHOID                Float
NUMERICOID             Float
BOOLOID                true, false
ABSTIMEOID             Time
RELTIMEOID             Time
TIMEOID                Time
TIMETZOID              Time
TIMESTAMPOID           Time
TIMESTAMPTZOID         Time
DATEOID                Time
INTERVALOID            Time
TINTERVALOID           Tinterval (new Ruby class)
BITOID                 BitString (new Ruby class)
VARBITOID              BitString (new Ruby class)
INETOID                NetAddr   (new Ruby class)
CIDROID                NetAddr   (new Ruby class)
MACADDROID             MacAddr   (new Ruby class)
POINTOID               Point     (new Ruby class)
LSEGOID                Segment   (new Ruby class)
BOXOID                 Box       (new Ruby class)
PATHOID                Path      (new Ruby class)
POLYGONOID             Polygon   (new Ruby class)
CIRCLEOID              Circle    (new Ruby class)

all others OID are converted to a String object

Class and modules

Global

transaction {|txn| }
create a new transaction, yield an object PL::Transaction
warn [level], message

Ruby interface to PostgreSQL elog()

Possible value for level are NOTICE, DEBUG and NOIND

Use raise() if you want to simulate elog(ERROR, "...")

$Plans (hash, tainted)
can be used to store prepared plans.

module PL

general module
args_type
Return the type of the arguments given to the function
column_name(table)
Return the name of the columns for the table
column_type(table)
return the type of the columns for the table
context
Return the context (or nil) associated with a SETOF function (ExprMultiResult)
context=
Set the context for a SETOF function (ExprMultiResult)
quote(string)
Duplicates all occurences of single quote and backslash characters. It should be used when variables are used in the query string given to spi_exec or spi_prepare (not for the value list on execp).
result_name
Return the name of the columns for a function returning a SETOF
result_type
Return the type of the columns for a function returning a SETOF or the type of the return value
result_size
Return the number of columns for a function returning a SETOF
result_description
Return the table description given to a function returning a SETOF
exec(string [, count [, type]])
spi_exec(string [, count [, type]])
Call parser/planner/optimizer/executor for query. The optional count value tells spi_exec the maximum number of rows to be processed by the query.
SELECT

If the query is a SELECT statement, an array is return (if count is not specified or with a value > 1). Each element of this array is an hash where the key is the column name.

If type is specified it can take the value

  • "array" return for each column an array with the element ["name", "value", "type", "len", "typeid"]
  • "hash" return for each column an hash with the keys {"name", "value", "type", "len", "typeid"}
  • "value" return all values

For example this procedure display all rows in the table pg_table.

CREATE FUNCTION pg_table_dis() RETURNS int4 AS '
res = PLruby.exec("select * from pg_class")
res.each do |x|
    warn "======================"
    x.each do |y, z|
        warn "name = #{y} -- value = #{z}"
    end
    warn "======================"
end
return res.size
' LANGUAGE 'plruby';

A block can be specified, in this case a call to yield() will be made.

If count is specified with the value 1, only the first row (or FALSE if it fail) is returned as a hash. Here a little example :

CREATE FUNCTION pg_table_dis() RETURNS int4 AS '
   PL.exec("select * from pg_class", 1) { |y, z|
      warn "name = #{y} -- value = #{z}"
  }
  return 1
' LANGUAGE 'plruby';

Another example with count = 1

create table T_pkey1 (
    skey1        int4,
    skey2        varchar(20),
    stxt         varchar(40)
);

create function toto() returns bool as '
   warn("=======")
   PL.exec("select * from T_pkey1", 1, "hash") do |a|
      warn(a.inspect)
   end
   warn("=======")
   PL.exec("select * from T_pkey1", 1, "array") do |a|
      warn(a.inspect)
   end
   warn("=======")
   PL.exec("select * from T_pkey1", 1) do |a|
      warn(a.inspect)
   end
   warn("=======")
   return true
' language 'plruby';


plruby_test=# select toto();
NOTICE:  =======
NOTICE:  {"name"=>"skey1", "typeid"=>23, "type"=>"int4", "value"=>"12", "len"=>4}
NOTICE:  {"name"=>"skey2", "typeid"=>1043, "type"=>"varchar", "value"=>"a", "len"=>20}
NOTICE:  {"name"=>"stxt", "typeid"=>1043, "type"=>"varchar", "value"=>"b", "len"=>40}
NOTICE:  =======
NOTICE:  ["skey1", "12", "int4", 4, 23]
NOTICE:  ["skey2", "a", "varchar", 20, 1043]
NOTICE:  ["stxt", "b", "varchar", 40, 1043]
NOTICE:  =======
NOTICE:  ["skey1", "12"]
NOTICE:  ["skey2", "a"]
NOTICE:  ["stxt", "b"]
NOTICE:  =======
 toto 
------
 t
(1 row)

plruby_test=# 
SELECT INTO, INSERT, UPDATE, DELETE
return the number of rows insered, updated, deleted, ...
UTILITY
return TRUE
prepare(string[, types])
spi_prepare(string[, types])
prepare(string, "types" => types, "count" => count, "output" => type, "tmp" => true)

Deprecated : See PL::Plan::new and PL::Plan#save

Prepares AND SAVES a query plan for later execution. It is a bit different from the C level SPI_prepare in that the plan is automatically copied to the toplevel memory context.

If the query references arguments, the type names must be given as a Ruby array of strings. The return value from prepare is a PL::Plan object to be used in subsequent calls to PL::Plan#exec.

If the hash given has the keys count, output these values will be given to the subsequent calls to each

class PL::Plan

class for prepared plan
initialize(string, "types" => types, "count" => count, "output" => type, "save" => false)

Prepares a query plan for later execution.

If the query references arguments, the type names must be given as a Ruby array of strings.

If the hash given has the keys output, count these values will be given to the subsequent calls to each

If "save" as a true value, the plan will be saved

exec(values, [count [, type]])
execp(values, [count [, type]])
exec("values" => values, "count" => count, "output" => type)
execp("values" => values, "count" => count, "output" => type)

Execute a prepared plan from PL::PLan::new with variable substitution. The optional count value tells PL::Plan#exec the maximum number of rows to be processed by the query.

If there was a typelist given to PL::Plan::new, an array of values of exactly the same length must be given to PL::Plan#exec as first argument. If the type list on PL::Plan::new was empty, this argument must be omitted.

If the query is a SELECT statement, the same as described for PL#exec happens for the loop-body and the variables for the fields selected.

If type is specified it can take the values

Here's an example for a PL/Ruby function using a prepared plan :

CREATE FUNCTION t1_count(int4, int4) RETURNS int4 AS '
    if ! $Plans.key?("plan")
        # prepare the saved plan on the first call
        $Plans["plan"] = PL::Plan.new("SELECT count(*) AS cnt FROM t1 
                                       WHERE num >= $1 AND num <= $2",
                                      ["int4", "int4"]).save
    end
    n = $Plans["plan"].exec([args[0], args[1]], 1)
    n["cnt"]
' LANGUAGE 'plruby';
cursor(name = nil, "values" => values, "output" => type)

Create a new object PL::Cursor

If output is specified it can take the values

If there was a typelist given to PL::Plan::new, an array of values of exactly the same length must be given to PL::Plan#cursor

each(values, [count [, type ]]) { ... }
fetch(values, [count [, type ]]) { ... }
each("values" => values, "count" => count, "output" => type) { ... }
fetch("values" => values, "count" => count, "output" => type) { ... }

Same then #exec but a call to SPI_cursor_open(), SPI_cursor_fetch() is made.

Can be used only with a block and a SELECT statement

create function toto() returns bool as '
       plan = PL::Plan.new("select * from T_pkey1")
       warn "=====> ALL"
       plan.each do |x|
          warn(x.inspect)
       end
       warn "=====> FIRST 2"
       plan.each("count" => 2) do |x|
          warn(x.inspect)
       end
       return true
' language 'plruby';

plruby_test=# select * from T_pkey1;
 skey1 | skey2 | stxt 
-------+-------+------
    12 | a     | b
    24 | c     | d
    36 | e     | f
(3 rows)

plruby_test=# 
plruby_test=# select toto();
NOTICE:  =====> ALL
NOTICE:  {"skey1"=>"12", "skey2"=>"a", "stxt"=>"b"}
NOTICE:  {"skey1"=>"24", "skey2"=>"c", "stxt"=>"d"}
NOTICE:  {"skey1"=>"36", "skey2"=>"e", "stxt"=>"f"}
NOTICE:  =====> FIRST 2
NOTICE:  {"skey1"=>"12", "skey2"=>"a", "stxt"=>"b"}
NOTICE:  {"skey1"=>"24", "skey2"=>"c", "stxt"=>"d"}
 toto 
------
 t
(1 row)

plruby_test=# 
release
Release a query plan
save
Save a query plan for later execution. The plan is copied to the toplevel memory context.

class PL::Cursor

A cursor is created with the method PL::Plan#cursor
close
Closes a cursor
each {|row| ... }
Iterate over all rows (forward)
fetch(count = 1)
row(count = 1)

Fetches some rows from a cursor

if count > 0 fetch forward else backward

move(count)
Move a cursor : if count > 0 move forward else backward
reverse_each {|row| ... }
Iterate over all rows (backward)
rewind
Positions the cursor at the beginning of the table

class PL::Transaction

a transaction is created with the global function transaction(). Only available with PostgreSQL >= 8.0

abort
Abort the transaction
commit
Commit the transaction

class BitString

The class BitString implement the PostgreSQL type bit and bit varying

The modules Comparable and Enumerable are included

from_string(string, length = strlen(string))
Convert a String to a BitString
<=>(other)

comparison function for 2 BitString objects

All bits are considered and additional zero bits may make one string smaller/larger than the other, even if their zero-padded values would be the same.

+(other)
Concatenate self and other
&(other)
AND operator
|(other)
OR operator
^(other)
XOR operator
~
NOT operator
<<(lshft)
LEFT SHIFT operator
>>(rshft)
RIGHT SHIFT operator
[*args]

Element reference with the same syntax that for a String object

Return a BitString or a Fixnum 0, 1

bitstring[fixnum]
bitstring[fixnum, fixnum]
bitstring[range]
bitstring[regexp]
bitstring[regexp, fixnum]
bitstring[string]
bitstring[other_bitstring]
[*args] = val

Element assignment with the same syntax that for a String object

bitstring[fixnum] = fixnum
bitstring[fixnum] = string_or_bitstring
bitstring[fixnum, fixnum] = string_or_bitstring
bitstring[range] = string_or_bitstring
bitstring[regexp] = string_or_bitstring
bitstring[regexp, fixnum] = string_or_bitstring
bitstring[other_str] = string_or_bitstring
concat(other)
append other to self
each
iterate other each bit
include?(other)
return true if other is included in self
index(other)

return the position of other in self

return nil if other is not included in self

initialize(init, nbits = -1)

create a new BitString object with nbits bits

init can be a Fixnum or a String

For a String the first character can be 'x', 'X' for and hexadecimal representation, or 'b', 'B' for a binary representation. The default is a binary representation

length
return the length of self in bits
octet_length
return the length of self in octets
push(other)
append other to self
to_i
convert self to a Fixnum
to_s
convert self to a String

class NetAddr

The class NetAddr implement the PostgreSQL type inet and cidr

The module Comparable is included

from_string(string, cidr = false)
Convert a String to a NetAddr
<=>(other)

comparison function for 2 NetAddr objects

comparison is first on the common bits of the network part, then on the length of the network part, and then on the whole unmasked address.

abbrev
return the abbreviated display format as a String object
broadcast
return the broadcast address from the network
contain?(other)
return true if other is included in self
contain_or_equal?(other)
return true if other is included in self, or equal
contained?(other)
return true if self is included in other
contained_or_equal?(other)
return true if self is included in other, or equal
family
return the String "AF_INET" or "AF_INET6"
first
return the first address in the network
host
extract the IP address and return it as a String
hostmask
return the host mask for network
initialize(string, cidr = false)
create a NetAddr from a String
last
return the last address in the network
masklen
return the length of the netmask
netmask
return the netmask for the network
network
return the network part of the address
set_masklen(len)
return a new NetAddr with netmask length len
to_s
return the string representation of the address

class MacAddr

The MacAddr implement the PostgreSQL type macaddr

The module Comparable is included

from_string(string, cidr = false)
Convert a String to a MacAddr
<=>(other)
comparison function for 2 MacAddr objects
initialize(string)
create a MacAddr from a String
to_s
return the string representation of the MAC address
truncate
return a new object with the last 3 bytes set to zero

class Tinterval

The Tinterval implement the PostgreSQL type tinterval

from_string(string)
Convert a String (PostgreSQL representation) to a Tinterval
high
return a Time which is the high value of the interval
high=(time)
set the high value for the interval
initialize(low, high)
create a Tinterval with the 2 Time objects low and high
low
return a Time which is the low value of the interval
low=(time)
set the low value for the interval
to_s
return the string representation of the object

class Box

The Box implement the PostgreSQL type box

The module Comparable is included

from_string(string)
Convert a String (PostgreSQL representation) to a Box object
+(point)
translate (right, up) self
-(point)
translate (left, down) self
*(point)
scale and rotate self
/(point)
scale and rotate self
===(other)
return true if the 2 boxes self and other are identical
<=>(other)
comparison operator for 2 Box based on the area of the 2 objects, i.e. self.area <=> box.area
above?(other)
return true if self is above other
area
return the area of the Box
below?(other)
return true if self is below other
center
return the center point of the Box
closest(other)

closest point to other

other can be a Point, or Segment

contain?(other)
return true if self contain other
contained?(other)
return true if self is contained by other
diagonal
return a line Segment which happens to be the positive-slope diagonal of Box
height
return the height of the Box (vertical magnitude)
in?(other)
return true if self is contained by other
initialize(*args)

create a new Box object

args can be 2 Point objects (low, high) or 4 Float objects (low.x, low.y, high.x, high.y)

intersection(other)
returns the overlapping portion of two boxes, or nil if they do not intersect.
intersect?(segment)

returns true if the Segment segment intersect with the Box

Segment completely inside box counts as intersection. If you want only segments crossing box boundaries, try converting Box to Path first.

left?(other)
return true if self is strictly left of other
overlap?(other)
return true if self overlap other
overleft?(other)
return true if the right edge of self is to the left of the right edge of other
overright?(other)
return true if the left edge of self is to the right of the left edge of other
right?(other)
return true if self is strictly right of other
same?(other)
return true if the 2 boxes self and other are identical
to_circle
convert a Box to a Circle
to_point
return the center Point of the Box
to_polygon
convert a Box to a Polygon
to_segment
return a line Segment which happens to be the positive-slope diagonal of Box
width
return the width of the Box (horizontal magnitude)

class Path

The Path implement the PostgreSQL type path

The module Comparable is included

from_string(string)
Convert a String (PostgreSQL representation) to a Path
<<(path)
concatenate the two paths (only if they are both open)
+(point)
translate (right, up) self
-(point)
translate (left, down) self
*(point)
scale and rotate self
/(point)
scale and rotate self
<=>(other)
comparison function based on the path cardinality, i.e. self.npoints <=> other.npoints
close
make a closed path
closed?
return true if self is a closed path
concat(path)
concatenate the two paths (only if they are both open)
initialize(points, closed = false)
create a new Path object from the Array of Point points
length
return the length of self
npoints
return the path cardinality
open
make an open path
to_polygon
convert self to a Polygon object

class Point

The Point implement the PostgreSQL type point

The module Comparable is included

from_string(string)
Convert a String (PostgreSQL representation) to a Point
+(point)
translate (right, up) self
-(point)
translate (left, down) self
*(point)
scale and rotate self
/(point)
scale and rotate self
[indice]

return the coordinate

indice can have the value 0 or 1

[indice] = value

set the coordinate

indice can have the value 0 or 1

==(other)
return true if self and other are the same, i.e. self.x == other.x && self.y == other.y
above?(other)
return true if self is above other, i.e. self.y > other.y
below?(other)
return true if self is below other, i.e. self.y < other.y
contained?(other)

return true if self is contained in other

other can be Point, Polygon or a Circle object

horizontal?(other)
return true if self and other are horizontal, i.e. self.y == other.y
in?(other)

return true if self is contained in other

other can be Point, Polygon or a Circle object

initialize(x, y)
create a Point with the 2 Float object (x, y)
left?(other)
return true if self is at the left of other, i.e. self.x < other.x
on?(other)

return true if self is on other

other can be Point, Segment, Box or Path object

right?(other)
return true if self is at the right of other, i.e. self.x > other.x
vertical?(other)
return true if self and other are vertical, i.e. self.x == other.x
x
return x for self
x=(value)
set the x value for self
y
return y for self
y=(value)
set the y value for self

class Segment

The Segment implement the PostgreSQL type lseg

The module Comparable is included

from_string(string)
Convert a String (PostgreSQL representation) to a Segment
<=>(other)

comparison function for the 2 segments, returns

0  if self[0] == other[0] && self[1] == other[1]

1  if distance(self[0], self[1]) > distance(other[0], other[1]) 

-1 if distance(self[0], self[1]) < distance(other[0], other[1]) 
center
return the center of the segment
closest(other)

closest point to other

other can be a Point, Segment or Box

With a point, take the closest endpoint if the point is left, right, above, or below the segment, otherwise find the intersection point of the segment and its perpendicular through the point.

horizontal?
returns true if self is a horizontal Segment
initialize(point0, point1)
create a Segment from the 2 Point p0, p1
intersect?(other)
returns true if self and other intersect
intersection(other)
returns the Point where the 2 Segment self and other intersect or nil
length
return the length of self, i.e. the distnace between the 2 points
on?(other)

return true if self is on other

other can be a Segment, or a Box object

parallel?(other)
returns true if the 2 Segment self and other are parallel
perpendicular?(other)
returns true if self is perpendicular to other
to_point
conversion function to a Point, return the center of the segment
vertical?
returns true if self is a vertical Segment

class Polygon

The Polygon implement the PostgreSQL type polygon

from_string(string)
Convert a String (PostgreSQL representation) to a Polygon
==(other)
return true if self is the same as other, i.e. all the points are the same
center
return the center of self, i.e. create a circle and return its center
contain?(other)

return true if self contains other

other can be a Point or a Polygon

contained?(other)
return true if self is contained in other by determining if self bounding box is contained by other's bounding box.
in?(other)
return true if self is contained in other by determining if self bounding box is contained by other's bounding box.
initialize(points, closed = false)
create a new Polygon object from the Array of Point points
left?(other)
return true if self is strictly left of other, i.e. the right most point of self is left of the left most point of other
overleft?(other)
return true if self is overlapping or left of other, i.e. the left most point of self is left of the right most point of other
overright?(other)
return true if self is overlapping or right of other, i.e. the right most point of self is right of the left most point of other
overlap?(other)
return true if self and other overlap by determining if their bounding boxes overlap.
npoints
return the number of points in self
right?(other)
return true if self is strictly right of other, i.e. the left most point of self is right of the left most point of other
same?(other)
return true if self is the same as other, i.e. all the points are the same
to_box
convert self to a Box
to_circle
convert self to a Circle
to_path
convert self to a Path
to_point
convert self to a Point by returning its center

class Circle

The Circle implement the PostgreSQL type circle

The module Comparable is included

from_string(string)
Convert a String (PostgreSQL representation) to a Circle
+(point)
translate (right, up) self
-(point)
translate (left, down) self
*(point)
scale and rotate self
/(point)
scale and rotate self
<=>(other)
comparison function based on area, i.e. self.area <=> other.area
area
return the area
above?(other)
return true if self is entirely above other
below?(other)
return true if self is entirely below other
contain?(other)
return true if self contain other
contained?(other)
return true if self is contained in other
diameter
return the diameter
initialize(center, radius)

create a Circle object with center and radius

center can be a Point or an Array [x, y]

overlap?(other)
return true if self overlap other
overleft?(other)
return true if the right edge of self is to the left of the right edge of other
left?(other)
return true if self is strictly left of other
overright?(other)
return true if the left edge of self is to the right of the left edge of other
radius
return the radius
right?(other)
return true if self is strictly right of other
same?(other)
return true if self is the same than other, i.e. self.center == other.center && self.radius == other.radius
to_box
convert self to a Box
to_point
convert self to a Point by returning its center
to_polygon(npts)
convert self to a Polygon with npts Points