Forum: Ruby on Rails Encoded Columns and ActiveRecord

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
71b39cce77a48a3f99b4e6a14a656007?d=identicon&s=25 Peer Allan (pallan)
on 2007-06-01 22:47
Hi all,

I am trying store IP addresses in a database.  In my old PHP days when I
wrote the SQL i used the INET_ATON and INET_NTOA functions in MySQL to
to the conversions of the address when I inserted or pulled the rows
from the database.  I would like to do something similar with my
ActiveRecord model in Rails as storing the IP as an integer is much more
efficient than in its dot format.

My question is how do I do it?  Is there a way for me to setup my model
so that whenever it saves it converts the stored IP and when it finds a
record it automatically converts it back to the dot format?  Is there a
way I can use the MySQL functions?  Or could I just always keep it in
the integer state, but have accessors in the class convert it when I
access it?

Thanks

Peer
39b36b2be47228f8619d61ea7a607a25?d=identicon&s=25 Matthew Beale (mixonic)
on 2007-06-01 23:10
(Received via mailing list)
On Fri, 2007-06-01 at 22:47 +0200, Peer Allan wrote:
> My question is how do I do it?  Is there a way for me to setup my model
> so that whenever it saves it converts the stored IP and when it finds a
> record it automatically converts it back to the dot format?  Is there a
> way I can use the MySQL functions?  Or could I just always keep it in
> the integer state, but have accessors in the class convert it when I
> access it?
>

Yow, no.  Wait, yes.  You could use a activerecord callback for
before_save to change the IP before writing to the database:

  http://railsmanual.com/module/ActiveRecord::Callbacks

but there is no callback for retrieval. Ahha, but we can just overwrite
the method in the model:

  def ip
    self.ip.gsub /2/, '3'
  end

Would replace 2s with 3s.

But really, why not just store it as a string? Why do you want to store
it as a number? It isn't a number, it's an IP.

Good luck!

--
Matthew Beale :: 607 227 0871
Resume & Portfolio @ http://madhatted.com
4520451c5b8c7cb8d5f9a40db1d47d69?d=identicon&s=25 Peer Allan (Guest)
on 2007-06-02 14:44
Hi Matthew,

Here are some of the reasons why I want to store the IP a a number:

http://arjen-lentz.livejournal.com/44290.html
http://forums.mysql.com/read.php?21,49094,49094#msg-49094

The table that I will be using will be getting large (a similar system
we have right now is growing at about 1 million records a month) and
using 4 bytes instead of 15 will really add up over time.  (Yes, I know
how cheap storage is nowadays, but I would like to avoid the adventure
of a full drives as long as possible, been there done that!)

I was looking to see if there was a way if I could mimic the "serialize"
functionality, but I'm afraid I am going to have to study ActiveRecord a
lot more first.

Peer

Matthew Beale wrote:
> On Fri, 2007-06-01 at 22:47 +0200, Peer Allan wrote:
>> My question is how do I do it?  Is there a way for me to setup my model
>> so that whenever it saves it converts the stored IP and when it finds a
>> record it automatically converts it back to the dot format?  Is there a
>> way I can use the MySQL functions?  Or could I just always keep it in
>> the integer state, but have accessors in the class convert it when I
>> access it?
>>
>
> Yow, no.  Wait, yes.  You could use a activerecord callback for
> before_save to change the IP before writing to the database:
>
>   http://railsmanual.com/module/ActiveRecord::Callbacks
>
> but there is no callback for retrieval. Ahha, but we can just overwrite
> the method in the model:
>
>   def ip
>     self.ip.gsub /2/, '3'
>   end
>
> Would replace 2s with 3s.
>
> But really, why not just store it as a string? Why do you want to store
> it as a number? It isn't a number, it's an IP.
>
> Good luck!
>
> --
> Matthew Beale :: 607 227 0871
> Resume & Portfolio @ http://madhatted.com
6b554084eb8bcb53a1c97864d8c4ca0d?d=identicon&s=25 Husein Choroomi (Guest)
on 2007-06-02 16:28
(Received via mailing list)
require 'ipaddr'

# INET_ATON
IPAddr.new("192.168.0.10").to_i # => 3232235530

# INET_NTOA
IPAddr.new(3232235530, Socket::AF_INET).to_s # => "192.168.0.10"

HTH
- H

On 6/2/07, Peer Allan <rails-mailing-list@andreas-s.net> wrote:
> using 4 bytes instead of 15 will really add up over time.  (Yes, I know
> > On Fri, 2007-06-01 at 22:47 +0200, Peer Allan wrote:
> >
> >
> --
> Posted via http://www.ruby-forum.com/.
>
> >
>


--
Husein Choroomi
Yucca Intelligence Development
http://www.YuccaHQ.com
D8bdf72ae5ca202c0997ac3a04b1bf5f?d=identicon&s=25 Nathan Amick (Guest)
on 2007-06-02 17:06
(Received via mailing list)
Hi Peer,

You can store the IP as an integer in the database and convert to and
from a string using Ruby.  Check out this class:

http://ruby-doc.org/stdlib/libdoc/ipaddr/rdoc/clas...

This seems to work:

class Address < ActiveRecord::Base

  require 'IPAddr'

  def self.find_by_ip(value)
    find :first, :conditions => {:ip => IPAddr.new(value).to_i}
  end

  # Convert from integer - IPAddr doesn't seem to have this method
  def ip
    [read_attribute(:ip)].pack("N").unpack("C*").join "."
  end

  def ip=(value)
    write_attribute(:ip, IPAddr.new(value).to_i)
  end

end


Hope this helps.

-=nathan
4520451c5b8c7cb8d5f9a40db1d47d69?d=identicon&s=25 Peer Allan (Guest)
on 2007-06-02 17:48
Thanks Husein,

I think I will combining all the suggestions I have received on this
thread so that I end up with something like this:

class Address < ActiveRecord::Base

  require 'IPAddr'
  before_save :convert_ip

  def self.find_by_ip(value)
    find :first, :conditions => {:ip => IPAddr.new(value).to_i}
  end

  def ip
    convert_ip
    IPAddr.new(read_attribute(:ip), Socket::AF_INET).to_s
  end

  def ip=(value)
    write_attribute(:ip, IPAddr.new(value).to_i)
  end

  def convert_ip
    if read_attribute(:ip) =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/
      write_attribute(:ip, IPAddr.new(read_attribute(:ip)).to_i)
    end
  end

end

I just whipped this up so I obviously have not tested it in any way.

Peer


Husein Choroomi wrote:
> require 'ipaddr'
>
> # INET_ATON
> IPAddr.new("192.168.0.10").to_i # => 3232235530
>
> # INET_NTOA
> IPAddr.new(3232235530, Socket::AF_INET).to_s # => "192.168.0.10"
>
> HTH
> - H
>
> On 6/2/07, Peer Allan <rails-mailing-list@andreas-s.net> wrote:
>> using 4 bytes instead of 15 will really add up over time.  (Yes, I know
>> > On Fri, 2007-06-01 at 22:47 +0200, Peer Allan wrote:
>> >
>> >
>> --
>> Posted via http://www.ruby-forum.com/.
>>
>> >
>>
>
>
> --
> Husein Choroomi
> Yucca Intelligence Development
> http://www.YuccaHQ.com
This topic is locked and can not be replied to.