I'm trying to call an instance method (defined in model) from the instance variable in controller but could not get that work and server logs saying
undefined method `active_users' for #< Admin
controller
#admin = Admin.first
#admin.active_users
Admin Model
def self.active_users
byebug
end
I know that we can call it through the class directly like Admin.active_users .
Why is it not accessible by instance of the class?
active_users method is defined for Admin object, not for its instances.
I do not know what you are trying to do, but message receiver matters.
To make #admin.active_users work, define a method an instance method:
def active_users
byebug
end
The thing is, that active_users (both with and without self) are instance methods. It is just that objects for which these methods defined are different.
The method with self is a "class instance method", while the one without self is an "instance method", e.g. method accessible by instances of the class Admin.
When you write def self.something_method, method will be available only for class, as in your example. If you want create method for object, you have to create method without self, like this: def something_method.
For example:
class Admin
def self.class_method
p 'class method'
end
def something_method
p 'method for object'
end
end
#admin = Admin.create(something_params)
# You can also use 'Admin.new', but it will not save your admin in database, or 'Admin.first', like in your example.
#admin.something_method
#=> 'method for object'
#admin.class_method
#=> not working :(
Admin.something_method
#=> not working :(
Admin.class_method
#=> 'class method'
Related
My Rails have gotten rather rusty and I'm driving myself crazy trying to figure out something that is probably so basic I can't see the forest for the trees.
I have a form that goes to the create action of my controller. When I save I need to also update some items from another table. I want to wrap it all in a transaction. I thought the preference was to put the transaction in the model. When I do that and try to call the create action it errors out telling me that there is no method for that class.
equip_swaps_controller.rb
def create
respond_to do |format|
#equip_swap = EquipSwap.new(equip_swap_params)
if #equip_swap.trans_equip_and_save
format.html { redirect_to #equip_swap, notice: 'Equipment transfer was successfully created.' }
else
format.html { render action: 'failure' }
end
end
end
Model equip_swap.rb
def self.trans_equip_and_save
EquipSwap.transaction do
Tool.transfer_equipment(self.to_vehicle, self.items_moved)
self.save
end
end
Tool model with needed method
def transfer_equipment(location,ids)
ids.each do |id|
Tool.find(id).update(location: location)
end
end
I thought that calling the class method would allow it to execute that method on my instance of the EquipSwap instance #equip_swap. When I try to submit the form and create the new record it tells me that there is no trans_equip_and_save method for Class..... There is something obvious that I'm missing. Help!
Method start with self its call class method and and without self its call instance method, let me give example
Class method
def self.class_method
# do some stuff
end
Instance method
def instance_method
# do some stuff
en
Call class method using
ModelName.class_method
Call instance method using
#instance_variable.instance_method
In your code change your method to instance method
def trans_equip_and_save
EquipSwap.transaction do
Tool.transfer_equipment(self.to_vehicle, self.items_moved)
self.save
end
end
And now call this method using instance variable #equip_swap.trans_equip_and_save
Edit:
If you are calling transfer_equipment using model name then add self before method name, I mean make it class method like following
def self.transfer_equipment(location,ids)
ids.each do |id|
Tool.find(id).update(location: location)
end
end
You have two things to notice here,
First of all there are two different methods, class methods and instance methods.
Class methods: Class methods are defined directly on a class and they are defined using def self.method.
Usage: Class.method
Instance methods: Instance methods are defined on an object of a class and they are defined without self.
Usage: object = Class.new(), def method ==> object.method
So, in your case, there will be two changes,
1) you called, #equip_swap.trans_equip_and_save.
Since, #equip_swap is an object, according to second point you shold have an Since, #equip_swap is an object, according to second point you shold have an instance method without self..
def trans_equip_and_save
EquipSwap.transaction do
Tool.transfer_equipment(self.to_vehicle, self.items_moved)
self.save
end
end
2) Tool.transfer_equipment, this is called with class name. So, according to first point it should be called with self.
def self.transfer_equipment(location,ids)
ids.each do |id|
Tool.find(id).update(location: location)
end
end
I have studied major difference between Ruby class ,instance method and the major difference I found is we don't need to create instance of that class we can directly call that method on class name directly.
class Notifier
def reminder_to_unconfirmed_user(user)
headers['X-SMTPAPI'] = '{"category": "confirmation_reminder"}'
#user = user
mail(:to => #user["email"], :subject => "confirmation instructions reminder")
end
end
So,here I defined instance method reminder_to_unconfirmed_user in my Notifier class to send email to unconfirmed users, and when I run Notifier.reminder_to_unconfirmed_user(User.last) it get called provided it's a instance method not a class method.
To define a class method, use the self keyword in the method's definition (or the class' name):
class Notifier
def self.this_is_a_class_method
end
def Notifier.this_a_class_method_too
end
def this_is_an_instance_method
end
end
In your case, reminder_to_unconfirmed_user should be defined as a class method:
class Notifier
def self.reminder_to_unconfirmed_user(user)
# ...
end
end
Then you can use it like this:
Notifier.reminder_to_unconfirmed_user(User.last)
I had the same question the OP did and after digging around I finally figured it out! The other answers just addressed when to use instance vs class methods in Ruby however Rails does some sneaky stuff behind the scences. The question wasn't when to use class vs instance methods but instead how come Rails allows you to call an instance method as if it's a class method as shown by his mailer example above. It's due to: AbstractController::Base and can be seen here: AbstractController::Base
Basically, in all controllers (whether they be your mailer or a standard controller), all defined methods are intercepted by "method_missing" and then returns an instance of that class! The defined methods are then also converted to public instance methods. Thus, because you never instantiate these classes (for example you never do Mailer.new.some_method) Rails automagically calls method_missing and returns an instance of that Mailer which then takes advantage of all the methods defined within that class.
In your case it must be :
class Notifier
def self.reminder_to_unconfirmed_user(user)
headers['X-SMTPAPI'] = '{"category": "confirmation_reminder"}'
#user = user
mail(:to => #user["email"], :subject => "confirmation instructions reminder")
end
end
As their name suggests:
Instance methods on a model should be used for logic/operations that relate to a specific instance of a model (the one on which the method is called.)
Class methods are for things which don't operate on an individual instance of a model or for cases where you don't have the instance available to you. Like in some cases you do want to apply changes on few group of objects.
If you want to update all users on a specific condition, Then you should go for class method.
They do have different way of calling :
class Test
def self.hi
puts 'class method'
end
def hello
puts 'instance method'
end
end
Foo.hi # => "class method"
Foo.hello # => NoMethodError: undefined method ‘hello’ for Test:Class
Foo.new.hello # => instance method
Foo.new.hi # => NoMethodError: undefined method ‘hi’ for #<Test:0x1e871>
I have a user model in my application. Now I want to replace some user model coding into 2 categories likely employ.rb and customer.rb under a module users, to avoid more number of codes in a single model. I want to access a method send_mail in customer.rb after a user created.
user.rb
after_create:send_msg_on_order
def send_msg_on_order
Users::Customer.send_mail
end
users/customer.rb
def send_mail
Mailer.send_mail_to_customer.deliver
end
And I am getting undefined method `send_mail' for Users::Customer:Module error.
You have defined send_mail method as instance method but calling it as a class method. Either make it a class method or create an instance of Customer model and call it.
Making the method a class method:
def self.send_mail
Mailer.send_mail_to_customer.deliver
end
If you wish to keep it an instance method, then call it like this:
after_create:send_msg_on_order
def send_msg_on_order
Users::Customer.new.send_mail
end
HTH
You can also call like this
def send_msg_on_order
Customer.send_mail
end
I am very new to RoR and I have played around the source code. But I have a problem that I already built a 'def A' for creating first CSV file, and 'def B' for creating second CSV file. Each 'def' has its own button, but I have the third button to create all CSVs (to produce output from first and second CSV files.)
What is the possible way to do it?
def first_csv
...
end
def second_csv
..
end
def all_csv
<< how to call get first and second csv >>
end
Thanks in advance,
It should be as simple as you imagine:
def all_csv
first_csv
second_csv
end
Muntasim's answer is correct, but I have to add some additional information.
Ruby provides two types of methods..class methods and instance methods.
class MyClass < AvtiveRecord::Base
# class method
def self.foo
# do something
# within this scope the keyword "self" belongs to the class
end
# another class method which calls internally the first one
def self.bar
something = foo # self.foo could also be written
# do something with something
# within this scope the keyword "self" belongs to the class
end
# instance method
def foo
# do something
# if you use the keyword "self" within an instance method, it belongs to the instance
end
# another instance method which calls class and the first instance method
def bar
mystuff = Myclass.bar # if you want to call the class method you cannot use "self", you must directly call the class
mystuff2 = foo # self.foo is the same, because of the instance scope
return [mystuff, mystuff2]
end
end
You can call the last instance method like following
instance = MyClass.first
instance.bar
My rails model has code that is attempting to define_method(method_name) inside the model.
I keep getting:
NoMethodError: undefined method `define_method'
What am I doing wrong? Am I doing this in the wrong place. I need this method attached to this model. Where else can I define this method?
EDIT:
For those asking to see the code:
for field in rdev_fields
next if self.attributes.include?(field)
count = count + 1
rdev_hash[field.to_sym] = self.attributes["attribute#{count}"]
if !self.respond_to?(field) then
define_method("#{field}") do
self.send("attribute#{count}".to_sym)
end
end
end
There's nothing magical or about a rails model, it's just a normal class with a bunch of pre-existing methods,
So, the question is "can I define_method in a class"?
Part 1: Yes you can.
The important distinction is than you can define method in a class not in an instance method
For example:
class Cow
define_method "speak" do
"MOOOO"
end
end
Cow.new.speak
=> "MOOOO"
This should work fine. Note you're defining it on the class Cow, so any other Cows that you already have will automatically get that method added.
Part 2: What do you do if you want to define a method from within an instance method?
You can't define methods from an instance method, so you have to grab the class, and use that to define the method. Like this:
class Cow
def add_speak
self.class.send(:define_method, :speak) do
"MOOOO added"
end
end
end
Cow.new.speak
NoMethodError: undefined method 'speak' for #<Cow:0xb7c48530>
Cow.new.add_speak
Cow.new.speak
=> "MOOOO added"
Problem solved. Astute readers will note that in this example I'm using send(:define_method) - this is needed because define_method is private, and private methods are only accessible to the thing they're in. In this case, define_method is in the class, we are in the instance, so we can't directly access it.
As above though, we're adding the method directly to the class, so all other Cows which already exist will automatically also get the speak method added.
Part 3: What do you do if you want to define a method for only 1 object, not all objects of that class?
Example:
class Cow
def add_speak_just_me
class << self
define_method "speak" do
"MOOOO added for just me"
end
end
end
end
Cow.new.speak
NoMethodError: undefined method 'speak' for #<Cow:0xb7c72b78>
c = Cow.new
c.add_speak_just_me
c.speak
=> "MOOOO added for just me" # it works, hooray
Cow.new.speak # this new cow doesn't have the method, it hasn't been automatically added
NoMethodError: undefined method `speak' for #<Cow:0xb7c65b1c>
How does this work? Down the rabbithole you go!
Read this: http://dannytatom.me/metaid/ and good luck. It helps when you realise that 'adding a method' to an instance isn't actually adding it to the instance at all :-)
If you came here searching for how to dynamically define a CLASS method, because define_method wasn't working (because it defines INSTANCE methods), here is your answer:
Use define_singleton_method :)
was able to cobble this together. Very little understanding of what's actually going on though.
My instance method foo is opening the class and defining bar on it so that I can then call that on my instance. More experienced folks will let us know if this is opening a can of worms at the same time.
Would be useful to know your specific use for this though.
class User < ActiveRecord::Base
def foo
(class << self; self; end).class_eval do
define_method(:bar) {puts "bar"}
end
end
end
u = User.first
u.foo
u.bar #=> "bar"
The answer to your question is "yes, you can". As for why it's not working for you - it's impossible to say for sure why, if you don't provide some context for the code.