Accessing Rails Session Variable from Backbone View - javascript

This might be a very simple question, but I could not find the answer for it. How should I access a rails session variable from javascript/jQuery (from a Backbone view)? Most likely this is not the best practice, but this is not a problem for me.
Thank you,
Alexandra

You can use the gon gem. It allows to send data automatically with each request and let it be accessible from javascript.
For example, after including the gem in your Gemfile, you can add a before filter to your ApplicationController:
class ApplicationController < ActionController::Base
before_filter :set_gon
...
protected
def set_gon
gon.my_session_variable = session[:my_session_variable]
end
end
In your application layout, section head of your html:
<%= include_gon %>
Now you can read this value from your javascript:
alert(gon.my_session_variable)
This way you only include data specific to the request in your ajax response, and all your extra info is available from the gon variable.

No, session variables reside on the server, so there is no direct method to access the session variable with javascript/jquery.
Your best bet is to make an ajax call with javascript/jQuery to the server to an url you have defined to return the session variable you want in for example JSON format so you can directly access it in Javascript:
{
"sessionVariableName": "sessionVariableValue"
}
And no, this is definitely NOT a best practice. Session variables should stay on the server IMO.

Just in case this might help someone ...
While ejosafat's answer is valid, the main reason why I needed to access rails session variables from Backbone was to be able to modify them. Since I did not know for sure what I wanted, my question was ambiguous to start with (for example, accessing the session variables did not have to be a direct process). The way I ended up achieving my goal was by using the following four-step process:
make sure to initialize the needed rails session variables in the sessions_controller.rb file
send the values of the session variables that you want to be able to modify to backbone (in my case, the session variables were accessed by the used rails controller, then sent to the corresponding rails view, and then to the backbone app.js file, which made them accessible to the necessary backbone views)
create a function on the rails side (in the used controller) that can update rails session variables based on GET requests from backbone. For example:
def modify_session_variables
if params[:taskParentId]
session[:task_parent_id] = params[:taskParentId]
end
end
each time a backbone variable that represents a session state is changed by the user, send an ajax request to the server with the updated value(s) for the rails session variable. For example, one of my backbone models will update a session variable when one of its attribute is modified:
saveAsParentTask: function() {
var self = this;
$.ajax({
url: '/modify_session_variables',
type: 'GET',
data: {
taskParentId: self.getId()
},
async: false,
success: function() {
App.filtersState.setSubtreeParent(self);
}
});
},
This process will save the user choices as long as (s)he does not log out of the application. A bit complex, but works the way I want it to.

Related

browser tab wise session creation using JSP ans Servlets example [duplicate]

I'm developing a single page jQuery & Backbone.js web app. The backend is a JBoss 6 application server.
Until now we had the following structure:
There is only one servlet (front controller). Every request from the JavaScript client goes through here.
In the servlet - at the first request of a certain JS client - I make a look p to a stateful session bean. For the next requests of this client, I store the result of the look up in an HTTP session container. So every JS client has exactly one stateful session bean. This connection is kept by a session cookie.
Now I have an additional requirement:
When the user has two browser tabs (in one browser), they should have two isolated instances of the web app in every browser tab. Because of that I have a problem with session cookies because this session cookie is for all browser tabs.
I have to change the structure so that:
The servlet has to generate a new session ID for the first request of a certain JS client. This session ID is communicated to the client.
With every POST to the backend the JS client has to send this session ID.
My question is:
Until now I saved the result of the look up in an HTTP Session object and I hadn't to think about generating a session ID. But now I have to store this somewhere else, where?
Has anybody experience with this kind of setting and can help me?
Update:
Thank you BalusC for this very interesting approach.
When I understood you well, this means:
All individual JS clients of the tabs of one browser share one HTTP session object. And in this HTTP session object, every tab has its own entry point. That sounds really good. So I still can use the whole HTTP session infrastructure and don't have to reinvent the wheel.
Autogenerate an unique value on the initial GET request which you store and pass around on every subsequent postback as a hidden input value. Use this unique value as identifier of the session attribute representing the view-scoped data.
During the 1st request on a brand new session, do:
Map<String, ViewData> viewScope = new HashMap<String, ViewData>();
session.setAttribute("viewScope", viewScope);
(the ViewData represents the view-specific data you'd like to track across postbacks on the same view)
During every GET request, do:
String viewDataId = UUID.randomUUID().toString();
viewScope.put(viewDataId, new ViewData());
request.setAttribute("viewDataId", viewDataId);
During generating the HTML, do:
<input type="hidden" name="viewDataId" value="${viewDataId}" />
During every POST request, do:
ViewData viewData = viewScope.get(request.getParameter("viewDataId"));
// Get/set view-specific data in there.
Make sure that jQuery also passes this hidden input around (which shouldn't be a big problem if you already properly use $(form).serialize() or e.g. AjaxForm plugin to ajaxify the forms).
If you're familiar with Java EE's MVC framework JSF, then it may be useful to know that its #ViewScoped annotation works roughly the same as described above. See also a.o. How to choose the right bean scope?
You can use session tracking with URL rewriting. See here:
Session shared in between tabs

Rails API - helpers for AJAX

I have a normal rails app website, but on some pages I need to use AJAX.
I already have some working AJAX Javascript code using jQuery, but so far I haven't used any rails helper to do that, writing strings corresponding to paths manually.
But is there a more convenient way to do it in javascript ? Suppose I have a javascript function which takes an ID as argument, and must call an AJAX action. So far I've been doing it this way
var url = "/tags/tagID"
function getTag(tag_id){
$.get(url.replace("tagID", tag_id) +'.json')
.fail(function(data){
alert('Oops error !');
})
.success(function( data ) {blabla ] )
}
Is it possible to rename the .js to .js.erb and use path helpers ? So I could get rid of this url variable and write
routes.rb
resources :tags
tags.js.erb
$.get(tag_path("tagID").replace("tagID", tag_id)....
Or is there a more convenient way to do this ? I only need very little AJAX, so I don't want to use a frontend framework (Angular, etc.), just jQuery
EDIT My scenario
A user searches for a given tag thanks to an autocomplete searchbar. This searchbar will return the ID somehow.
The user can select several tags this way, and their IDs will be stored in an array. Now, upon clicking a button, I want to send a query to a non-RESTful (with the ID array as parameter) controller action via AJAX. For now I will focus on sending one item at a time (so just one ID string), for it is easier/more reactive.
This action is actually going to look in my models for projects and ingeneers that possess this tag, and return a JSON with formatted results.
Yes, you can use *.js.erb to use Rails helpers. Rails provides some handy helpers to work with Ajax. Normally with rails you can use them by using the the tag remote: true.
In your case something like
<%= link_to 'Tags', tags_path(<tag.id>), remote: true %> (roughly),
Read more about using Rails helpers with Ajax here, and this explains it nicely.
Update
Rails is using CSRF token to validate requests (except GET), so if you are going to use pure HTML/JavaScript, you want to add the token to your request. Have a look at this post on the same.
I agree there is no out-of-the-box way of doing that, but there are few workarounds.

Calling Devise from a javascript bookmarklet

I have a javascript bookmarklet which used to work as a single user mode. So how it used to work is I would click on my bookmarklet on the browser, and it would inject a remote javascript on my server. This second javascript in turn makes an ajax call to my rails server with a code that looks something like this:
$.post(
"http://192.168.1.2:3000/stuffs",
{stuff: JSON.stringify({"link":address})},
"json"
);
This code used to work when I was working on my project without an account. But today I added a devise authentication system. So now the system has users. In my controller code I have
before_filter :authenticate_user!
def create
puts current_user
#stuff.user = current_user
...
end
This doesn't work. current_user returns nothing. I am just trying to figure out whether there's an existing session (whether someone is signed in), and then want to create an entry under that user. In this case, a user has_many stuffs and stuff belongs to a user. The rest of the site works fine. It just seems trying to access using bookmarklet doesn't preserve a session.
Anyone know what's going on?
I believe you need to pass authenticity token parameter to params sent by jQuery, like this:
$.post(
"http://192.168.1.2:3000/stuffs",
{
stuff: JSON.stringify({"link":address}),
authenticity_token: <%= "#{form_authenticity_token.inspect}" if protect_against_forgery? %>
},
"json"
);
More docs on form_authenticity_token can be found here: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection.html#method-i-form_authenticity_token

Trouble with Creating Backbone Models & Collections

I'm really, really trying to learn Backbone and it's quite a lot for me to wrap my head around (coming from Rails). So I'm trying to code a simple app that just fetches a collection from a Sinatra backend. Right now, the route /schools returns a JSON object that looks like ["One School", "Two School"]. Pretty simple. Unfortunately, the following always returns ReferenceError for me:
School model
(function() {
window.school = Backbone.Model.extend({});
}).call(this);
School collection
(function() {
window.schools = Backbone.Collection.extend({
url: '/schools',
model: window.school
});
}).call(this);
Console
var f = new window.school({name: "temp"});
undefined
f.id();
ReferenceErrror
So simple interactions like this won't work. Also, calling window.schools.fetch() results in a UndefinedObject error. Don't know where I went wrong exactly, but nothing seems to be working. Any help would be awesome!
Edit: The collection & model are written in a closure because its compiled from Coffeescript.
There are two ways of getting the model's id: model.id and model.get('id'). model.id() is not defined so it will give you an error. See http://documentcloud.github.com/backbone/#Model-id.
I've never used Coffeescript, however, I'm getting better at backbone... so I'll give it a try. There are a couple of things that may be going on here. backbone.js is reliant on jquery or zepto, and underscore.js, which use the '$' and '_' as their special variables. That may be causing problems with coffeescript.
You might want to get a sample backbone app running instead of trying it with coffeescript.
As far as the code above, there are a couple of things I think I've spotted:
When you instantiate a model with data, it will have no 'id' (since it's not been synced with the server as per the documentation mentioned above). If the data IS from the server, then include an id: id in the init hash, and model.id will return an id. If you need a unique identifier for a model that has NOT been synced, you can use the 'cid' attribute (which is a local, unique identifier).
Remember, when you 'extend' you are actually building a class, so, unless you've instantiated an instance of the collection, a 'fetch' won't work. You'd need to do:
var collection = new Collection();
collection.fetch();
The reason why 'save()' isn't working is because you've not defined a url for the singular model. You've defined it in the collection, but not the model, so if you try to instantiate a non-collection model, it's got no reference to the restful service.
Hope that helps!
f does not have an id because it has not been saved to the server. Backbone has two unique identifiers for every model : one is the clientid which is created the very moment the model is client side. This is not sent to the server. When a model is saved to the server, Backbone expects it to return the JSON encoded saved model, which of course has an id attribute (which it aquires once it is saved in the database) and updates the local model to match the model data sent by server, thereby creating the id attribute on the client model instance. If your server side model does does not correspond exactly to the client side model then you can override Backbone.sync and Backbone.Model.parse functions to suit your requirements.
window.schools.fetch() fails because window.schools is a Collection class and not an instance. Create a collection instance just the way you created a model instance before you fetch and make sure the rails resource schools is correctly configured to send a json encoded list of school model instances.
Also if you are going with the out-of-the-box implementation of Backbone.sync , you will have to set :
ActiveRecord::Base.include_root_in_json = false

AJAX with Ruby on Rails?

This is probably a really dumb question with a simple answer but...
I am working on a project where I need to use AJAX to send/receive information. I am using Ruby on Rails (which I am pretty new to), but rather than using the helper methods or the built in functionality in the 'defaults' Javascript file, I need to do this manually in my own Javascript.
I believe that ultimately I will want to send a request with a JSON object, then have the controller return another JSON object containing the requested information.
The problem is that I cannot seem to find the syntax for sending something to a specific controller method, with parameters, directly from Javascript - everything I Google ends up being tutorials for using the Rails AJAX helper methods.
Depending on the version of Rails you're using, you can do the following:
Install the jrails plugin. This will replace all the Prototype javascript in your application with jQuery. This means you now have access to all the jQuery libraries, but it won't break your existing rails helper stuff (remote_form_for, etc).
Now you can use the jQuery AJAX to make any AJAX requests you want to make. A simple example would be something like below:
//Let's get a specific post by id
$.ajax({
type: "GET",
url: "/posts/123",
success: function(data){
//put the data in the DOM
}
});
Then just add the appropriate respond_to in your controller:
PostsController < ApplicationController
def show
#post = Post.find(params[:id)
respond_to do |w|
w.json { render :json => #post.to_json }
end
end
end
if using jQuery is an option for you, jQuery.ajax will solve your problem.
[and for those who likes to say jQuery is not solution for everything, i know that. i'm just giving him an option].

Categories

Resources