eggmatters
eggmatters

Reputation: 1140

Library class methods aren't loaded by rails

I have a library that defines a base class that all other classes derive from. Rails, on initialization, should provide all of the classes from this library. Rails, however, doesn't recognize class methods inherited from my library class. Here is an example:

Model: app/models/mailing_address.rb:

require 'account_model'
class MailingAddress < AccountModel
  # class accessors, initializer and method
end

Library class: lib/account_model.rb

###################################
# AccountModel
# 
# This is a super class to be inherited by 
# the domain classes of this application.
# Responsible for api calls and 
# error handling.
##
class AccountModel
  class << self
    def get_all id = nil
     # class method implementation
    end
    ##
    # get id
    # fetch method, parses url provided by child class in path hash
    # and performs appropriate fetch
    # method returns class instance set by fetch
    def get id
      # class method implementation
    end

    def update_existing options
      # class method implementation
    end

    def create_new options
      #class method implementation
    end

    def delete_existing id
      # class method implementation
    end   
end

These methods wrap api calls using the HTTParty gem. On load, the methods get and get_all are recognized. However, create_new, and update_existing are not:

app/controllers/mailing_address_controller.rb:

def update
    @mailing_address = MailingAddress.update_existing params[:mailing_address]
   . . .
end

Throws the following error:

Processing by MailingAddressesController#update as JSON
. . .
Completed 500 Internal Server Error in 133ms

NoMethodError (undefined method `update_existing' for MailingAddress:Class):
  app/controllers/mailing_addresses_controller.rb:17:in `update

In Passenger, I need to reload tmp/restart.txt, in WEBRick, I need to restart the server.

I do not see this behavior in the IRB.

Here is what I've tried so far with no success:

I have never seen this behavior in a rails app before, I have another library class that works just fine.

I'm running: - ruby-2.1.1 - rails 4.03 - passenger 5.07

UPDATE:

While attempting to investigate this further, I uncovered yet another issue:

I added a class method to MailingAddress:

class MailingAddress < AccountModel
. . .
def self.debug_methods
  return self.methods
end

Which also throws a "MethodNotFound" exception. Since this does work in the rails console, but not in WEBRick or Passenger, I'm tempted that there is some server caching going on.

UPDATE

After shutting everything down and restarting, Now the situation is reversed:

-WEBRick processes the request successfully -Passenger processess the request successfull -Rails console throws an error:

Webrick and passenger:

Processing by MailingAddressesController#update as JSON
. . .
Completed 200 OK

Console:

MailingAddress.update_existing params
NoMethodError: undefined method `update_existing' for MailingAddress:Class

I'm guessing it's first come first serve as to whomever gets the loaded class.

config.autoload_paths is set correctly:

config.autoload_paths += %W(#{config.root}/lib)

LAST UPDATE The only workaround that seems to work is clobbering my tmp/ directory and restarting everything (Passenger need to have touch tmp/restart.txt ran).

This, however, is a crappy workaround. I say bug!

Upvotes: 2

Views: 1154

Answers (2)

eggmatters
eggmatters

Reputation: 1140

The server (Passenger, WEBRick) doesn't reliably load library classes as expected. This could be due to the fact that the library class has static methods as well as the derived classes. Those may not be accurately namespaced.

This behavior occurs when a change is made to the application (controller, model, etc.) Even though the library code may not change, the namespacing gets messed up. The console loads application state each time it is invoked where Servers possibly cache application state.

Clearing the tmp directory, and restarting passenger is a valid workaround. Any changes to the code would have to be treated as library changes.

Upvotes: 0

NM Pennypacker
NM Pennypacker

Reputation: 6952

You have to call that method on an instance of the class:

MailingAddress.new.update_existing params[:mailing_address]

or

@mailing_address.update_existing params[:mailing_address]

If you don't need update_existing to be an instance method you can move it outside of the self block.

Also, you might consider just putting your account_model.rb file in app/models. That way you won't have to require it at the top of mailing_address.rb

Upvotes: 0

Related Questions