I have a class method in my User model:
def self.method_name
...
end
In a controller, I need to call this method on a User instance obtained through association:
#user = game_play.player.user
As expected, it threw a no method error because it's a class method.
What is the way to call the method in this case?
EDIT: Adding code for question clarification
#user = #game_play.client.user.
#token = #user.set_login_bypass_token
My model:
def set_login_bypass_token
#We generate a raw token and an encrypted version of the same token
raw, enc = Devise.token_generator.generate(User, :login_bypass_token)
self.login_bypass_token = enc
self.login_bypass_token_set_at = Time.now
self.save(validate: false)
#Raw token is sent to the user via email to provide auto-login
raw
end
The error:
PG::UndefinedColumn: ERROR: column users.login_bypass_token does not exist
Notice the error has it as users.login_bypass_token instead of set_login_bypass_token
EDIT:
My first answer was before you mentioned Devise and assuming you didn't know if you needed a class or instance method. It's clear that your method must be an instance one.
I think you are trying to apply something you found here: https://stackoverflow.com/a/30857087/3372172
This requires that you add a new field to the users database, to manage the :login_bypass_token. Because you will use this column later to perform a find_by. Devise does not add this column to the database.
PREVIOUS ANSWER
If the method needs to access instance variables (which means it acts differently depending the specific object in the User class), it should be an instance method, defined without the self keyword.
If it is a class method, it cannot depend on any attribute from a specific object, and you cannot call it from an instance of the class.
You must decide if it's really a class method or an instance method.
`
If you need a class method to be called from an instance, you can do this (but I don't know why you could need it).
class User
def self.method_name
# blablabla
end
def method_name
User.method_name
end
end
Should be using an instance method instead of a class method. I'm not sure how you would get an error where it's looking for an attribute on the model since those can only be defined in the schema. If you're adding a regular instance method within the model it should work correctly.
Related
I want a HomePage model to be a Singleton class, as i want only one instance of the HomePage model. So this is what I did:
require 'singleton'
class HomePage < ApplicationRecord
include Singleton
has_one_attached :image
end
In HomePagesController, I want the users to be able to edit the unique instance of the HomePage model. So, i did something like this:
class HomePagesController < AdminDashboardsController
def edit
#home_page = HomePage.instance
end
end
Problem:
The default value that HomePage.instance returns nil. I am guessing that the instance is not persisted, as it returns false for the presisted? method call.
I want to be able to create the unique instance for the first time, i.e. override the nil instance that I get from HomePage.instance using seed data, or rails console, and then give the user the ability to edit that instance for as long as they want, using the HomePage Controller as shown in above code.
What i tried:
I tried updating the initial unique instance of the HomePage model, by calling HomePage.instance.update(name: "Hello"). This seemed to create a different instance with id:2, rather than overwriting the previous unique object.
Am I missing out on something? Or am I misunderstanding the overall use of Singleton class itself?
The problem is that singleton is about Ruby object, not about record in the database. And because of multi-threading the processes in Rails are isolated
So if you need to keep just one record don't use require 'singleton'
Define method
def self.get
first || create # more Rails way - first_or_create
end
or
def self.get
first || new # more Rails way - first_or_initialize
end
And in your code call HomePage.get
I'm completely new to ruby / ruby on rails, i'm just giving a sort of extreme assistance on an old existing project, so, forgive myself if this is a silly question ;)
I have this class:
class MyClass
def call
category_id = context.params['category_id']
tmp_context = context.clone
... stuff...
content_id
end
end
Called this way:
MyClass.call(params: params, current_user: current_user)
All i need to get is the return value of the function, the "content_id".
I've tried in several ways and all i can get from this function it's a reference to the context:
#<Interactor::Context params={...}, current_user=#<User ...>>
But no hint about the return value.
Any suggestion?
The call class method is the proper way to invoke an interactor. The
hash argument is converted to the interactor instance's context. The
call instance method is invoked along with any hooks that the
interactor might define. Finally, the context (along with any changes
made to it) is returned.
https://github.com/collectiveidea/interactor
So the proper way to assign a "return value" would be:
class MyClass
include Interactor
def call
category_id = context.params['category_id']
tmp_context = context.clone
# ...
context.content_id = content_id
end
end
MyClass.call.content_id # => some value
So to break this down the Interactor module (provided by the interactor gem). Declares a class method named call which creates an instance of MyClass with the "context" as an argument. It then calls the call instance method on the instance and returns the context.
You can get a better idea of how this works by looking at the source code.
I'm just starting to learn rails. I have a test project and I added a instance method and a class method to a Post model class with some existing sample data.
class Post < ActiveRecord::Base
has_many :comments
attr_accessor :region
def sport
puts "Football"
end
def self.region
puts "West"
end
end
I correctly get "Football" when I run Post.first.sport
But I get nil when I run Post.first.region. Why doesn't rails console return "West"?
Thanks!
Since self.region is a defined as a Class method you should run Post.region to output "West"
See https://stackoverflow.com/a/11655868/1693764 for a good description of Class vs instance methods
It fails because you are using a class method on an instance object.
Do:
Post.region #=> 'west'
When you add 'self.' to a method it becomes a class method. Class method are invoked on the entire class. Instance methods on the other hand are invoked on a instance of the class.
Use class methods when you want methods that are applicable for the entire class. For example a method like find_post_with_most_comments.
Post.find_post_with_most_comments
Use instance method when you are dealing with a particular instance of the class. For example a method like first_comment
#post = Post.find(params[:post_id])
#post.first_comment
attr_accessor adds instance methods to the class.
As per your code, it creates an instance variable called "#region" during initialization of an object of Post, in the getter method.
def region
#region
end
and default value of instance variable is 'nil'. So when you access Post.first.region, it returns the default value of the instance variable.
In 'self.region' code, its defined as class method. So it can be called using the syntax 'Model.class_method'
Thus class methods are called on class objects while instance methods are called on instance objects.
Get a deeper understanding of ruby metaclasses for a complete picture, it will make you more curious about the ruby architecture, and will help you learn more.
I have a
class CommentsController < ApplicationController
def foo
session[:comments] ||= {}
comment = Comment.new(params[:comment])
# Validation and such
session[:comments][comment.post_id] = comment
#several redirections and remote authentications. User returns to another method,
# But for the sake of the example, we continue here.
CommentsController.publish_from_session
end
def self.publish_from_session
session[:comments].each do |comment|
comment.save!
end
end
end
This gives me a can't convert Symbol into Integer error. When diving into this, apparently session is simply not available, or not a hash. It might be that calling CommentsController.some_method is plain wrong.
What would be the correct solution?
Also: As mentioned in the commented code, the real deal is a bit more complex. The user returns either on another controller (sessions controller via oauth) or on yet another method on the CommentsController. See controllers calling eachother and Comments created after Oauth for how I came to this.
session is an instance method. You can't access it in a class method, but you can pass it (or just session[:comments] to the class method.
I have a name attribute on a Person model and every time I access the name attribute, I want name.capitalize to be returned.
Doing the following inside the model won't work,
def name
name.capitalize
end
so what is the alternative?
I suggest you to create a secondary method with your custom formatters.
class Person
def formatted_name
name.capitalize
end
end
This is a better solution compared with overwriting the default implementation because setter and getters might called when updating/writing/saving the record to the database.
I remember once when I overwrote the default implementation of an attribute and each time a record was saved, the attribute was updated with the formatted value.
If you want to follow this way you can use alias_method_chain or take advantage of inheritance including an external module.
class Person
def name_with_formatter
name_without_formatter.capitalize
end
alias_method_chain :name, :formatter
end
Also you can overwrite name and call read_attribute(:name) from within your custom method.
def name
read_attribute(:name).capitalize
end
def name
self[:name].capitalize
end
Again, don't do this. Go ahead and create a custom method.
But happens when name is null?
capitalize will throw an undefined method for nil::Class if self[:name] returns nil.
The following covers that:
def name
self[:name] ? self[:name].capitalize : nil
end
But I agree that you should create the formatted method and leave the name method as is. Never know when you might need the raw data.
FYI: the reason why your method didn't work was because it was causing, what I like to call, a self referring loop. You are redefining the method but calling the method in the new method. So you have to use self[:name] or read_attribute to get to the internal model data.
Try this:
def name
self[:name].capitalize
end