Plugin Module Help


#1

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 :slight_smile:


#2

Any help would be appreciated.


#3

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 :slight_smile:

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


#4

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


#5

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.


#6

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 :slight_smile:

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.


#7

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?


#8

Thanks Mark!


#9

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 :slight_smile:

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