How to periodically fetch/update value in rails? - ruby-on-rails

Let's take a scenario:
counter 10 seconds
User visited show.html.erb page
show.html.erb fetch the value from database using <%= #post.value %>.
Counter started and each iteration of counter is for 10 seconds.
After every 10 seconds I wanted to change the #post.value using utility class.
update the #post.value in database.
Refresh the show.html.erb automatically and <%= #post.value %> will show the updated value
Above process will be run in loop until user move to other pages.
If I have to simplify the problem in code then it would like this:
View Page
<%= #post.value %>
<%= #post.name %>
Controller Method
def show
#post = Post.find(params[:id])
end
def update
.... #It's empty right now
end
def fetching_value_from_PostUpdate(current_value)
.. # Now I need to update the value in database
end
Utility class
I want to update the post.value on the basis of method written in this class. Pseudo code:
class PostUpdate
##instance_variable
def initialize(post_value)
##instance_variable = Marshal.load(Marshal.dump(post_value))
end
#Method required to calculate the value
def update_data_in_database
...
return data
end
Questions
Where do I have to put the counter? client side or server side? I don't want to use background jobs in rails.
What changes do I need to make in show method so that after every interval page will refresh automatically and pass the updated value in #post.value?
Thanks for your help!

I would go with Firebase as opposed to polling the server.
However, if you're wanting to poll the server periodically, I would just have a JavaScript method which executes every 10 seconds, or at whatever interval you'd like, and fetches whatever data you'd like asynchronously and subsequently updates the DOM.
Also, ruby wrapper for firebase api, in case that may be of interest

I would say the easiest approach doing it would be using ActionController::Live. Using it you'll be able to send SSE(Server Sent Event) to your client while having js script there to catch and update your <%= #post.value %>. Here's a pretty nice manual on using it.
Still from my point of view the most appropriate way to implement things you want to do will be using something like Faye. It is a publish/subscribe messaging system which will allow you to update your client with new data as soon as it appears, e.g., you can set an after_save in your model and publish an update to all subscribed clients. And of course there is a gem also called faye to wrap its usage.

Related

Sharing the variables between the actions in the controller in Rails

I am working on a module where I have to take the consent of the user to save the set of records.
those set of records are created in an action, which has to be made available in another action of the same controller, the records are being saved by the user consent.
now I can send these set of records to UI, from UI to again controller, if the user continues to save, if not cancel.
Problem is there will be thousands of records, which is painful to carry between UI and controller so My plan is to make the set of records available to the action which is being called by the continue button
the code
def create
#valid_members = generate_member_upload_results(params[:member_upload_user][:members_list])
end
in this action #valid_members is going to have the set of records. after this action executes in UI we will ask user whether the records are to be saved if no then cancels if yes then the following action will takes palce
def create_member
count = 0
unless #valid_members.blank?
#valid_members.each do |m|
count = count + 1
m.save(:validate => false)
end
end
redirect_to :back , notice:'#{count} members records created'
end
I want my #valid member should the same object which I used in create def.
I'm not entirely sure this is feasible with the flow you're suggesting. This sounds like something that could be resolved with a multi-step form but you would need to pass the data across or temporarily store it, which is seemingly what you're trying to avoid.
Alternatively, can you create a Rails endpoint that services the first step via javascript directly from the frontend? That can return the data without the user leaving the page, they can then confirm they are happy and submit the page once with approval.

Where do I save a variable that should not be overwritten when I refresh the page in rails

I am pretty new to Rails and Ruby and still wrapping my head around the hole concept of Rails.
What I want to do:
I'm creating a shift-planner with a view of one week and want to create a button that will show the next/last week.
What I did:
I have 3 tables that are relevant. shift, person and test (contains types of shifts)
Where both Test and Person have one to many relations to Shift.
In my controller I did the following:
def index
#todos = Todo.all
#shifts = Shift.all
#people = Person.all
#start_of_week = Date.new(2015,8,7)
#end_of_week = Date.new(2015,8,11)
view:
<% person.shifts.where(:date_of_shift => #start_of_week..#end_of_week).order(:date_of_shift).each do |shift| %>
<td>
<%="#{shift.test.name} #{shift.date_of_shift}"%>
</td>
<%end%>
My Idea was I would make a link where I would increment both Dates and refresh my Page
<a href="/todos/new">
Next Week
<% #start_of_week += 7 %>
<% #end_of_week += 7 %>
</a>
Unfortuately that doesn't work. Cause everytime I call the index function in my controller it sets the date on the default value.
And I'm pretty clueless how to fix this problem in a rails way.
My only would be to somehow pass the dates as parameter to the index function or something like that.
The general structure is:
I scaffolded a Todo view/controller/db just for the sake of having a view / controller and my 3 tables.
Thx for the help.
PS: I'm using the current version of ruby and rails on lubuntu15 (shouldn't be really releveant^^)
The controller method will be invoked every time someone loads the page. If you want to have temporarily persistent storage you should look into cookies (generally sessions are easier to use but in your case it might be relevant to let the client change the data too, click on back/forward buttons and you don't want to have a HTTP request for every little interaction, right?). You can find the relevant documentation here.
Using the variable could look like this:
# in the controller
cookies[:start_of_week] ||= Date.new(2015,8,7)
# in the view
cookies[:start_of_week]
Maybe you want to work with an integer instead of a date object here if you plan on using it on the client side, but then you would have to take that into account in your views.
Edit: On re-reading your question I realized that my answer didn't address several things.
Why is the code you tried not working?
<a href="/todos/new">
Next Week
<% #start_of_week += 7 %>
<% #end_of_week += 7 %>
</a>
When writing your views you have to be aware that it will be evaluated on the server side and then be sent to the client side (web browser in this case). You increment the instance values #start_of_week and #end_of_week when the page is rendered but not later.
So either you have the link go to a special action in your controller that will handle the new value set the cookie/session variable (later one is an option only in this case) and render a new page and send it back to the user or you use Javascript and do this on the client side and change the cookie so no unescessary additonal requests have to be made (but then thinking about it you will most probably need to load data from the server anyway so it might not make any difference which one you take).

Resque status update

In a rails4 app I have a very long task (can take hours) handled by resque (like railscasts #271).
The create action in the controller looks like this:
def create
#longstuff = Longstuff.new(params[:longstuff])
if #longstuff.save
Resque.enqueue(LongStuffHandler, #longstuff.id)
redirect_to #longstuff, :notice => "Success."
else
render 'new'
end
end
I want to update the user on the status of the queue and give him useful information (stats, elapsed time, progressbars etc.)
My idea is to create a div in the "new" view that autorefreshes with javascript every 3 seconds and displays some content that the LongStuffHandler class provides.
So far I've been able to achieve that making the LongStuffHandler class write some html to a file that is loaded by the javascript.
This leads to several problems, for example writing to a file 10 times per second is less than ideal.
I would like to have the LongStuffHandler class to be able to export some variables in real time and making them available to the "new" view so that just reloading a partial will make my page look updated.
An example would be that the LongStuffHandler class has a variable called "#lastlog" and makes it available to the "new" view where there is a div like this autoupdated by javascript:
<div class="lastlog">
<h2>Last Log</h2>
<p><%= #lastlog %></p>
</div>
Is it possible for the background job to export such a variable and for the view to import it?

Multiple GET requests in Rails?

I'm developing an application, and on one page it requires approximately 12-15 GET requests to be made to an API in the background. My original intent was to make the requests using AJAX from jQuery, but it turns out that it is impossible to do so with the Steam Web API I am using.
Doing this in the Rails controller before the page loads is, for obvious reasons, very slow.
After I get the data from the API, I parse it and send it to the JavaScript using gon. The problem is that I don't know how to get and set the data after the page renders.
Here is what my controller would look like:
def index
#title = "My Stats"
if not session.key?(:current_user) then
redirect_to root_path
else
gon.watch.winlossdata = GetMatchHistoryRawData(session[:current_user][:uid32])
end
end
The function GetMatchHistoryRawData is a helper function that is calling the GET requests.
Using the whenever gem --(possibly, see below)....
Set a value in a queue database table before rendering the page. Using a "cron" task (whenever gem) that monitors the queue table you can make requests to the Steam API and populate a queue result table. On the rendered page you could implement a JavaScript periodic check with AJAX to monitor the queue result table and populate the page once the API returns a result.
Additional Info:
I have not used the whenever gem yet but I did some more reading on it and there might be an issue with the interval not being short enough to make it as close to real time as possible. I am currently doing my job processing with a Java application implementing a timer but have wondered about moving to whenever and CRON. So whenever might not work for you but the idea of an asynchronous processor doing the work of contacting the API is the gist of my answer. If the payload from the Steam API is small and returned fast enough then like what was stated above you could use a direct call via AJAX to the controller and then the Steam API.
Regarding the Rails code it should be pretty much standard.
controller:
def index
# Create a Steam API Queue row in the database and save any pertinent information needed for contacting the Steam API
#unique_id = Model.id # some unique id created for the Steam API queue row
end
# AJAX calls START
def get_api_result
# Check for a result using
params[:unique_id]
# render partial for <div>
end
# AJAX calls end
View: index
# Display your page
# Setup an intermittent AJAX call to "controller#get_api_result" with some unique id #{#unique_id} i.e. params[:unique_id] to identify the Steam API Queue table row, populate the result of the call into a <div>
external_processor_code (Whenever Gem, Java implementation, some Job processor, etc...)
Multiple threads should be available to process the Steam API Queue table and retrieve results every few seconds and populate the result table that will be read by the controller when requested via the AJAX call.
To give a complete example of this type of implementation would take some time so I have briefly, from the conceptual level, outlined it above. There might be some other ways to do this that could be more efficient with the way technology is expanding so please do some investigation.
I hope this is helpful!

Rails pub/sub with faye

In a Rails app I've used Faye (Rack adapter) for push notifications (for chat).
I want to use Faye for another use case (more push notifications) but I can't seem to figure it out.
In my app a model can be created from a background job so I want to refresh one of my views (say the index action) when the model gets created.
Like so:
app/models/post.rb
class Post
include Mongoid::Document
after_create :notify_subscribers
private
def notify_subscribers
Faye::Client.publish("/posts")
end
end
app/views/posts/index.html.erb
<%= subscribe_to(posts_url) do %>
uhh what do I do here? ajax call to refresh the whole page??
<% end %>
So is publishing the notification directly from an after_create callback a good idea
and when I get a message from the Faye server how to I go about implementing the "update"?
Do I just do an AJAX call to reload the data from the server? That seems like it would be slow.
Further I want to use something similar for updates to the model (say a user added some comments or the author changed the content) so thrashing the DB all the time doesn't seem like a good plan...
First of all, yes, publishing the notification with after_create is fine. What you should do in your notify_subscribers method is to publish all relevant information about the new post that you want to use in the client so that you don't have to make another unnecessary AJAX request when the notification is received.
So, for instance, if the title and content of the post are relevant for the user to see immediately after it gets created, you would do something like:
def notify_subscribers
client = Faye::Client.new('http://localhost:3000/faye')
client.publish("/posts/new", {
'title' => self.title,
'content' => self.content
})
end
...and then, in the view, you would probably use jQuery to use the data about the new post which you receive from the server. Rails code won't help you here. E.g. you would do this (assuming you're using jQuery and have included faye.js in your header):
<script type="text/javascript">
$(function() {
var client = new Faye.Client('http://localhost:3000/faye');
client.subscribe("/posts/new", function(data) {
/* do whatever you need with data */
});
});
</script>
Finally, if somehow the information you want to transfer is too complex to transfer as part of the notification, or you already have existing AJAX processes for updating your view, you could just publish the post's ID and trigger an AJAX call with jQuery in the subscribe callback function. I'd recommend this only if necessary though.

Resources