Run a method every 30 min if user still logged in - ruby-on-rails

I am trying to figure out a strategy for triggering a method to run every 30 minutes if a user is logged in to my site.
I currently have a method called signed_id? which checks if the user is signed in, and I am currently using the delayed_job gem to handle some other background processes in my application.
I've heard about the whenever and cron gems, but I'm not sure what would be best for my particular need. Is there something that works well with delayed_job ?

Well, you could keep a database record for each user of the timestamp that the method was last triggered (the first time would just be whenever they logged in), and have a cron that runs every minute.
You would keep a server-side record of when the client first logged in, and keep pinging (maybe with ajax) to make sure the user is still logged in. You would make Javascript execute an action (that goes to the server to execute your method) every 30 minutes, of course being calculated based on the server-side record of when you first logged in. This count would reset every 30 minutes as well, continuing the cycle.
I hope this helps.

Yes with cron it is more flexible and easy to use
cron on ruby

Related

Timer on server side in Rails

What am trying to achieve is:
1. User pressess button and starts time-limited form.
2. Timer starts.
3. After X time when timer reaches 0 and user did not finish the form, rails create a record in database and do some session cleaning actions (Model.create and session.delete).
This timer will work like safety measure if user starts the form and then becomes unavailable (closes web brower or shuts PC).
Could you point to me to right direction? Where I should start? What language/framework should I use to do that?
You can use timestamps to record when something happened. Rails already has a great example of this in the updated_at and created_at fields.
You can re-use this concept to store when the user first started filling out the form, perhaps on the resource's new route, which usually coincides with the opening of the form.
As for performing the cleanup, you have two options that would could even both work together, depending on the situation:
If the cleanup isn't security-sensitive, you could have a timer kick off on the front-end and then send an AJAX request to the back-end when it expires.
If the cleanup isn't time-sensitive, you could run a task every few minutes or so, which performs the cleanup on any forms that haven't been cleaned up yet. It would look at the timestamps to determine this.
Aside from those suggestions, we'd have to know more about your use cases to answer more questions.
Is the cleanup time-sensitive?
Is the cleanup security-sensitive?
There are more options - you could run the task more granularly or more securely.
Tight timing
One option if your timing is very tight (down to seconds) is to implement both options above, and in addition the following functionality:
Use ActionCable to keep a web socket open with the user. Periodically have them ping the server to check their status.
In a before_filter, probably in ApplicationController, check first if they have permission to continue the action, i.e. the timestamp hasn't expired yet. If the timestamp has expired, and the cleanup hasn't been performed yet, then perform it now.
Limitations of this approach:
If the user closes the website entirely, the cleanup is not going to happen until the next cleanup task happens.
There are vulnerabilities on the front-end. The user can disable the front-end checks, and then avoid submitting the form, which will delay the cleanup in the same way as #1.

How do I have an action happen 24 hours after a specific date in Rails?

Im on Rails 4, I'm creating a listing/rental site where people can list things and then other people can rent them. I'm using Stripe to handle all my payments, and I have a form set up that gets the users credit card and makes them a customer when they request to book a rental. After that, the owner of the rental can view the request and confirm or deny it. If they confirm it, the user renting gets their card charged and their money goes into holding.
When a user requests a booking, they choose a pick-up and drop-off date. I would like to have an action that calls a payout from stripe to the listings owner 24 hours after the pick up date. I am not sure how to go about this, so any suggestions are great! Of course if anyone knows of any tutorials implementing such a thing that would be awesome :).
Thanks.
Couple of things you can do
delayed_job: requires a database and a running process to run scheduled jobs; You can use it on heroku as shown here
resque-scheduler: requires redis and resque and a running process to run scheduled jobs. You can use it on heroku as shown here. Use resque-web and resque-cleaner to check and handle failed jobs.
whenever: requires access to cron jobs and your own script to be setup to run every hour or every few minutes to then pickup listings that need to be processed and then process them away. You'll need to work out a good error reporting system. Doesn't run on heroku
heroku scheduler: that is all managed through heroku but essentially gives you the same capabilities as whenever.
Resque would probably be my choice, but you should know better about your domain.
Install the whenever gem and documentation is available here: https://github.com/javan/whenever
Then in config/scheduler.rb file mention your function name and defined in the model or as per requirement. It will be behave like CRON job but major difference is that it can run application internal function as well.
There are many ways of doing this.
All of which involve some sort of data store that is checked every X minutes. Within this data store you can usually set a run on time.
Checkout:
https://github.com/collectiveidea/delayed_job
or
https://github.com/mperham/sidekiq

Ruby on Rails times out. How do I fork a process?

I have a page of a long list of items. Each has a check box next to it. There's a jQuery check-all function, but when I submit all of them at once, the request times out because it's doing a bunch of queries and inserting a bunch of records in the MySQL database for each item. If it were to not timeout, it'd probably take about 20 minutes. Instead, I just submit like 30 at a time.
I want to be able to just check all and submit and then just go on doing other work. My coworker (1) said I should just write a rake task. I did that, but I ended up duplicating code, and I prefer the user interface because what if I want to un-check a few? The rake task just submits them all.
Another coworker (2) recommended I use fork. He said that would spawn a new process that would run on the server but allow the server to respond before it's done. Then, since an item disappears after it's been submitted, I could just refresh the page to check if they're done.
I tried this on my local, however, it still seems that Rails is waiting for the process to finish before it responds to the POST request sent by the HTML form. The code I used looks like this:
def bulk_apply
pid = fork do
params[:ids].each do |id|
Item.find(id).apply # takes a LONG time, esp. x 100
end
end
Process.detach(pid) # reap child process automatically; don't leave running
flash[:notice] = "Applying... Please wait... Then, refresh page. Only submit once. PID: #{pid}"
redirect_to :back
end
Coworker 1 said that generally you don't want to fork Rails because fork creates a child process that is basically a copy of the Rails process. He said if you want to do it through the web GUI, use BackgroundJob (Bj) (because we're already using that in our Rails app). So, I'm looking into BackgroundJob now, but what do you recommend?
I've had good success using background job. If you need rails you will be using script/runner which still starts up a new process with rails. The good thing is that Backround Job will make sure that there is never more than one running at a time.
You can also use script runner directly, or even run a rake task in the background like so:
system " RAILS_ENV=#{RAILS_ENV} ruby #{RAILS_ROOT}/script/runner 'CompositeGrid.calculate_values(#{self.id})' & " unless RAILS_ENV == "test"
The ampersand tells it to start a new process. Be careful because you probably don't want a bunch of these running at the same time. I would definitely take advantage of background job if it is already available.
you should check out IronWorker . It would be super easy to do what you want and it doesn't matter how long it takes.
In your action you'd just instantiate a worker which has the code that's doing all your database queries. Example worker:
Item.find(id).apply # takes a LONG time, esp. x 100
And here's how you'd queue up those jobs to run in parallel:
client = IronWorkerNG::Client.new
ids.each do |id|
client.tasks.create("MyWorker", "id"=>id)
end
That's all you'd need to do and IronWorker takes care of the rest.
Try delayed_job gem. This is a database-based background job gem. We used it in an e-commerce website. For example, sending order confirmation email to the user is an ideal candidate for delayed job.
Additionally you can try multi-threading, which is supported by Ruby. This could make things run faster. Forking an entire process tends to be expensive due to memory usage.

Determine if User's online

How can I determine if a user is online or not? Preferably a group of users. I was thinking everytime a user visits a page create a record in the db with Time.now and use AJAX to invoke periodic calls to my remote server; just to see if the time from their last noted activity was about, say, 10 minutes?
You could use Juggernaut to determine if the user if online by "ping" him constantly.

How to go about sending email x hours after a user signs up in Ruby on Rails?

How would I go about sending an email to a user, say, 48 hours after they sign up, in Ruby on Rails? Thanks!
As Joseph Daigle mentioned, you need to obviously record the exact date and time the user registered. After that, you need a cron running every certain number of minutes (every hour, for example) checking to see if there's any new users whose registration time is greater than 48 hours, send a mail to said user and mark that user as already emailed, so you don't email them again.
As per the actual mail sending, check out the following documentation page:
http://wiki.rubyonrails.org/rails/pages/HowToSendEmailsWithActionMailer
It has all you need to know to send mails with RoR.
I recommend that you use the latest version of BackgrounDRb to handle this. You can read about BackgrounDRb here: http://backgroundrb.rubyforge.org/
In order to queue a message for later delivery, the BackgrounDRb client code (in your application model's after_create callback, maybe) could look something like this:
MiddleMan(:email_worker).enq_send_email_task(:message => #message,
:job_key => "notify1",
:scheduled_at => Time.now + 48.hours)
You'd have to build a BackgrounDRb worker to handle sending the email:
# RAILS_ROOT/lib/workers/email_worker.rb
class EmailWorker < BackgrounDRb::MetaWorker
set_worker_name :email_worker
def send_email_task(message)
# ... Code to send the email message
end
end
Note that in order to use BackgrounDRb in this way, you have to use persistent job queues, so make sure you run the migration included with BackgrounDRb to set up the persistence table in your application.
BackgrounDRb is started separately from Rails (mongrel, apache, etc) using 'script/backgroundrb start', so make sure that you add the daemon to whatever process monitoring you're using (god, monit, etc) or that you create an /etc/init.d script for it.
First you're going to need a running daemon or background service which can poll your queue (probably from in a database) every few minutes.
The algorithm is pretty simple. Record the time of the user event in the queue. When the daemon checks that item in the queue, and the time difference is greater than 48 hours, prepare the e-mail to send.
You can queue jobs with a delay using async observer. Ideally, anything you have that isn't known to be instant (or very close to it) all the time should pass through something like that.
I wrote a plugin called acts_as_scheduled that may help you out.
acts_as_scheduled allows you to manage
scheduled events for your models.
A good example of this is scheduling
the update of RSS Feeds in a
background process using Cron or
BackgroundRB.
With acts_as_scheduled your schedule
manager can simply call
"Model.find_next_scheduled()" to grab
the next item from the database.
How I would approach this is by creating a scheduling controller, that will query the database for the next_scheduled and then use a mailer to send the message. The you set up a Cron Job to call the controller periodically using WGET or CURL. The advantage of the Cron/Controller approach is that no further infrastructure or configuration is required on the server and you avoid complicated threading code.
I think I'd be inclined to store the need for the email and the earliest time after which it should be sent, somewhere separate, then have my things-to-do task look at that. That way I only have to process as many records as there are emails to be sent, rather than examine every user every time, which would either get tedious or require an otherwise probably unnecessary index. As a bonus, if I had other tasks to be performed on some sort of a diarised basis, the same construct would be useful with little modification.

Resources