Forum: Ruby on Rails Plugin Module Help

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.
Nate L. (Guest)
on 2009-01-22 00:46
First off, I'm an intermediate Rails developer who is still learning the
inner workings of Rails.. so here is my situation/problem:

I have written a Plugin that accesses a third party API.  Within the
Plugin I had multiple classes that inherit from one another.  Example:

**************************
Old Code (Plugin Classes)
**************************

class ApiConn
 attr_accessor :api_url, :api_key

 def initialize(url, key)
   @api_url = url
   @api_key = key
 end

 def api_perform(class_type, method, *args)
   begin
     server = XMLRPC::Client.new3({'host' => self.api_url, 'path' =>
"/api/xmlrpc", 'port' => 443, 'use_ssl' => true})
     result = server.call("#{class_type}.#{method}", self.api_key,
*args)
   rescue XMLRPC::FaultException => e
     puts "*** Api Error: #{e.faultCode} - #{e.faultString} ***"
   end

   return result
 end
end

class ContactService < ApiConn
  def api_contact_add(data)
    api_perform('ContactService', 'add', data)
  end
end

....

**************************
Then within my ApplicationController I have this:
**************************

Account
def current_account
  @current_account ||=
Rails.cache.fetch("ch:current:account:#{current_subdomain}"){
    Account.find_by_subdomain(current_subdomain)
  }
end

def app_contact_svc
  @app_contact_svc ||=
ContactService.new("#{current_account.api_url}.application.com",
current_account.api_key)
end


**************************


Well, this has worked fine.  However, as I've been going through my
controllers, I've noticed that a lot of these calls should be moved to
the models.  For example, I have an ActiveRecord model named Contact and
I would like to be able to call api_contact_add from within the instance
of that model.  I know I would be able to do that by switching the
plugin to a module.

However, here is the problem:  The rails app uses accounts and each
account has a different api_url and api_key.  The current_account(see
above) is set by the subdomain being used.

So I guess my question is how I can have module attributes set
specifically for each account through multiple models?

So instead of doing Contact.new(api_url, key) in an initialize statement
for each class that includes the ApiConn module, I would like these
values set once for all models specific to the account.

I hope this make sense :)
Nate L. (Guest)
on 2009-01-22 19:02
Any help would be appreciated.
Mark Reginald J. (Guest)
on 2009-01-26 16:54
(Received via mailing list)
Nate L. wrote:
> class ApiConn
> "/api/xmlrpc", 'port' => 443, 'use_ssl' => true})
> class ContactService < ApiConn
>
> ContactService.new("#{current_account.api_url}.application.com",
> I would like to be able to call api_contact_add from within the instance
> So instead of doing Contact.new(api_url, key) in an initialize statement
> for each class that includes the ApiConn module, I would like these
> values set once for all models specific to the account.
>
> I hope this make sense :)

Perhaps something like:

module ContactService
   def self.set_account(url, key)
     Thread.current[:api_conn] = ApiConn.new(url, key)
   end

   def api_contact_add(data)
     Thread.current[:api_conn].api_perform('ContactService', 'add',
data)
   end
end

before_filter do
   ContactService.set_account(
   "#{current_account.api_url}.application.com",
current_account.api_key)
end


--
Rails Wheels - Find Plugins, List & Sell Plugins -
http://railswheels.com
Nate L. (Guest)
on 2009-01-26 22:58
Mark Reginald J. wrote:
>
> Perhaps something like:
>
> module ContactService
>    def self.set_account(url, key)
>      Thread.current[:api_conn] = ApiConn.new(url, key)
>    end
>
>    def api_contact_add(data)
>      Thread.current[:api_conn].api_perform('ContactService', 'add',
> data)
>    end
> end
>
> before_filter do
>    ContactService.set_account(
>    "#{current_account.api_url}.application.com",
> current_account.api_key)
> end
>
>
> --
> Rails Wheels - Find Plugins, List & Sell Plugins -
> http://railswheels.com

Hey Mark,

Thanks for the response. I think I'm following what you're suggesting.
Is that multi-thread safe for the newer versions of rails?
Mark Reginald J. (Guest)
on 2009-01-28 04:32
(Received via mailing list)
Nate L. wrote:

> Is that multi-thread safe for the newer versions of rails?

Yes.

--
Rails Wheels - Find Plugins, List & Sell Plugins -
http://railswheels.com
Nate L. (Guest)
on 2009-01-28 21:59
Mark Reginald J. wrote:
> Nate L. wrote:
>
>> Is that multi-thread safe for the newer versions of rails?
>
> Yes.
>
> --
> Rails Wheels - Find Plugins, List & Sell Plugins -
> http://railswheels.com

Ok thanks.  I guess my next question is more in regards to threads, but
if I have multiple accounts logged in how do the threads stay specific
for each account?

Looks like I need to read up on threads.
Nate L. (Guest)
on 2009-01-28 22:33
Mark Reginald J. wrote:

> Yes.


Is the thread specific for the request in rails? Meaning.. the
before_filter will be run on each rails action/request therefore is a
new thread created in rails for that process?  Jeez.. I hope I'm
explaining it properly :)

Also, since that before filter is creating a new AppConn obj do I have
to worry about performance?  It just seems that creating a new AppConn
for each rails request seems like overkill.  Am I wrong in thinking
that?

Thanks again for your responses.
Mark Reginald J. (Guest)
on 2009-01-29 05:35
(Received via mailing list)
Nate L. wrote:

> Is the thread specific for the request in rails? Meaning.. the
> before_filter will be run on each rails action/request therefore is a
> new thread created in rails for that process?  Jeez.. I hope I'm
> explaining it properly :)

Yes, each thread only carries one request at time.

> Also, since that before filter is creating a new AppConn obj do I have
> to worry about performance?  It just seems that creating a new AppConn
> for each rails request seems like overkill.  Am I wrong in thinking
> that?

Creating such a small AppConn object with each request shouldn't
be a problem.

If it is you can instead do

    def self.set_account(url, key)
      if ac = Thread.current[:api_conn]
        ac.url, ac.key = url, key
      else
        Thread.current[:api_conn] = ApiConn.new(url, key)
      end
    end

--
Rails Wheels - Find Plugins, List & Sell Plugins -
http://railswheels.com
Nate L. (Guest)
on 2009-01-29 17:48
(Received via mailing list)
Thanks Mark!
This topic is locked and can not be replied to.