Read my latest article: 8 things I look for in a Ruby on Rails app (posted Thu, 06 Jul 2017 17:59:00 GMT)

rbot is cool

Posted by Sun, 11 Sep 2005 22:17:00 GMT

This weekend, I found myself helping the Portland Ruby Brigade get setup with a new mailing list and while in our IRC channel (#pdx.rb on freenode), I was reminded that we never setup a bot in the channel. So, I went and tried to get Eggdrop running but didn’t like that the configuration file is a whopping 1342 lines long. Seriously… it’s long. I am a busy person right now and this was instantly a turn-off. So, i looked around for ruby based ones… found a few python ones and found rbot

yes… a ruby-based irc bot!

SVN checked it out… added the few deps and the configuration file…


--- 
irc.nick: pdxrbot
core.address_prefix:
  - "?"
irc.user: pdxruby
server.name: irc.freenode.net
auth.password: ******
irc.join_channels:
  - "#pdx.rb"       

Yes! YAML!

It ran right away…and I was sold. Nice work. (and only about 1330 lines less of configuration!)

So, it was up and running and then I looked at the plugins api. (huge grin)

I’ve added two plugins to the #pdx.rb bot already. One for rubyurl (uses shorturl) and one that shows the last 5 entry titles from the planet rubyonrails site. :-)

We’re already scheming how we’ll make it work with meeting reminders, subversion, and other random fun. I’m quite impressed so far.

Check it out here

PL/Ruby loves RubyGems and DRb

Posted by Mon, 22 Aug 2005 21:09:00 GMT

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.

What is a procedural language in Postgresql?

PostgreSQL docs describe them as, ”…allows user-defined functions to be written in other languages besides SQL and C. “

Well, PostgreSQL has PLs for Perl, Python, Java, C, PHP… and even RUBY!


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';

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

Note: Never do this if your system user that runs PostgreSQL has privileges to do anything harmful on your system.

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.

Then it hit me. “Why haven’t you emailed the author?”

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.

First of all, some of you might be thinking, ”Why on Earth would you want to do this?”

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

SELECT redcloth(‘strong text and emphasized text‘);

Why not do this in the application? Well, I do actually have a case where I have an older PHP 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.

Okay, so show me an example of one of these scary PostgreSQL functions.


CREATE FUNCTION redcloth(text) RETURNS text AS '

  require ''rubygems''
  require ''redcloth''

  content = args[0]

  rc = RedCloth.new(content)

  return rc.to_html

' LANGUAGE 'plruby';

”Wait! You said this would be scary!?”

Well, PL/Ruby allows you to write… plain ole Ruby within your functions. (do you see where I am getting here?)

PL/Ruby meets RedCloth


 rb=# SELECT redcloth('*strong text* and _emphasized text_');
                             redcloth
------------------------------------------------------------------
 <p><strong>strong text</strong> and <em>emphasized text</em></p>
(1 row)

PL/Ruby meets ShortURL


CREATE FUNCTION rubyurlize(text) RETURNS text AS '

  require ''rubygems''
  require ''shorturl''

  return ShortURL.shorten(args[0])

' LANGUAGE 'plruby';

...which allows for


 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)

PostgreSQL meets DRb

Okay, this is one of the reasons why I wanted to play with PL/Ruby a bit more. Distributed Ruby Objects… from PostreSQL?

What is DRb?

If you don’t know already… per the description in RDOC, “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.”

It basically allows you to share an object to other machines… at the same time!

mmm…distributed objects…

DRb Object

Here is a simple ruby script that you would run from the shell. It creates a DRb object which accepts connections at localhost:9000.


#!/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

Start me up!

$ ruby mydrb.rb

Now that we have DRb running and listening for connections…we need a client to connect to it.

DRb function in PL/Ruby

Here is a very simple DRb client script and I just drop that into a PostgreSQL function.


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';

The result?


rb=# SELECT drb_test('Potato');
           drb_test
-------------------------------
 You say Potato. I say OTATOP!
(1 row)

Are we having fun yet?

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

Let’s all go get some coffee (or tea) and start playing with PL/Ruby today!

Installing untrusted PL/Ruby for PostgreSQL

Posted by Mon, 22 Aug 2005 13:55:00 GMT

This is going to be short and sweet.

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

Method 1. The standard, safe, PL/Ruby.

Before running this, you need to have all the PostgreSQL headers installed. (se INSTALL in the postgresql directory) make install-all-headers

To install PL/Ruby, you need to download the tarball from here. As you can see, I download it with wget and then install like I would any ruby library. (maybe plruby could become a gem?)

cd /usr/local/src
wget ftp://moulon.inra.fr/pub/ruby/plruby.tar.gz
tar zxvf plruby.tar.gz
cd plruby
ruby extconf.rb
make
make install

Method 2: The untrusted, but super cool PL/Ruby.

Guy Decoux, author of PL/Ruby, was kind enough to share a secret about the PL/Ruby install. (from his email…)

Well plruby normally run with $SAFE = 12, this value if fixed at compile time. 

Now it has an undocumented option, if you compile it with

ruby extconf.rb --with-safe-level=0 ...

 it will run with $SAFE = 0 and you have the equivalent of an untrusted language.

Pretty simple solution, eh?

On my server I was able to run the following:

cd /usr/local/src
wget ftp://moulon.inra.fr/pub/ruby/plruby.tar.gz
tar zxvf plruby.tar.gz
cd plruby
sudo ruby extconf.rb \ --with-pgsql-dir=/usr/local/pgsql-8.0 \ --with-safe-level=0 \ --with-suffix=u
make
make install

Update: the --with-suffix=u was added after someone commented on this. This allows you to install plruby and plrubyu.

Installing PL/Ruby in PostgreSQL Up until now, you haven’t actually installed the language into the database. We’re close though!

All that you need to do is run the following commands to install it to a specific database in your server.

$ psql template1
template1=# CREATE DATABASE plruby;
CREATE DATABASE
template1=# \c plruby
You are now connected to database "plruby".
plruby=#    create function plruby_call_handler() returns language_handler
plruby-#    as '/usr/lib/site_ruby/1.8/i386-linux/plruby.so'
plruby-#    language 'C';
CREATE FUNCTION
plruby=#    create  language 'plruby'
plruby-#    handler plruby_call_handler
plruby-#    lancompiler 'PL/Ruby';
CREATE LANGUAGE
plruby=#

That should be all there is to it!

Where do we go from here?

See my post: PL/Ruby loves RubyGems and DRb

Active Record, I <3 U but I still trust my database server (a tiny bit more)

Posted by Fri, 19 Aug 2005 02:20:00 GMT

While working on a portion of my book, I found myself in ./script/console and was seeing some weird issues when I would use has_many and belongs_to.

Let’s take two simple models.

<pre>
class Order < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_many :orders
end
</pre>
</code>

After a few test records...

<code>
<pre>
test_dev=# SELECT * FROM customers;  
 id |      name      
----+----------------
  1 | Robby
  2 | Nigel
  3 | Linus
(3 rows)

test_dev=# SELECT * FROM orders;
 id | customer_id | amount 
----+-------------+--------
  1 |           1 |  12.00
  2 |           3 |  12.00
(2 rows)
</pre>
</code>

Nothing completely crazy going on, right?

<typo:code lang="ruby">
Loading development environment.
>> Customer.destroy(3)
=> {"name"=>"Linus", "id"=>"3"}
>>     

=# SELECT * FROM orders;
 id | customer_id | amount 
----+-------------+--------
  1 |           1 |  12.00
  3 |           3 |  12.00
(2 rows)

Wait a minute! I just deleted a customer with an id of 3!

So, what is wrong with this scenario? Can you think of any potential problems that could occur from data like this? The record has a customer_id for a customer that does not exist. This is why we have relational databases in the first place, right? :-)

Here is something that I learned today that I was unaware of. Active Record allows you to pass the has_method declaration the option :dependent.

class Customer < ActiveRecord::Base
  has_many :orders, :dependent => true
end

What is this option? Well, according to the AR documentation, “:dependent – if set to true all the associated object are destroyed alongside this object. May not be set if :exclusively_dependent is also set.”

In a nutshell, this works like ON DELETE CASCADE does in PostgreSQL. So, it will through and delete the orders associated with the customer that I was attempting to destroy.

Up until today, I hadn’t broken myself out of the habit of using the built-in constraints/triggers of PostgreSQL. So, as soon as I did, this issue came up and I learned about :dependent.

test_dev# \d orders
                                Table "public.orders" 
   Column    |     Type      |                       Modifiers                        
-------------+---------------+--------------------------------------------------------
 id          | integer       | not null default nextval('public.orders_id_seq'::text)
 customer_id | integer       | 
 amount      | numeric(10,2) | 
Indexes:
    "orders_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "orders_customer_id_fkey" FOREIGN KEY (customer_id) REFERENCES customers(id)

test_dev=# ALTER TABLE orders DROP CONSTRAINT orders_customer_id_fkey;
ALTER TABLE 

RobbyOnRails:~/Programming/footest robbyrussell$ ./script/console 
Loading development environment.
>> cust = Customer.create(:name => 'Jim')
=> #<Customer:0x275373c @new_record_before_save=true, @new_record=false, @attributes={"name"=>"Jim", "id"=>5}, @errors=#<ActiveRecord::Errors:0x274fa88 @base=#<Customer:0x275373c ...>, @errors={}>>
>> cust.orders.create(:amount => '25.00')
=> #<Order:0x274991c @new_record=false, @attributes={"id"=>4, "amount"=>"25.00", "customer_id"=>5}, @errors=#<ActiveRecord::Errors:0x2746dfc @base=#<Order:0x274991c ...>, @errors={}>>
>>                

test_dev=# SELECT * FROM orders;
 id | customer_id | amount 
----+-------------+--------
  1 |           1 |  12.00
  3 |           4 |  29.00
  4 |           5 |  25.00
(3 rows)

As you can see, I put myself into the hands of Active Record when I ran the DROP CONSTRAINT. Then I tried running the code at the top of the post… and it didn’t work.

According to the docs, if you use :dependent => true, it should delete the foreign table records. If not, it should set the value of the foriegn key field to NULL in the foreign table.

Basically, perform these two SQL queries:
UPDATE orders SET customer_id = NULL WHERE customer_id = 17; 

DELETE FROM customers  WHERE id = 17;

Then, the records are still in the database for those orders, but the customer is deleted. There are arguments for and against doing this sort of thing… but the ability to have the option is always nice. In any event, Active Record would not run the first query,it was just deleting from the customers table. Without my constraint, no error would be returned from PostgreSQL and I started to get some bad data.

Imagine showing a list of orders and trying to display the customer name associated with an order that has no linking customer. Doh! If Active Record sets the customer_id to NULL we can at least have some logic to work with this without having to run some fun SQL queries to figure out which orders do and dont have customers. (we want our applications to have clean data!)

Anyhow… Was this a bug? Should Active Record know to update the records to NULL in this case? I figured that is should be handling this task, especially since it was handling cascading deletes when you passed :dependent => true.

However, I didn’t want to prematurely post a bug report, so I began asking around on #rubyonrails (irc.freenode.net). People made a bunch of suggestions as to how to work around it. I could add a before_destroy method in my model, track the bug down, (re)add an ON DELETE trigger to my table (hah), etc. So, I decided that I would see if I could track down what happens when has_many is used for a model upon #destroy.

After a while of digging and making some tests, I posted a patch and a bug report. (please disregard my first patch… it did not work! heh)

Now that I figured this out, I am going to happy add my constraints back to my tables and go back to playing around. This reminded me of a post I had a few months ago when I mentioned that I thought it was best to put some constraints and logic in the database. I also agree that constraints should be put in the abstraction layer, but we cannot always put all faith in our code either. A few levels of checks doesn’t hurt. :-)

This was a fun little riddle that I took on today. The moral of the story? If you have the ability to use the builtin referential integrity features of PostgreSQL and those other databases, it might be a good idea to do so. Things get overlooked, people login to the database in many ways, and from different programs.

UPDATE: DHH responded to this post and provided a link which discusses Application Database versus Integrated Database

It should be noted that there is an important distinction between the two methods. When I said, “Things get overlooked, people login to the database in many ways, and from different programs” I was basically describing Integration Database. However, I was also thinking of the possibility of someone opening up their MySQL or PostgreSQL GUI and manually removing a record in plain SQL. According to Application Database, the moment that you do that, you basically break this model and cannot expect your application to be fully responsible for the problems that may or may not occur. At this point, you would need to look at your application in terms of Integration Database. Please do correct me if I am wrong on this. :-)

However, with this scenario, my first attempt to move to relying on AR had a minor hiccup, but it was an easy enough fix.

By performing the following command, I am moving towards an Integration Database pattern and that should be recognized when taking this into consideration.

ALTER TABLE orders ADD CONSTRAINT orders_customers_id_fkey 
    FOREIGN KEY (customer_id) REFERENCES customers (id) MATCH FULL; 

Okay, back to work!

Once again. Use constraints! (if you can)

... and thanks to DHH for providing the link and motivating me to make a note of this in my entry.

Ruby FPDF on Ruby on Rails

Posted by Sat, 04 Jun 2005 14:24:00 GMT

3 comments Latest by sg Mon, 21 Aug 2006 09:57:06 GMT

I have been tinkering with Ruby FPDF for a client all night. I found the examples for it to lack in some real-world examples, so I have taken the example from the RubyOnRails wiki and added a bit more to it. I have added things like an image, links and made a generic letter template. (just an example). I didn’t get into the header/footer functions yet, may do that later.

If you are not sure how to use Ruby FPDF, check out the FPDF documentation.

Just download Ruby FPDF and unpack the archive in your libs directory.

To get Railst to load fpdf, I added this to environment.rb:

ADDITIONAL_LOAD_PATHS.concat %w(
  app
  app/models
[..snip...]
  lib/fpdf
).map { |dir| "#{RAILS_ROOT}/#{dir}" }.select { |dir| File.directory?(dir) }
At the top of my controller that uses this lib, I added:
require 'fpdf'

When I browse to this controller/method, I got this PDF to generate with this code.

  def pdf
    send_data gen_pdf, :filename => "robbyonrails-fpdf-test.pdf", :type => "application/pdf"
  end

 private
  def gen_pdf

    d = Date.today

    pdf=FPDF.new
    pdf.AddPage
    pdf.SetFont('Arial')
    pdf.SetFontSize(10)

    pdf.Image('/home/matchboy/logo2.jpg',10,8,86,0,'JPG')

    pdf.Cell(0,6, "PLANET ARGON", 0,1,'R')
    pdf.Cell(0,6, "2802 NE 57th Ave",0,1,'R')
    pdf.Cell(0,6, "Portland, OR 97213",0,0,'R')

    pdf.Ln
    pdf.Ln
    pdf.Write(5, "Jane Doe
123 ABC Street
Gilroy, CA 95020

#{d.month}/#{d.mday}/#{d.year}

Dear Jane Doe,

I just wanted to say...

Epsum factorial non deposit quid pro quo hic escorol. Olypian quarrels et gorilla congolium sic ad nauseum. Souvlaki ignitus carborundum e pluribus unum. Defacto lingo est igpay atinlay. Marquee selectus non provisio incongruous feline nolo contendre. Gratuitous octopus niacin, sodium glutimate. Quote meon an estimate et non interruptus stadium. Sic tempus fugit esperanto hiccup estrogen. Glorious

Cheers,

Robby Russell
          ")
    pdf.Ln
    pdf.Cell(0,6, "PLANET ARGON", 0,1,'L',0,'http://www.planetargon.com/')
    pdf.Output
  end

Once again, the output. (click to view PDF)

It’s nothing special, but it’s just an example of how you can add a bit more than ‘Hello World’ to the top of a PDF. I’m still working on figuring out all the x/y stuff. Maybe I will post a better tutorial in the future with headers and footers.

Until then… have fun!

The Zen of Ruby on Rails

Posted by Mon, 11 Apr 2005 01:46:00 GMT

It’s 1:30PM PST, and I am about to devote the next several hours to programming with Ruby on Rails. I haven’t been able to spend much time the last several days as I have been wrapping up a project that was started in PHP. I ended up spending about 7 hours the other day Refactoring a good chunk of the code base, using many of the examples outlined in Fowler’s book (catalog). Derek Sivers suggested this book last winter when I had the privilege of meeting him and discussing programming a bit. The book has been on my desk more than any other book since I purchased it at Powells in December.

Part of me wished that there was a good implementation of Active Record in PHP, so I debated writing it, but came across this from January. The author seems to use Ruby on Rails. I might get around to playing with it soon and see if it’ll make my PHP work a little less gruesome. :-)

In other news, my clients Rails-based application is coming along nicely. grins more

I am probably going to order a few books this week to promote some more best practice approaches to OO. If anyone has some good suggestions, feel free to comment and share the titles.

Cheers

Older posts: 1 ... 9 10 11