ActiveRecord (1.1) question

hello all,

i wrote a collection of Ruby classes to analyse the log entries
generated by a Billion firewall/router. The script, persists the
_attack_ information in an RDBMS through Active Record (version 1.1).

both myself and a colleague, use the same code, on two different
machines + setup. i use it with:

* Fedora Core 2 GNU/Linux distro,
* ruby 1.8.1 (2003-12-25) [i386-linux-gnu]
* PostGreSQL 7.4

he uses it with:

* Debian distro,
* ruby 1.8.2 (2004-11-03) [i386-linux]
* libmysql-ruby1.8 : 2.4.5-6

the classes run fine in my environment, but throw the following
exception in his:

/usr/local/lib/site_ruby/1.8/active_record/base.rb:817:in `method_missing': undefined method `min' for #<Attack:0x4035a2d8> (NoMethodError)
~ from ./analyser.rb:28:in `analyse'
~ from ./analyser.rb:198:in `run'
~ from ./logan.rb:53:in `run'
~ from ./logan.rb:61

the line in question looks like so:

at = Attack.find_by_sql(\
  "SELECT MIN(occured_on) " +\
  "FROM attacks " +\
  "WHERE occured_on BETWEEN '#{start_date_str}' AND '#{end_date_str}'")[0].min

the Attack class is defined as:

class Attacker < ActiveRecord::Base
  has_many :attacks
  has_many :black_listings
end

trying to find out more how the 'at' object (instance of Attack) looks
like when the exception is thrown, reveals an object with an instance
attribute that looks like so:

@attribute {"MIN(occured_on)"=>"some date..."}

any idea why this is happening? and, most importantly if he is missing
some library, which one is it?

TIA + cheers;
rsn

Earlier i wrote:

...
the Attack class is defined as:

class Attacker < ActiveRecord::Base
  has_many :attacks
  has_many :black_listings
end

i meant to write:

class Attack < ActiveRecord::Base
  belongs_to :attacker
end

cheers;
rsn

at = Attack.find_by_sql(\
  "SELECT MIN(occured_on) " +\
  "FROM attacks " +\
  "WHERE occured_on BETWEEN '#{start_date_str}' AND '#{end_date_str}'")[0].min

Try using Attact.connection.select_one("SELECT MIN(occured_on)...")

That should return the value without needing to do [0].min. I'm actually not quite sure why it works on your machine. I would have thought you needed to do "MIN(occured_on) as min" to get the effect you're looking for.

But in any case, you should grab the connection like my example above. It's a slight misuse of AR to load real objects just to get one aggregated value.

···

--
David Heinemeier Hansson,
http://www.basecamphq.com/ -- Web-based Project Management
http://www.rubyonrails.org/ -- Web-application framework for Ruby
http://macromates.com/ -- TextMate: Code and markup editor (OS X)
http://www.loudthinking.com/ -- Broadcasting Brain

Postgresql calls the result fo the min function min by default:

palga=> select min(datont) from main;
    min

···

On Friday 26 November 2004 11:24, David Heinemeier Hansson wrote:

> at = Attack.find_by_sql(\
> "SELECT MIN(occured_on) " +\
> "FROM attacks " +\
> "WHERE occured_on BETWEEN '#{start_date_str}' AND
> '#{end_date_str}'")[0].min

Try using Attact.connection.select_one("SELECT MIN(occured_on)...")

That should return the value without needing to do [0].min. I'm
actually not quite sure why it works on your machine. I would have
thought you needed to do "MIN(occured_on) as min" to get the effect
you're looking for.

------------
1990-01-01
(1 row)

Cheers,

Han Holl

aliasing the aggregate function works with both PostgreSQL and MySQL.

cheers;
rsn

···

On Fri, 26 Nov 2004 09:24 pm, David Heinemeier Hansson wrote:

On Fri, 26 Nov 2004 07:22 pm, Raif S. Naffah wrote:
> at = Attack.find_by_sql(\
> "SELECT MIN(occured_on) " +\
> "FROM attacks " +\
> "WHERE occured_on BETWEEN '#{start_date_str}' AND
> '#{end_date_str}'")[0].min

...I would have
thought you needed to do "MIN(occured_on) as min" to get the effect
you're looking for.

> > at = Attack.find_by_sql(\
> > "SELECT MIN(occured_on) " +\
> > "FROM attacks " +\
> > "WHERE occured_on BETWEEN '#{start_date_str}' AND
> > '#{end_date_str}'")[0].min
>
> Try using Attact.connection.select_one("SELECT MIN(occured_on)...")
>
> That should return the value without needing to do [0].min. I'm
> actually not quite sure why it works on your machine. I would have
> thought you needed to do "MIN(occured_on) as min" to get the effect
> you're looking for.

i'll try that and see if it solves the mysql case.

Postgresql calls the result fo the min function min by default:

palga=> select min(datont) from main;
    min
------------
1990-01-01
(1 row)

indeed; otherwise i would've seen the exception with PostgreSQL, instead
of discovering it with a MySQL setup.

thank you both for your prompt reply. i'll try aliasing the aggregate
function and will get back to you.

also, thank you David for writing such an elegant piece of code, and
making it available, for free.

cheers;
rsn

···

On Sat, 27 Nov 2004 12:35 am, Han Holl wrote:

On Friday 26 November 2004 11:24, David Heinemeier Hansson wrote: