Change view dynamically from controller - Progress bar with threading in Rails - ruby-on-rails

I have a time taking task in my controller for which I want to trigger a simple progress bar/progress status in my view.
In my controller I have something like
threads = []
total.times do |x|
Thread.new {
the_time_taking_task(x)
#Something here to trigger the progressbar in the view
}
end
threads.each { |aThread| aThread.join }
render :action => 'some_action'
I can't put a render in there because it throws a double render error.
I tried putting a render and return there and I got 'return can't jump across threads' error
I am trying render as it seems to be the only way my controller can talk to view. Is my approach wrong ?
Would want something simple like this in the view which applies the progress bar width through that call
<div style="width: <%= (percent_val)%>px; ">
Please advice.

Yes it's a wrong approach.
The Web is stateless, and you can't block a process to render view and get it after by another request.
The good way is :
1) Generate a queue tasking instead of your thread. You can use delayed_job, resque or queue system, like rabbitMQ or beanstalkD
2) Add a controller to check if the process in your queue is end and how is progress. Inside, you put the queue id and check if is end or not and return the progress percent.
The easiest way is do that by Ajax.

Yes, your approach is wrong.
shingara explains this well.
I'm offering a slightly more concrete solution for you.
To get the effect you want, you basically need the following steps:
Offload the thread into a background job server. Checkout resque or delayed_job.
Have the background job write the progress to database or some persistant medium.
In Rails, write an action that returns the current progress (as json or xml)
In your view, write a periodic ajax request that calls the action written in step 3.
update the view accordingly.
You're done! :D

Related

rails controller action contain an algorithm

I have made a rails app. Users can upload images. Once the images are saved into database. An algorithm being called to process those pictures.
For now, it was realized within a controller action like this:
def create
#post = current_user.posts.build(params)
if #post.save
flash[:success]="post created"
redirect_to root_url
image_names = []
#post.picture.each do |imgs|
image_names << imgs.url
end
my_algorithm(image_names)
else
render 'static_pages/home'
end
end
It works correctly. The problem is the page didn't show until the algorithm finishing. And the algorithm took long time. How to fix it. Or maybe call my_algorithm other places? Or delay_job?
I think you should use Active Job for that it will make your job background job
You should try the background jobs to perform that action in particular time.
You may use sidekiq gem to trigger the events in background.
Or You can refer the following URL to, do the active jobs,
http://edgeguides.rubyonrails.org/active_job_basics.html#enqueue-the-job
Refer the above url and configure the app and perform the operations in background.
I have additionally mention the one more link for scheduled jobs,
How to perform a background job now?
First of all, this should be done in a call back, here in the after_create callback of the Post model. And from the callback you can en queue one delayed job or other background job which will fire the algorithm. In front end you can show some messages like under processing in the place where you want to show the processed information.
Thanks

putting controller action into thread

After reading Running Rails jobs in background threads I tried to refactor an controller action that is basically rendering an report in a xml format and sends the report file to the user browser. Now the action runs c. 30 sec, so I thought this a good candidate for a background job. Hovewer, I have run into a problem: it seems I can't access the report erb template from within the new thread.
This is my first attempt with threads and I am not sure if I am able to accomplish my refactoring with the approach.
Simplified code of action before refactoring:
#orders = Order.onrange(range).includes(:logo => [:simulations, :pantones])
respond_to do |format|
format.xml {
orders_xml = render_to_string template: 'orders/bom'
send_data orders_xml, filename: "BOM raport - #{range.to_s}.xml"
}
end
I don't know if you already see this guide: http://guides.rubyonrails.org/active_job_basics.html if not, please take a closer look.
From what you describe you can achieve your goal with:
Make a first request, that will call a controller action to produce the report and use the callback 'after_perform' to update the view with the produced report.
Split the reports from "asking the report" and "view the report". The first will enqueue the report generation and once it's ready will be available for user in a reports section.
I would prefer the second option if you plan the report generation time will increase or if it's unacceptable to have the user waiting 30 secs for a report. Other option could be to generate the report after you have the needed data, so when the user ask for it it's already generated. (just guessing because I don't know your application flow)

How to Make the Controller wait for a Delayed Job while the rest of the App continues on?

(This question is a follow-up to How do I handle long requests for a Rails App so other users are not delayed too much? )
A user submits an answer to my Rails app and it gets checked in the back-end for up to 10 seconds. This would cause delays for all other users, so I'm trying out the delayed_job gem to move the checking to a Worker process. The Worker code returns the results back to the controller. However, the controller doesn't realize it's supposed to wait patiently for the results, so it causes an error.
How do I get the controller to wait for the results and let the rest of the app handle simple requests meanwhile?
In Javascript, one would use callbacks to call the function instead of returning a value. Should I do the same thing in Ruby and call back the controller from the Worker?
Update:
Alternatively, how can I call a controller method from the Worker? Then I could just call the relevant actions when its done.
This is the relevant code:
Controller:
def submit
question = Question.find params[:question]
user_answer = params[:user_answer]
#result, #other_stuff = SubmitWorker.new.check(question, user_answer)
render_ajax
end
submit_worker.rb :
class SubmitWorker
def check
#lots of code...
end
handle_asynchronously :check
end
Using DJ to offload the work is absolutely fine and normal, but making the controller wait for the response rather defeats the point.
You can add some form of callback to the end of your check method so that when the job finishes your user can be notified.
You can find some discussion on performing notifications in this question: push-style notifications simliar to Facebook with Rails and jQuery
Alternatively you can have your browser periodically call a controller action that checks for the results of the job - the results would ideally be an ActiveRecord object. Again you can find discussion on periodic javascript in this question: Rails 3 equivalent for periodically_call_remote
I think what you are trying to do here is little contradicting, because you use delayed_job when do done want to interrupt the control flow (so your users don't want to want until the request completes).
But if you want your controller to want until you get the results, then you don't want to use background processes like delayed_job.
You might want to think of different way of notifying the user, after you have done your checking, while keeping the background process as it is.

How to force to complete response in controller action?

I need to make some slow operations on controller action. But it's not necessary to wait this operation for response rendering.
class ProductController < ActionController
def update
slow_operations()
render json: {status: 'ok'}
end
end
Even I move my code after render in Product#update action, it's not reduces response time.
class ProductController < ActionController
def update
render json: {status: 'ok'}
slow_operations()
end
end
How to force to return complete response before executing of slow operations?
Because of the way Rails works, it's still going to do the actual "rendering" after the action is complete - so, as you found out, moving "render" higher in your action doesn't help. What you need to do is shuffle off the long-running operation into a background process. There's lots of gems to do this, including BackgroundRb, Delayed Job and Sidekiq (my personal favourite, largely because it is multi-threaded, cutting down on the number of processes you need to start, and because of its nice web-based admin/monitoring interface).
There's even a Railscast to get you started for most of these, like this one: http://railscasts.com/episodes/366-sidekiq
You need to integrate something like Resque or Girl Friday to offload the slow process to a background task.

Providing updates during a long Rails controller action

I have an action that takes a long time. I want to be able to provide updates during the process so the user is not confused as to whether he lost the connection or something. Can I do something like this:
class HeavyLiftingController < ApplicationController
def data_mine
render_update :js=>"alert('Just starting!')"
# do some complicated find etc.
render_update :js=>"alert('Found the records!')"
# do some processing ...
render_update :js=>"alert('Done processig')"
# send #results to view
end
end
No, you can only issue ONE render within a controller action. The render does NOTHING until the controller terminates. When data_mine terminates, there will be THREE renders, which will result in an error.
UPDATE:
You'll likely have to set up a JavaScript (jquery) timer in the browser, then periodically send an AJAX request to the server to determine the current status of your long running task.
For example the long running task could write a log as it progresses, and the periodic AJAX request would read that log and create some kind of status display, and return that to the browser for display.
It is impossible to handle the request that way. For each request, you have just one answer.
If your action takes a long time, then maybe it should be performed asynchronously. You could send user e-mails during the process to notify him of the progress.
I suggest that you to take a look on DelayedJob gem:
http://rubygems.org/gems/delayed_job
It will handle most difficult parts of dealing with assync stuff for you (serializing / deserializing your objects, storage, so on...).
Hope it helps you!

Resources