How to call after_update callback for a particular column updation? - ruby

I have one model called points and in that i have 2 columns named clicked_at and opened_at. Those two columns will not be entered while creating record. Both the columns will be updated manually. Now i want to call a callback only for updating clicked_at column. Is there any way to do this? Help will be appreciated.

You have to check it manually.
def my_callback
if clicked_at_changed?
clicked_at_did_change
end
end
def clicked_at_did_change
# do stuff
end

for this ..observers will be a good option and there are many options when you want to call your code..while updating/creating/deleting/editing/saving ...it goes on.
class PointObserver < ActiveRecord::Observer
##this method will be called everytime AFTER you update point object
def after_update(point)
point.clicked_at =Time.now
point.opened_at =Time.now
point.save!
end
end
enable this observer in application.rb
# Activate observers that should always be running.
config.active_record.observers = :point_observer
You can do whatever you want and also you have many callbacks such as before_save,before_update..etc.
Moreover you can place all your observers in app/observers/..

Related

Have a token/unique url in order to destroy resource

I would like to add following functionality to one of my models:
Once it's created, a token of some sort will be created and this token allows one to destroy the object e.g. http://localhost:3000/items/7AEaC6Nhq946.
Is there a gem or similiar that offers this functionality already?
You could make a 'Tokenable' concern and include it in the models you want to:
In app/models/concerns/tokenable.rb
module Tokenable
extend ActiveSupport::Concern
included do
before_create :generate_token
end
protected
def generate_token
self.random_token = loop do
random_token = SecureRandom.urlsafe_base64(nil, false)
break random_token unless self.class.exists?(random_token: random_token)
end
end
end
In your model:
include Tokenable
Be sure to add the random_token column in the database for the model where you include the concern.
Now in your controller you would do something like Item.find_by(random_token: params[:random_token]) and perform the actions you wish to do with the object.

Using shove to add a string to an array in ruby

As part of an online Ruby tutorial, I must create a text-based game. One requirement is that I use require to pull in another file. I've done that as well as include the module that holds a method. However, I cannot produce the result I want. Here's my file with the module:
module Inventory
def Inventory.inventory(item)
items = Array.new
if item == "show"
items.inspect
else
items << item
end
end
end
I want the parameter (item) to be added to the array items as a string that can be inspected when I pass the "show" argument to it.
So for example, I want to add a 'bat' to the inventory so I call Inventory.inventory("bat"). Later I'd like to add other things. But when I call Inventory.inventory("show") it doesn't show anything.
I've spent days looking through many other tutorials and hundreds of questions here but still can't get it work. I'm probably not understanding something really fundamental so please be gracious to me as I'm still learning.
Is it the way I'm adding to an array? The way I'm trying to get it to show? Or do I not understand how to use methods and arguments?
you can Dylan's answer if you want to go with instance approach or you can use class variables.
The problem with your code is that you initialize items local variable every time you call inventory.
Here is a version that will persist items in a class variable:
module Inventory
def Inventory.inventory(item)
##items ||= Array.new
if item == "show"
##items.inspect
else
##items << item
end
end
end
Inventory.inventory 1
Inventory.inventory 2
p Inventory.inventory 'show'
this is producing
"[1, 2]"
This would make a lot more sense as a class. This way, you can store the items in an instance variable that will persist during multiple calls to add, show, etc. You can of course put this class into a separate file and still include it.
class Inventory
def initialize
#items = []
end
def add(item)
#items << item
end
def show
#items.inspect
end
end
# To use the class:
inventory = Inventory.new
inventory.add('bat')
inventory.show
# => ["bat"]
The issue is that your items array is being recreated every time this method is called, so there is no persistence between method calls for what is passed into the array. Dylan Markow's answer shows how you can use instance variables to persist values between method calls.

Can't display more than one table model inheriting from the same class on different tables in QtRuby

I've been following this article to display ActiveRecord data in QtRuby. I've copied the BoatTableModel class from there(used my own code for the rest). In the article, BoatTableModel is defined to only support the Boat model, but except for the column definitions the code is quite generic. So, I've changed it so instead of having the columns defined there, I've made it take the columns from a column_names method, and define that methods in subclasses for each model.
Here is my code:
class QtArModel<Qt::AbstractTableModel
def initialize(items)
super()
#items=items
end
def rowCount(parent=nil)
#items.size
end
def columnCount(parent=nil)
column_names.length
end
def data(index,role=Qt::DisplayRole)
invalid=Qt::Variant.new
return invalid unless role==Qt::DisplayRole or role==Qt::EditRole
item=#items[index.row]
return invalid if item.nil?
v=item[column_names[index.column]]||""
return Qt::Variant.new(v)
end
def headerData(section,orientation,role=Qt::DisplayRole)
invalid=Qt::Variant.new
return invalid unless role==Qt::DisplayRole
v=case orientation
when Qt::Horizontal
column_names[section]
else
""
end
return Qt::Variant.new(v.to_s)
end
def flags(index)
return Qt::ItemIsEditable|super(index)
end
def setData(index,variant,role=Qt::EditRole)
if index.valid? and role==Qt::EditRole
s=variant.toString
item=#items[index.row]
if index.column.between?(0,column_names.length-1)
item[column_names[index.column]]=s
else
raise "invalid column #{index.column}"
end
item.save
emit dataChanged(index,index)
else
return false
end
end
end
class QtCoursesTableModel<QtArModel
def column_names
return [
:number,
:name,
:tutor_name,
:site,
:active,
]
end
end
class QtTasksTableModel<QtArModel
def column_names
return [
:course,
:ex_number,
:received,
:due,
:description,
:link,
:completed,
:file,
]
end
end
Now, when I display one model(doesn't matter which) - everything works just fine. However, when I display both models, each in it's own Qt::TableView - only the first one is displayed, and the other table view is blank.
I've tried different ordering, and the table that gets to display it's data is always the one which it's Qt::TableView is created first - the order of the creating the Qt models does not matter. Also, when I create the model object for the first table, but don't actually set it's model property to it, the second table displays it's data.
I've also tried to display the same model twice in two different table views - and it worked - for a split second, and then the second view's data disappeared.
I've also tried to copy-paste the QtArModel, change it's name, and make one of the models inherit from the copy. That did work - but it's obviously a huge code duplication, so I would really like to avoid that.
Now, my guess is that something in QtArModel is defined as a class member instead of instance member, making both model instances share something they shouldn't share. It has to be in QtArModel - because if it was higher in the inheritance tree, the problem would have remained when I've duplicated QtArModel. However, I can't find anything in my QtArModel that's class-scoped instead of instance-scoped.
What am I missing?
OK, I've managed to work this out. Apparently, the problem was not the inheritance, but the GC. Since the only connection to the models was from TableView's model property - which is just a wrapper for C++ getter and setter - ruby thought it lost the reference to my models, and GC'd them.
Solved by keeping the models in ruby variables.

Overload each method with order

I have an erb template which generates the config file for an httpd.
It's important that a particular location is written last (it is a catch all)
At the moment the code looks like
cluster.apps.each do |app|
# Render config
end
I'd like to overload the each method on the apps object to guarantee order. What's the best place to start looking for how to do this?
If you want to overload it, you can do something like
class Cluster
#..code
def each_application
return unless block_given? #ensure a block was given
a = #apps.shift #Implement this to grab the element you want
#apps.each{|x| yield x}
yield a #yield the element that you want last
end
end
So you can now do:
cluster.each_application do |app|
#Render config
end
And with the current implementation above, it will yield all the elements(except the first one) in a row. The last yielded item is the first one that was shifted off.

Rails 3, confused about 'before_create :some_method' ... WHEN does some_method do its thing?

we have model helper (used by several different models) called set_guids that sets self.theguid to a random string. Been using it for a long time, we know it works.
in a new model 'Dish' we created, we have
before_create :set_guids (NOTE: no other before/after/validation, just this)
def do_meat_dish
( this is invoked by #somemeat.do_meat_dish in the Dish contoller )
( it manipulated the #somemeat object using self.this and self.that, works fine)
( THEN sometimes it creates a new object of SAME MODEL type )
( which is handled differently)
#veggie = Dish.new
#veggie.do_veggie_dish
end
def do_veggie_dish
recipe_str = "add the XXXX to water"
recipe_str.gsub!("XXXX", self.theguid) *** the PROBLEM: self.theguid is nil
end
as soon as we execute veggie = Dish.new shouldn't veggie.theguid be initialized?
Note we have not saved the new object yet... but the before_create should still have done its thing, right?
it is something to do with create a new instance of a model inside a method for the same model?
is it something with using # for the variables?
Additional note: if we comment out the line trying to access self.theguid everything else works fine ... it's ONLY the value (supposedly) set by the before_create set_guids that is nil instead of being a guid.
before_create is called only before the object is saved to the database the first time. That's why you get nil.
I suggest that you use after_initialize callback instead. Be careful though, since after_initialize will be called whenever the document is new or loaded from the db, that way you will have new guids every time you get the document, which is not what you want. So I suggest you do something like:
def set_guids
return unless theguid.nil?
.....
end
As another solution, if you don't want to change the after_create callback above, you can do something like:
def theguid
super || set_guids
end
That should let you go also.

Resources