I have the following model:
class ActivityLog < ActiveRecord::Base
validates :user_id, :instance_id, :action, presence: true
validates :user_id, :instance_id, :action, numericality: true
def log
ActivityLog.create(
user_id: current_user ? current_user.id : -1,
instance_id: instance_id,
action: actions.index(action)
)
end
private
def actions
['start','stop','create','destroy']
end
end
When I call the following line from the rails console, I get an error:
ActivityLog.log(user_id: 1, instance_id:1, action: 'create')
# Error returned from console
NoMethodError: undefined method `log' for #<Class:0x007fb4755a26a8>
Why doesn't my method call work? I defined it in the class, but it says it is undefined. What am I missing or misunderstanding? Thank you.
log
Say you have a class User
, and within the class, you define the method has_cell_phone
. (The contents of that method does not matter.) When you define a method in a class as def has_cell_phone
, that method can be called on any User
object. While the class User
is itself a class object, you would call it on an object whose immediate class is User
. In correct terms, you would be writing an instance method for an instance of the User
class.
You are getting that error because the method log
you defined works only for an _instance of the ActivityLog
class. If you do the following, you can call log
correctly, given your current code:
activity_log = ActivityLog.create # with required params
activity_log.log
Secondly, you are calling log
with parameters, while your method definition does not require any. (That would look like def log(params)
.)
Now, here is where you modify your existing code. When you want to call a method on the entire class (meaning the class itself), you prepend the keyword self
to the class-method definition. For example, for the User
class, it would be def self.create_user_with_cell_phone
. You can also add arguments to that method. The arguments you provide in your "method call" line, I would add those to your class method, like so:
def self.log(instance_id, action)
# ...
end
ActivityLog.log(1, 'create')
You would not need to include the user_id
, because, based on your logic, it checks if the current_user
object is true
, and follows from there.
A second look at your question, I found that you are defining a method actions
. Remember what I said about instance methods? Since it appears that actions
will always remain constant, I recommend that you make it one! To do so, it's recommended you place the following line in your class, before any method definitions.
ACTIONS = ['start','stop','create','destroy']
Then, any time you want to call ACTIONS
while inside the ActivityLog
class, you do the following: ACTIONS.index(action)
. If you want to call this constant outside of its class, you would do this: ActivityLog::ACTION
. It is similar syntax to a class-method call, instead you use ::
to separate the class from the constant. Re-examining your code, it should now look like this:
class ActivityLog < ActiveRecord::Base
ACTIONS = ['start','stop','create','destroy']
validates :user_id, :instance_id, :action, presence: true
validates :user_id, :instance_id, :action, numericality: true
def self.log(instance_id, action)
ActivityLog.create(
user_id: (current_user ? current_user.id : -1),
instance_id: instance_id,
action: ACTIONS.index(action)
)
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With