Robby on Rails: PL/Ruby loves RubyGems and DRbthoughts.sort_by{|t| t[:topic]}.collect tag:www.robbyonrails.com,2005:TypoTypo2006-10-11T16:15:41-04:00Robby Russellurn:uuid:0e1f7180-23f5-4f8a-abb6-07dd31b7a8632005-08-22T16:09:00-04:002006-10-11T16:15:41-04:00PL/Ruby loves RubyGems and DRb<p>I admit it. I have had a torrid love affair with procedural languages ever since I started playing with PostgreSQL. The ability to share logic amongst all the applications touching the same database server.. was…well… a breath of fresh air.</p>
<p>What is a procedural language in Postgresql?</p>
<p>PostgreSQL docs describe them as, ”…allows user-defined functions to be written in other languages besides <span class="caps">SQL</span> and C. “</p>
<p>Well, PostgreSQL has PLs for Perl, Python, Java, C, <span class="caps">PHP</span>… and even <span class="caps">RUBY</span>!</p>
<pre><code>
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';
</code></pre>
<p>PL/PGSQL is nice and all, but it’s not as fun as playing with Ruby. PL/Perl… well is perl, and PL/Python… is python. Both PL/Perl and PL/Python have untrusted variants. You see, they don’t want your PostgreSQL server to do anything harmful to the machine by being able to do stuff like system(‘cat /dev/null > /etc/passwd). But for some people, (like me) they want the flexibility of their language anyways. :-)</p>
<p>Note: Never do this if your system user that runs PostgreSQL has privileges to do anything harmful on your system.</p>
<p>The PL/Ruby documentation is minimal at the moment, but covers enough to get you started. I don’t know if many people are using it out there… but hopefully that is about to change! I’ve played with it a bit, but always wanted to be able to do stuff like require ‘rubygems’, but this is a feature of an untrusted language. I even found myself digging around in C code to see if I could figure out how to hack the plruby language to skip over those checks… but I am not a C programmer and I got lost in some header files.</p>
<p>Then it hit me. “Why haven’t you emailed the author?”</p>
<p>So I emailed the author of PL/Ruby, Guy Decoux, who responded pretty quickly with the answer to my dreams! Okay, I do have bigger dreams than this… but you get the idea.</p>
<p>First of all, some of you might be thinking, ”Why on Earth would you want to do this?”</p>
<p>Well, here is a simple example of how it could be used with RedCloth Let’s say that I want to be able to perform the following query from within <span class="caps">SQL</span>.</p>
<typo:code lang=ruby"><span class="caps">SELECT</span> redcloth(‘<strong>strong text</strong> and <em>emphasized text</em>‘);</code></pre>
<p>Why not do this in the application? Well, I do actually have a case where I have an older <span class="caps">PHP</span> application that I will be porting to Ruby in the future, but would like to give the application some access to some of the features of Ruby that I will be using, such as RedCloth.</p>
<p>Okay, so show me an example of one of these scary PostgreSQL functions.</p>
<pre><code>
CREATE FUNCTION redcloth(text) RETURNS text AS '
require ''rubygems''
require ''redcloth''
content = args[0]
rc = RedCloth.new(content)
return rc.to_html
' LANGUAGE 'plruby';
</code></pre>
<p>”Wait! You said this would be scary!?”</p>
<p>Well, PL/Ruby allows you to write… plain ole Ruby within your functions. (do you see where I am getting here?)</p>
<h2>PL/Ruby meets RedCloth</h2>
<pre><code>
rb=# SELECT redcloth('*strong text* and _emphasized text_');
redcloth
------------------------------------------------------------------
<p><strong>strong text</strong> and <em>emphasized text</em></p>
(1 row)
</code></pre>
<h2>PL/Ruby meets ShortURL</h2>
<pre><code>
CREATE FUNCTION rubyurlize(text) RETURNS text AS '
require ''rubygems''
require ''shorturl''
return ShortURL.shorten(args[0])
' LANGUAGE 'plruby';
</code></pre>
<p>...which allows for</p>
<pre><code>
rb=# SELECT
rb-# rubyurlize('http://www.robbyonrails.com/') as link1,
rb-# rubyurlize('http://moulon.inra.fr/ruby/plruby.html') as link2;
link1 | link2
--------------------------+------------------------
http://rubyurl.com/lyoKm | http://rubyurl.com/dTo
(1 row)
</code></pre>
<h2>PostgreSQL meets DRb</h2>
<p>Okay, this is one of the reasons why I wanted to play with PL/Ruby a bit more. Distributed Ruby Objects… from PostreSQL?</p>
<h3>What is DRb?</h3>
<p>If you don’t know already… per the description in <span class="caps">RDOC</span>, “dRuby is a distributed object system for Ruby. It allows an object in one Ruby process to invoke methods on an object in another Ruby process on the same or a different machine.”</p>
<p>It basically allows you to share an object to other machines… at the same time!</p>
<p>mmm…distributed objects…</p>
<h3>DRb Object</h3>
<p>Here is a simple ruby script that you would run from the shell. It creates a DRb object which accepts connections at localhost:9000.</p>
<pre><code>
#!/usr/bin/ruby
require 'drb'
class MyRemoteObject
def say(str)
return "You say #{str}. I say #{str.reverse.upcase}!"
end
end
server = MyRemoteObject.new
DRb.start_service('druby://localhost:9000', server)
DRb.thread.join
</code></pre>
<p>Start me up!</p>
<code>$ ruby mydrb.rb</code>
<p>Now that we have DRb running and listening for connections…we need a client to connect to it.</p>
<p>DRb function in PL/Ruby</p>
<p>Here is a very simple DRb client script and I just drop that into a PostgreSQL function.</p>
<pre><code>
CREATE FUNCTION drb_test(text) RETURNS text AS '
require ''drb''
DRb.start_service
ro = DRbObject.new(nil, ''druby://localhost:9000'')
return ro.say(args[0])
' LANGUAGE 'plruby';
</code></pre>
<p>The result?</p>
<pre><code>
rb=# SELECT drb_test('Potato');
drb_test
-------------------------------
You say Potato. I say OTATOP!
(1 row)
</code></pre>
<p>Are we having fun yet?</p>
<p>Okay, so how do I manage to get this to work? Well… for that, you will have to read my blog post, Installing untrusted PL/Ruby for PostgreSQL</p>
<p>Let’s all go get some coffee (or tea) and start playing with PL/Ruby today!</p><p>I admit it. I have had a torrid love affair with procedural languages ever since I started playing with PostgreSQL. The ability to share logic amongst all the applications touching the same database server.. was…well… a breath of fresh air.</p>
<p>What is a procedural language in Postgresql?</p>
<p>PostgreSQL docs describe them as, ”…allows user-defined functions to be written in other languages besides <span class="caps">SQL</span> and C. “</p>
<p>Well, PostgreSQL has PLs for Perl, Python, Java, C, <span class="caps">PHP</span>… and even <span class="caps">RUBY</span>!</p>
<pre><code>
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';
</code></pre>
<p>PL/PGSQL is nice and all, but it’s not as fun as playing with Ruby. PL/Perl… well is perl, and PL/Python… is python. Both PL/Perl and PL/Python have untrusted variants. You see, they don’t want your PostgreSQL server to do anything harmful to the machine by being able to do stuff like system(‘cat /dev/null > /etc/passwd). But for some people, (like me) they want the flexibility of their language anyways. :-)</p>
<p>Note: Never do this if your system user that runs PostgreSQL has privileges to do anything harmful on your system.</p>
<p>The PL/Ruby documentation is minimal at the moment, but covers enough to get you started. I don’t know if many people are using it out there… but hopefully that is about to change! I’ve played with it a bit, but always wanted to be able to do stuff like require ‘rubygems’, but this is a feature of an untrusted language. I even found myself digging around in C code to see if I could figure out how to hack the plruby language to skip over those checks… but I am not a C programmer and I got lost in some header files.</p>
<p>Then it hit me. “Why haven’t you emailed the author?”</p>
<p>So I emailed the author of PL/Ruby, Guy Decoux, who responded pretty quickly with the answer to my dreams! Okay, I do have bigger dreams than this… but you get the idea.</p>
<p>First of all, some of you might be thinking, ”Why on Earth would you want to do this?”</p>
<p>Well, here is a simple example of how it could be used with RedCloth Let’s say that I want to be able to perform the following query from within <span class="caps">SQL</span>.</p>
<typo:code lang=ruby"><span class="caps">SELECT</span> redcloth(‘<strong>strong text</strong> and <em>emphasized text</em>‘);</code></pre>
<p>Why not do this in the application? Well, I do actually have a case where I have an older <span class="caps">PHP</span> application that I will be porting to Ruby in the future, but would like to give the application some access to some of the features of Ruby that I will be using, such as RedCloth.</p>
<p>Okay, so show me an example of one of these scary PostgreSQL functions.</p>
<pre><code>
CREATE FUNCTION redcloth(text) RETURNS text AS '
require ''rubygems''
require ''redcloth''
content = args[0]
rc = RedCloth.new(content)
return rc.to_html
' LANGUAGE 'plruby';
</code></pre>
<p>”Wait! You said this would be scary!?”</p>
<p>Well, PL/Ruby allows you to write… plain ole Ruby within your functions. (do you see where I am getting here?)</p>
<h2>PL/Ruby meets RedCloth</h2>
<pre><code>
rb=# SELECT redcloth('*strong text* and _emphasized text_');
redcloth
------------------------------------------------------------------
<p><strong>strong text</strong> and <em>emphasized text</em></p>
(1 row)
</code></pre>
<h2>PL/Ruby meets ShortURL</h2>
<pre><code>
CREATE FUNCTION rubyurlize(text) RETURNS text AS '
require ''rubygems''
require ''shorturl''
return ShortURL.shorten(args[0])
' LANGUAGE 'plruby';
</code></pre>
<p>...which allows for</p>
<pre><code>
rb=# SELECT
rb-# rubyurlize('http://www.robbyonrails.com/') as link1,
rb-# rubyurlize('http://moulon.inra.fr/ruby/plruby.html') as link2;
link1 | link2
--------------------------+------------------------
http://rubyurl.com/lyoKm | http://rubyurl.com/dTo
(1 row)
</code></pre>
<h2>PostgreSQL meets DRb</h2>
<p>Okay, this is one of the reasons why I wanted to play with PL/Ruby a bit more. Distributed Ruby Objects… from PostreSQL?</p>
<h3>What is DRb?</h3>
<p>If you don’t know already… per the description in <span class="caps">RDOC</span>, “dRuby is a distributed object system for Ruby. It allows an object in one Ruby process to invoke methods on an object in another Ruby process on the same or a different machine.”</p>
<p>It basically allows you to share an object to other machines… at the same time!</p>
<p>mmm…distributed objects…</p>
<h3>DRb Object</h3>
<p>Here is a simple ruby script that you would run from the shell. It creates a DRb object which accepts connections at localhost:9000.</p>
<pre><code>
#!/usr/bin/ruby
require 'drb'
class MyRemoteObject
def say(str)
return "You say #{str}. I say #{str.reverse.upcase}!"
end
end
server = MyRemoteObject.new
DRb.start_service('druby://localhost:9000', server)
DRb.thread.join
</code></pre>
<p>Start me up!</p>
<code>$ ruby mydrb.rb</code>
<p>Now that we have DRb running and listening for connections…we need a client to connect to it.</p>
<p>DRb function in PL/Ruby</p>
<p>Here is a very simple DRb client script and I just drop that into a PostgreSQL function.</p>
<pre><code>
CREATE FUNCTION drb_test(text) RETURNS text AS '
require ''drb''
DRb.start_service
ro = DRbObject.new(nil, ''druby://localhost:9000'')
return ro.say(args[0])
' LANGUAGE 'plruby';
</code></pre>
<p>The result?</p>
<pre><code>
rb=# SELECT drb_test('Potato');
drb_test
-------------------------------
You say Potato. I say OTATOP!
(1 row)
</code></pre>
<p>Are we having fun yet?</p>
<p>Okay, so how do I manage to get this to work? Well… for that, you will have to read my blog post, Installing untrusted PL/Ruby for PostgreSQL</p>
<p>Let’s all go get some coffee (or tea) and start playing with PL/Ruby today!</p>