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
Related
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.
I've created an instance variable on an ActiveRecord Model where I want to save a bit of computationally heavy data in each instance... Here's my code to do that:
class Account < ActiveRecord::Base
after_initialize :init
attr_accessor :market_value
def init
self.market_value ||= my_lengthy_function
end
end
where I'll take the hit to get that instance data (market_value) run when I init an instance of the model.
This works - I can see how I don't have to re-calculate my market_value property.
My problem is, when I access that object through another context, rails doesn't leverage that data as I'd expect.
For example:
Say I create an instance of an account (a = Account.find_by_id(2)). That market_value will be calculated on that object once.
If I have a nested has_many relationship to something called "holdings" (not in my sample code) on that account object, I'm going to want each of those holding objects (a holding) to be able to use it's parent account object.
However, in my code, I access the account from it's nested holding objects (my_holding.account.market_value) - I re-instantiate an instance of that account object, and incur that costly computation, even though it's already been computed.
How can I better leverage that account market_value property so that it doesn't keep recalculating?
I would not put the calculation logic in the ActiveRecord model. Maybe something along these lines:
class MarketValueCalculator
def initialize()
#market_values = {}
end
def calculate_for_account(account)
#market_values[account.id] ||= heavy_lifting
end
def heavy_lifting
###
end
end
#calculator = MarketValueCalculator.new
#market_value = #calculator.calculate_for_account(account)
#market_value = #calculator.calculate_for_account(my_holding.account)
i would build up a simple cache on class-level with the model ids as keys:
class Account < ActiveRecord::Base
def market_value
##market_value ||= {}
##market_value[id] ||= my_lengthy_function
end
end
not tested if this would work though, especially with class reloading in development.
For various reasons I won't go into here, we need to implement a table-per-customer setup for a couple of our models. For most customers, the default table will be fine. But for some we need to point the model to a different table. The table name is stored in the customer record as custom_table_name. Here's how I tried doing it in Rails (2.3.10):
g rails
In the console, assume customer with id 10 has custom_table_name set to 'big_co_foos'
Foo.customer = Customer.find(10)
Foo.table_name
#outputs "Foo.table_name called"
#then "big_co_foos"
Looks good so far, but try calling Foo.table_name again
Foo.table_name
#outputs "foos"
The table name isn't "sticking." From what I can tell, the call to super in self.table_name is the culprit. If I don't call super in that method, the local function is called every time. But I obviously don't won't to re-implement table_name here. I suspect I must be missing something around how class methods inherit vs. instance methods. Any suggestions?
Also, I tried using a class variable instead (##customer), but that didn't make a difference.
Finally after looking at the Rails source code I tried calling reset_table_name instead of super but it seemed to have the same result as calling super (that is, subsequent calls to Foo.table_name are bypassed).
Update: This appears to be working
class Foo < ActiveRecord::Base
#customer = nil
def self.customer=(value)
#customer = value
if #customer && #customer.table_name
self.set_table_name(#customer.table_name)
else
self.reset_table_name
end
end
end
I am trying to access an instance variable which is set in the controller in the model. The controller is the products controller and the model is the products model. The instance variable is a instance of another model called account.
The instance variable is #current_account
When I run the code nothing happens, I do not get an error. Does anyone know where I can find something read about access instance variables set in the controller from the model?
Thanks
Eef
You shouldn't generally try to access the controller from the model for high-minded issues I won't go into.
I solved a similar problem like so:
class Account < ActiveRecord::Base
cattr_accessor :current
end
class ApplicationController < ActionController::Base
before_filter :set_current_account
def set_current_account
# set #current_account from session data here
Account.current = #current_account
end
end
Then just access the current account with Account.current
DISCLAIMER: The following code breaks MVC conventions, that said...
Using class attributes can probably lead to thread safety issues. I would use Thread.current + around_filter to store controller related data at thread level, and ensure it gets cleared
just before the request finishes:
class ApplicationController < ActionController::Base
around_filter :wrap_with_hack
def wrap_with_hack
# We could do this (greener solution):
# http://coderrr.wordpress.com/2008/04/10/lets-stop-polluting-the-threadcurrent-hash/
# ... but for simplicity sake:
Thread.current[:controller] = self
begin
yield
ensure
# Prevent cross request access if thread is reused later
Thread.current[:controller] = nil
end
end
end
Now the current controller instance will be avaliable globaly during the request processing through Thread.current[:controller]
If you need to access a controller variable from a model it generally means your design is wrong because a controller serves as bridge between view and model (at least in Rails), controller gets info from models, models shouldn't know anything about controllers, but if you want to do it anyway you can do it just as jeem said, but I'd rather do:
class << self
attr_accessor :current
end
instead of cattr_accessor :current
you can see why here => cattr_accessor doesn't work as it should
I can't comment directly so I'll post here: the accepted answer does not seem to be right. As #vise notes, class variables are shared across requests. So unless there's just one current account for the entire app, this won't behave as expected.
For more, see the accepted answer by #molf here: Is Rails shared-nothing or can separate requests access the same runtime variables?
I'm not sure if I understand the question exactly, but I'll take a stab.
I think if you need to access a controller instance variable from the model then you either need to make it an attribute in the model, or move your logic to the other class controller, not model.
I have a rails model class
class Model < ActiveRecord::Base
has_many :object_collection
def add_object(object)
object_collection.push object // works
#object_collection.push object // does not work
self.object_collection.push object // works
end
end
I was wondering if someone can please explain to me why the # does not work yet self does i thought these two meant the same
cheers
They are not the same. Consider the following Ruby code:
class Person
attr_accessor :employer
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"
The method attr_accessor conveniently generates an attribute reader and writer for you (employer= and employer). You can use these methods to read and write an attribute, which is stored in the instance variable #employer.
Now, we can rewrite the above to the following, which is functionally identical to the code above:
class Person
def employer=(new_employer)
#works_for = new_employer
end
def employer
#works_for
end
end
john = Person.new
john.employer = "ACME"
john.employer # equals "ACME"
Now, the instance variable #employer is no longer used. We chose to write the accessors manually, and have the freedom to pick a different name for the instance variable. In this particular example, the name of the instance variable is different than the name of the attribute accessors. There is nothing that prevents you from doing that.
This is similar to how ActiveRecord stores its attributes internally. They are not stored in instance variables of the same name, that is why your push call to #object_collection does not work.
As you may understand, attribute readers and writers offer a certain abstraction that can hide the implementation details from you. Reading and writing instance variables directly in subclasses is therefore generally considered bad practice.
#foo identifies an instance variable called #foo. foo identified a method called foo.
By default, instance variables in Ruby are private. It means you cannot access the value of an instance variable unless you have some public method that exposes the value.
Those methods are called setters and getters. By convenction, setter and getter have the same name of the instance variable, but this is not a requirement.
class MyClass
def initialize
#foo
end
def foo=(value)
#foo = foo
end
def foo
#foo
end
def an_other_foo=(value)
#foo = foo
end
def an_other_foo
#foo
end
end
Though methods and instance variables can have similar names, thery are different elements.
If this topic is not clear to you, you probably need to stop playing with Rails and go back studying how Ruby works.
In your specific case, object_collection doesn't exist as an instance variable because it's an association method.
They do not mean the same thing. One is an instance variable, the other is a method.
The #foo means "the value of the instance variable foo", where as self.foo means "the value of a call to the method foo on myself".
It is typical for a method foo= to set the #foo instance variable, so I can see how someone new to the language might be confused. I'd encourage you to pick up a book on the ruby language. There's one specifically for people who have done some rails but never learned ruby proper. You often can hack rails without understanding the language or what these statements mean, but you'll be far less productive than someone who spends the small amount of time it takes to learn the ruby language itself.
As a general rule, use the self.foo form whenever you can, as this is less sensitive to changes in the classes definition.