Forum: Ruby on Rails DRY (don't repeat yourself) way / Hook to Activerecord object (to store IP address on every object

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.
A55c9166450a21261741dc7750ec3d7e?d=identicon&s=25 Chris (Guest)
on 2009-06-08 23:45
(Received via mailing list)
As part of my project, I need to store IP address for every object
that was created.

  User
  Topic
  Payment
  and more...

Now all these records have the following attribute in their table
called "ip_address_on_create".

One way to do this is to put this code everywhere in the controllers:

   user.ip_address_on_create =  request.remote_ip   ,.... and so
forth.


Now to conform to DRY (don't repeat yourself), I see the mechanism:
after_create   (model)  and after_filter ( controller).  I am not
really sure how to tie this thing together. Would appreciate any tips
or Ruby-Fu .


Thanks in advance!
Chris
885ac43bca92fc2b1034356f3283e788?d=identicon&s=25 pharrington (Guest)
on 2009-06-09 01:46
(Received via mailing list)
I haven't tested this at all, and to be quit honest it feels a bit
hackish, but the following comes to mind:

before_filter lambda { params.merge! :ip_address_on_create =>
request.remote_ip }

Add the appropriate attribute to attr_accessible and you'll be good to
go.
885ac43bca92fc2b1034356f3283e788?d=identicon&s=25 pharrington (Guest)
on 2009-06-09 01:47
(Received via mailing list)
or rather you're more likely going to be doing params[:user].merge!...
but you get the idea
5f94b9b346c2aa648a80bc259978e5bc?d=identicon&s=25 Colin Law (Guest)
on 2009-06-09 09:37
(Received via mailing list)
2009/6/8 Chris <chrisnow7@gmail.com>:
> called "ip_address_on_create".
> or Ruby-Fu .
>

Might the DRYest way to do this be to use a polymorphic association
for the IP address?
Colin
29ebf90af6107d2eb39b587c7972639c?d=identicon&s=25 Mukund (Guest)
on 2009-06-09 12:42
(Received via mailing list)
Have a look at active_record::observers and wrap the save method with
an update to the ip address.  Declare a class level variable in
application.rb with a before_filter that populates the IP into the
variable (and subsequently used by the observer).
B09a3f6cdc4797532647d2d264b5df49?d=identicon&s=25 Jodi Showers (jshow)
on 2009-06-10 16:49
(Received via mailing list)
Makund - it is quite likely that your solution will break when
multithreading arrives (as will will loads of other stuff)
5f94b9b346c2aa648a80bc259978e5bc?d=identicon&s=25 Colin Law (Guest)
on 2009-06-10 23:07
(Received via mailing list)
009/6/10 Jodi Showers <jodi@nnovation.ca>:
>
> Makund - it is quite likely that your solution will break when
> multithreading arrives (as will will loads of other stuff)

Could you explain why please, for the uninitiated?
Thanks
Colin
885ac43bca92fc2b1034356f3283e788?d=identicon&s=25 pharrington (Guest)
on 2009-06-10 23:45
(Received via mailing list)
Basically... the class variable is shared by all instances of the
controller in question spawned by the Rack server process.
Multithreaded Rails handles multiple requests simultaneously, and thus
multiple simultaneous instances of the controller. Two requests come
along, one from 1.1.1.1, then one from 2.2.2.2 immediately after.
before_filter from 1.1.1.1's controller instance sets class variable
accordingly, then before_filter from 2.2.2.2's instance overwrites the
variable. Controller instance from 1.1.1.1 now uses 2.2.2.2's IP, and
shinanagins ensue.
A8be277b450f30e5ea8d5278b758d48b?d=identicon&s=25 hoenth (Guest)
on 2009-06-11 04:46
(Received via mailing list)
>
> > > Makund - it is quite likely that your solution will break when
> > > multithreading arrives (as will will loads of other stuff)
>

You could try using Thread.current in your model when storing your
class variable.

For example, in your User model, you could have the following methods:

def self.ip_address=(ip_address)
    Thread.current[:ip_address] = ip_address
end

def self.ip_address
    Thread.current[:ip_address]
end

You would set these as part of your authentication process

User.ip_address = request.remote_ip

Then you might be able to use this in a :before_save filter in your
models

before_save :set_ip_address

def set_ip_address
  self.ip_address = User.ip_address
end
059ed46172a087063ce26250e44c8627?d=identicon&s=25 Fernando Perez (fernando)
on 2009-06-11 08:22
> For example, in your User model, you could have the following methods:
>
> def self.ip_address=(ip_address)
>     Thread.current[:ip_address] = ip_address
> end
>
> def self.ip_address
>     Thread.current[:ip_address]
> end

God please tell me he did not suggest to use threads in order to add an
argument.
29ebf90af6107d2eb39b587c7972639c?d=identicon&s=25 Mukund (Guest)
on 2009-06-11 12:31
(Received via mailing list)
Replace the class variable with a method call instead?
A8be277b450f30e5ea8d5278b758d48b?d=identicon&s=25 hoenth (Guest)
on 2009-06-11 15:20
(Received via mailing list)
> God please tell me he did not suggest to use threads in order to add an
> argument.
> --
> Posted viahttp://www.ruby-forum.com/.

Here is a brief discussion on Thread.current used in class variables.

http://coderrr.wordpress.com/2008/04/10/lets-stop-...

As there is clearly debate about the robustness of using this method,
I offer it only as a suggestion. It has worked for me.
This topic is locked and can not be replied to.