I have a model for one of my database tables. I want to override the column name for that particular table. How would I achieve it.
For example, let my table be called DUMMY and it has one column called col_a
col_a
20
34
42
23
12
I would be doing a #dummy.col_a. Now this method should return me 0 for numbers ending with 0 and for everything else, it should return the original value. I could do that by defining a new method, but I want to override the column name itself. Please help.
You can override the col_a method. Use the read_attribute method to read the value in database. Something like this:
def col_a
if self.read_attribute(:col_a).to_s.end_with?('0')
0
else
self.read_attribute(:col_a)
end
end
You can simply define a method of the same name as the column. To get the actual column value, use self[column_name]. So something like this should work:
class Dummy < ActiveModel::Base
def col_a
self[:col_a] % 10 == 0 ? 0 : self[:col_a]
end
end
(This assumes col_a is an integer.)
I'm a little late to the party here, but a really elegant way to do it is to simply use super
class Dummy < ApplicationRecord
def col_a
super % 10 === 0 ? 0 : super
end
end
You can achieve that by overwriting default accessors as described in the documentation. All column values are automatically available through basic accessors on the Active Record object, but sometimes you want to specialize this behavior. This can be done by overwriting the default accessors (using the same name as the attribute) and calling read_attribute(attr_name) and write_attribute(attr_name, value) to actually change things.
Scroll to the Overwriting default accessors section for more info.
Related
I have a simple model with some integer values (I think, I set them to integer in the migration file at least) and I'm just trying to increment them with a member function of the model but when I try to add to them I'm getting the error "Undefined method `+' for nil:NilClass)
Any tips here??
def take()
#total -= 1
User.find(#poster_id).lifetime -= 1
end
def give()
#total += 1
....
nothing more to it really, it's just simple not working. do I need to cast these somehow? I made sure to initialize the values to 0 upon each instantiation of the model class
Firstly, if total is a column in database (and you say it is), then within the instance method of the class you should access it as total, not #total. Secondly, if you want to update the total attribute you should, well, update it :)
def take # notice no () - they are optional
update(total: self.total - 1)
end
def give
update(total: self.total + 1)
end
Analogically with poster_id (if, again, it is a column in db) you would do:
user = User.find(poster_id) # notice not #poster_id
user.update(lifetime: user.lifetime - 1)
The title is a bit confusing, so let me explain.
I have 3 Model classes called Table1, Table2, and Table3. All three tables have the "total" column.
This is what I want to be able to do:
index = either 0, 1, or 2
tableNames = ["Table1", "Table2", "Table3"]
tableNames[index].total
^ Obviously I can't do that because tableNames[index] returns a string, not a reference to the actual class itself.
This is what I'm currently doing:
index = either 0, 1, or 2
if index == 0 then
Table1.total
elsif index == 1 then
Table2.total
elsif index == 2 then
Table3.total
end
I guess what I want to do is a bit analogous to the "send" method in ruby, where you can use variables as method names.
Is there a way to do this, or do I have to do the if elsif check? This makes the code longer and clunkier and I'm wondering if there's a better way. Thanks!
If you are getting the model name as a string. You can do this
model_name = "Table1" #or "Table2", "Table3"
model = model_name.constantize
model.total
You can directly turn any string into a class with constantize method.
Note - If you are going to use rails further, ideally refer to them as Models not tables.
I have a relation in my rails database and I get one instance of my relation. I then need to determine how much of that instance is filled in. I do this by counting the nils in the relation but I don't know how to do this through code. I know about the .each loop but that makes me state the fields and i need something more like an array. This is what i have so far
#survey_data = Surveyprofile.find_by(:user_id => #user.user_id)
#counter = 0
#index = 0
#survey_data.each do |d|
//i need something like
if d[index].nil? == false
#counter = #counter +1
end
#index++
end
does anyone know how to express this??
(this is all done in the controller by the way)
I haven't tested this approach, but it should work.
survey_profile.rb:
class SurveyProfile < ActiveRecord::Base
def self.number_of_empty_answers(user_id)
user_survey = SurveyProfile.find_by_user_id(user_id)
count = 0
user_survey.attributes.each do |attr_name, attr_value|
count += 1 if attr_value.nil?
end
return count
end
survey_profiles_controller.rb:
SurveyProfile.number_of_empty_answers(#user.id)
The class method number_of_empty_answers counts the answers the user has not filled. Then you call that method from your controller.
Note that it doesn't have to be a class method. It can also be an instance method. You can find the instance first in the controller and then call the method on it.
Since you're iterating through the attributes hash, you can get more fancy and build an array of field names that have not been empty. But if you just want the count, the above should work.
I have a form in which users input a number for the attribute :bytesize, which has an integer datatype. The number represents the amount of bytes for my object #catcher.
I'd like to have a method that will convert the value of :bytesize to megabytes. That is, I'd like to be able to run #catcher.mbsize, and that will display the number of megabytes for that object.
I'm pretty new to Rails, so my apologies if this seems obvious.
Conversion methods are pretty straight-forward:
class Catcher
def mbsize
self.bytesize / (1 << 20)
end
end
Remember that attributes are internally stored as instance variables, so attr_accessor :bytesize is stored in #bytesize.
You need to add mbsize column to your db.
In controller:
def create
#other code
def mbsize
#bytesize / (1 << 20)
end
#catcher.mbsize=mbsize
#cather.save
end
EDIT:
If you don't need related DB record, you can simply define this method in Catcher model:
def mbsize
bytesize=self.bytesize
mbsize=#your method of converting
end
By some reason your bytesize is sting. You can convert it into integer by .to_i method
For single table inheritance, how do you force Rails to use an integer column for the 'type' column instead of string?
You can override the methods Rails uses to convert the table name to class name and vice versa:
The relevant methods are find_sti_class which is responsible for the translating the value stored in the type column to the respective ActiveRecord model and sti_name which is responsible for retriving the value stored in type column given an ActiveRecord subclass.
You can override them like this:
class Institution::Base < ActiveRecord::Base
ALLOWED_CLASSES = %w[Institution::NonProfit Institution::Commercial]
class << self
def find_sti_class type_name
idx = type_name.to_i
super if idx == 0
ALLOWED_CLASSES[idx-1].constantize
rescue NameError, TypeError
super
end
def sti_name
idx = ALLOWED_CLASSES.index(self.name)
if idx.nil?
super
else
idx + 1
end
end
end
end
I have written a post elaborating this in more detail.
You would have to find the part of ActiveRecord responsible for handling the "type" column and monkey patch it, i.e. override how it worked from within your application.