Plugin Module Help

Nate Leavitt wrote:

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:

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

Nate Leavitt wrote:

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

Yes.

Nate Leavitt 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

Thanks Mark!