I have a Rails app that is multi-tenant. In the Tenant record, I store codes that are particular to that Tenant.
The following works well (PS - scope doesn't work):
class Worequest < ActiveRecord::Base
acts_as_tenant(:tenant)
def self.closed
where("statuscode_id = ?", ActsAsTenant.current_tenant.request_closed)
end
What I really need is not worequest.statuscode_id but instead worequest.statuscode.position.
I tried this:
def self.closed
self.statuscode.position = ActsAsTenant.current_tenant.request_closed
end
But, that gives this error:
undefined method `statuscode'
Thanks for the help!
Your method is a class method. It means that your code is referring to the class via self object.
def self.closed
self.statuscode.position = ActsAsTenant.current_tenant.request_closed
end
self here is class Worequest and it doesn't have an attribute statuscode.
What are you trying to with self.statuscode.position = ActsAsTenant.current_tenant.request_closed?
Related
I want to create a calculated column on a model, which is not in the database.
class Result < ActiveRecord::Base
attr_accessor :calculated_duration
def calculated_duration
(build_start_time - build_end_time)
end
end
This is what I have, and I am trying to access it like this:
#results = Result.all.order(:build_number)
#results.calculated_duration
I'm getting a no method error:
undefined method `calculated_duration'
Can anyone suggest why?
Because you're calling an instance method on an ActiveRecord::Collection
#result = Result.first
#result.calculated_duration
will work
I'm working on a Rails app, and I've run into a small issue, but I can't seem to find any information pertaining to my certain case.
I'm getting an undefined method error '[]' for NilClass when calling the add_user method in an instance of the class below.
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
#user_info = Hash.new
def add_user(user_id)
new_info = OpenStruct.new
new_info.score1 = 0
new_info.score2 = 0
#user_info[user_id] = new_info
end
...
end
Why is this happening and how can I fix it? In a C++, Java program the equivalent would work fine but I guess Ruby works a bit differently.
When you use #user_info = Hash.new in the body of Group's class definition, the scoping rules of Ruby dictate that #user_info is an instance variable of the Group instance of the class Class. It is accessible only when self is Group, not when self is an instance of Group.
You can give yourself access to the class-level instance variable by defining a singleton method on Group.
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
#user_info = Hash.new
def self.user_info
#user_info
end
def add_user(user_id)
new_info = OpenStruct.new
new_info.score1 = 0
new_info.score2 = 0
self.class.user_info[user_id] = new_info
# OR: Group.user_info[user_id] = new_info
end
end
Since your class is a subclass of ActiveRecord::Base, you probably should not override the constructor. You can do the initialization in the after_initialize hook:
class Group < ActiveRecord::Base
after_initialize do
#user_info = {}
end
# Other stuff here
end
BTW, there's a huge difference between ruby and other languages: The instance variables defined directly in the class belong to the class itself, not its instances. Only those instance variables defined in the instance methods belong to that class's instances.
I have a model base_table, and I have a extended_table which has extra properties to further extend my base_table. (I would have different extended_tables, to add different properties to my base_table, but that's non-related to the question I'm asking here).
The model definition for my base_table is like:
class BaseTable < ActiveRecord::Base
module BaseTableInclude
def self.included(base)
base.belongs_to :base_table, autosave:true, dependent: :destroy
# do something more when this module is included
end
end
end
And the model definition for my extended_table is like:
class TennisQuestionaire < ActiveRecord::Base
include BaseTable::BaseTableInclude
end
Now I what I want is the code below:
params = {base_table: {name:"Songyy",age:19},tennis_ball_num:3}
t = TennisQuestionaire.new(params)
When I created my t, I want the base_table to be instantiated as well.
One fix I can come up with, is to parse the params to create the base_table object, before TennisQuestionaire.new was called upon the params. It's something like having a "before_new" filter here. But I cannot find such kind of filter when I was reading the documentation.
Additionally, I think another way is to override the 'new' method. But this is not so clean.
NOTE: There's one method called accepts_nested_attributes_for, seems to do what I want, but it doesn't work upon a belongs_to relation.
Any suggestions would be appreciated. Thanks :)
After some trails&error, the solution is something like this:
class BaseTable < ActiveRecord::Base
module BaseTableInclude
def initialize(*args,&block)
handle_res = handle_param_args(args) { |params| params[:base_table] = BaseTable.new(params[:base_table]) }
super(*args,&block)
end
private
def handle_param_args(args)
return unless block_given?
if args.length > 0
params = args[0]
if (params.is_a? Hash) and params[:base_table].is_a? Hash
yield params
end
end
end
end
end
EDITED:
I trying to refactore this:
self.before_create do |obj|
obj.position = self.class.count
end
to this:
self.before_create :set_position
private
def set_position
obj.position = self.class.count
end
But, Its displaying Error:
undefined local variable or method `obj'
How fix this?
class YourClass < AR
before_create :set_position
private
def set_position
self.position = ##count
end
end
The set_position method is instance method, so self is assigned to the instance. The count I believe should be a class variable.
class Something < ActiveRecord::Base
before_create :set_position
protected
def set_position
self.position = self.class.count
end
end
Be aware that if you use class variables you might run into trouble if your server uses multiple processes (##count getting out of sync between them). A functional style queries count from the db.
Note: A lot of this behavior (including properly implemented atomic changes to lists) can be found in the acts_as_list gem, that used to be part of Rails a while back. I suggest using that.
message and user. my message belongs_to user and user has_many messages.
in one of my views, i call something like
current_user.home_messages?
and in my user model, i have...
def home_messages?
Message.any_messages_for
end
and lastly in my message model, i have
scope :any_messages_for
def self.any_messages_for
Message.where("to_id = ?", self.id).exists?
end
ive been trying to get the current_users id in my message model. i could pass in current_user as a parameter from my view on top but since im doing
current_user.home_messages?
i thought it would be better if i used self. but how do i go about referring to it correctly?
thank you.
You could use a lambda. In your Message model:
scope :any_messages_for, lambda {|user| where('user_id = ?', user.id)}
This would work like so:
Message.any_messages_for(current_user)
And you could add a method to your user model to return true if any messages are found. In this case you use an instance method and pass in the instance as self:
def home_messages?
return true if Message.any_messages_for(self)
end
But really, I'd just do something like this in the User model without having to write any of the above. This uses a Rails method that is created when declaring :has_many and :belongs_to associations:
def home_messages?
return true if self.messages.any?
end
You can do either of the following
def self.any_messages_for(id) #This is a class method
Message.where("to_id = ?", id).exists?
end
to call above method you have to do
User.any_messages_for(current_user.id) #I am assuming any_messages_for is in `User` Model
OR
def any_messages_for #This is a instance method
Message.where("to_id = ?", self.id).exists?
end
to call above method you have to do
current_user.any_messages_for
This stuff in your Message class doesn't make a lot of sense:
scope :any_messages_for
def self.any_messages_for
Message.where("to_id = ?", self.id).exists?
end
The scope macro defines a class method on its own and there should be another argument to it as well; also, scopes are meant to define, more or less, a canned set of query parameters so your any_messages_for method isn't very scopeish; I think you should get rid of scope :any_messages_for.
In your any_messages_for class method, self will be the class itself so self.id won't be a user ID and so it won't be useful as a placeholder value in your where.
You should have something more like this in Message:
def self.any_messages_for(user)
where('to_id = ?', user.id).exists?
# or exists?(:to_id => user.id)
end
And then in User:
def home_messages?
Message.any_messages_for(self)
end
Once all that's sorted out, you can say current_user.home_messages?.