Rails: generating URLs for actions in JSON response - ruby-on-rails

In a view I am generating an HTML canvas of figures based on model data in an app. In the view I am preloading JSON model data in the page like this (to avoid an initial request back):
<script type="text/javascript" charset="utf-8">
<% ActiveRecord::Base.include_root_in_json = false -%>
var objects = <%= #objects.to_json(:include => :other_objects) %>;
...
Based on mouse (or touch) interaction I want to redirect to other parts of my app that are controller specific (such as view, edit, delete, etc.).
Rather than hard code the URLs in my JavaScript I want to generate them from Rails (which means it always adapts the latest routes).
It seems like I have one of three options:
Add an empty attr to the model that the controller fills in with the appropriate URL (we don't want to use routes in the model) before the JSON is generated
Generate custom JSON where I add the different URLs manually
Generate the URL as a template from Rails and replace the IDs in JavaScript as appropriate
I am starting to lean towards #1 for ease of implementation and maintainability.
Are there any other options that I am missing? Is #1 not the best?
Thanks!
Chris

I wrote a bit about this on my blog: Rails Dilemma: HATEOAS in XML/JSON Responses.
I came to similar conclusions. There's no incredibly clean way to do it as far as I know, because by default the model is responsible for creating a JSON representation of itself, but generating URLs is strictly a controller/view responsibility.
Feel free to look over my thoughts/conclusions and add comments here or there.

Related

How to accomplish this MVC layout

Being relatively new to MVC I have been struggling for the past several weeks getting my layout to work.
I have managed to get myself really twisted into knots. So instead of trying to explain and unravel my mess perhaps instead someone could explain how I would accomplish the following at a high level.
_Layout this would have all the css js etc. It would also have basic structure.
Of course HTML tags not allowed in code block....each render is in a div.
#RenderPartial(Header)</div>
#RenderBody()</div>
#RenderPartial(Footer)</div>
RenderBody is Index.cshtml and it would be broken into three pieces
#
#Html.Partial(NavMenu, model)</div>
#Html.Partial(SubNavMenu, model)</div>
#Html.Partial(MainContent, model)</div>
I have this basic layout and it looks fine until you click one of the menu items.
The menu items render as:
<a class="k-link" href="/stuffroute">Stuff</a>
That route goes to a controller that returns a view and that navigates away from the above arrangement in Index.cshtml. So I end up with the header, footer, and subdash nav....
So the question is...
How do I route / orchestrate my layout to not lose the differing pieces?
Partials don't do anything for you here. You're essentially asking about how to create SPA (single page application), although in this case your application will have other pages, it's just that the index view will act like a SPA.
That requires JavaScript, specifically AJAX, to make requests to endpoints that will return HTML fragments you can use to replace portions of the DOM with. For example, clicking "Stuff 1" causes an AJAX request to be made to the URL that routes to FooController.GetSubNav([stuff identifier]). That action then would use what was passed to it to retrieve the correct sub-nav and return a partial view that renders that sub-nav. Your AJAX callback will then take this response, select a portion of the DOM (specifically the parent of the sub-nav) and insert the new HTML as its innerHTML.
If you're going to be doing a lot of this, you'll want to make use of some client-side MVC-style JavaScript library, like Angular for example. These make it trivial to wire everything up.

Where should I put chart-drawing code in Rails?

I've got some custom Ruby code for generating a chart (which will generally be displayed as an inline SVG in a "show" view) based on the contents of the model.
I'm wondering where I should put the drawing code. As I see it, I could:
Put it in the model, so I can call #my_object.chart_as_svg in my view ... but this would diverge from MVC
Put it in a view, like show.svg.erb, and let my controller respond to format.svg
Put it in the controller as a separate action
Put it in a helper...
What's the prevailing wisdom on this?
If you think you might reuse the charting code for other things, make it a class, put it in lib and set it up so you can do something like this in your controller:
#chart = MyChart.new(:data => #my_object.data_method, :title => 'Foo Chart', ....)
send_data #chart.to_svg, ...
..
This way you can extend it with other options, add .to_png, etc without mucking up your model.
IMO: A little bit of everything! Going down your list:
Your object has charts, so you should definitely have #obj.chart, but the chart isn't a part of the model - it may be created using model data, but it's not something that's a) restricted to that model or b) required by that model - so you do want it as part of a different package/module/object/etc
The Ruby object of the chart, IMHO, shouldn't "know" how to turn itself into an HTML view. That's the job of a partial - _chart.svg.erb, _chart_typeB.svg.erb - but it should "know" how to transform its information - counts, averages, percentages, etc etc. Partials then consume those different "formats".
I'm going to bet that at some point, you'll want to access the chart data directly via an API. Maybe you're turning your stuff into a platform, maybe you're doing AJAX updates to the current page; doesn't matter - you're going to eventually want some controller actions to directly access the chart data.
You should take anything complex out of the view partial and turn them into helpers, but the partial should still be responsible for the "styling". That is, the partial should generate the smallest atomic view of the chart - just the chart, nothing but the chart - but the partial should be whatever else you generally want to show when you want to include the chart in a web page.
Edit: Reading the other answer a bit, I've got a different assumption: I'm assuming your taking the chart information and generating the picture via Javascript on the webpage, rather than generating a picture on the server and serving it. If you are doing the latter, I'd make it as part of the chart class - it's a different "format" into which the data can be transformed.

mixing clientside javascript template with rails logic for backbone.js

I've worked a while now with Backbone.js, and one of the things I nowadays run into is; Sometimes you need to have serverside logic into a .eco.jst template
For example
an i18n translation (currently look at i18n.js gem for this)
a path a route without hardcoding it (somemodel_path(somemodel))
authorisation (for example, show a delete button if the user can destroy this model). Atm I solve this by passing in some rights object in the json that gets filled in.
Rendering a html helper like simple_form or S3_file_uploader (atm I solve this with rendering it serverside, and put the display on none)
As you know, .eco get parsed by node.js, so I can't call ruby in the eco files. Most of these problems I solve by basicly creating a "data" object in the head. Similar to this:
window.data = {
some_translation = "<%= t('cool') %>",
<%= "can_destoy_model = true," if can?('destroy', Model) %>
post_edit_link = "<%= post_path(#post) %>
}
Besides this being bulky (this is just an example, normally this would be more ordened or I add a html5 data attribute to some dom element), It's time consuming, sometimes you have to recreate complete business logic which otherwise would be a oneliner in rails (take for example the S3_file_uploader, which requires encoded amazon policyfile and a token)
What are your thoughts about this? Should I perhaps not use .eco (although I like templates in seperate files instead of poluting the view). Would I able to use serverside logic if I for instance used mustache or handlebars and which gem would you recommend if so?
My experience with Backbone.js is kind of limited, but I've managed to setup an environment with logic-less templates using the following gems:
handlebars_assets
haml_assets
And a bunch of other stuff, even a mini-framework I'm currently working on (you can find it here)
I picked this approach for building Single Page Applications using Backbone.
Basically, the haml_assets gem provides sprockets with the ability to parse .haml files, this is not needed but I love HAML syntax. The handlebars_assets gem provides means to parse Handlebars templates, both on the server-side and the client-side. You can use Ruby code inside the templates and you would solve both the i18n and the path methods problems you mentioned.
I've found these tools to be excellent to help DRY an application's templates, and it can really save you from adding logic inside templates. If you use Backbone Views to take, for example, decisions on whether to show a delete button or not, you can keep the logic inside the Backbone View, and use that logic to render the proper Handlebars template (or partial).
Using your example:
Coffeescript:
class ProjectShowView extends Backbone.View
template: (context) -> HandlebarsTemplates['projects/show'](context)
deleteButtonTemplate: (context) -> HandlebarsTemplates['projects/shared/delete_button'](context)
render: (canDelete = false) ->
#$el.html(#template(#model.toJSON()))
#$('.delete_button_container').append(#deleteButtonTemplate()) if canDelete
#
The example is quite primitive and basic, but can hopefully point in the right direction. I hope it helps!

Backbone With Rails

I'm getting my data from a service and then setting the data to my bean object and want to using the same data in my view displaying it in a form in rails view now. Now I want to validate this form using backbone.
I'm new to both Rails and Backbone.
Kindly give me an idea on how to proceed on this.
I think perhaps you are confused about how web applications work. Backbone is a client-side framework; it uses Javascript code that is run in your users' browsers. Rails is a server-side framework; it uses Ruby code that runs on your server.
Given all that, your Backbone code and your Rails code by definition have to be completely separate. The two can communicate in only two ways:
1) Your Rails code can write <script> tags to the page (inside a .html.erb file) and put variable data there; for instance:
<script>
var myVarFromRails = '<%= someRailsVariable %>';
</script>
When that comes back from the server (ie. when you view source the page) that will get converted to:
<script>
var myVarFromRails = 'foo';
</script>
(assuming 'foo' was the value of someRailsVariable).
2) Your Javacript code can make AJAX requests to Rails URLs, and whatever the Rails code spits out there will come back as the response to your AJAX request. In other words you can do:
$.ajax({url: someRailsUrl, complete: function(response) {
// whatever the server sent back will be inside the "response" variable
}});
Other than that the two are pretty much entirely separate, and if you want to do the same thing in both of them (eg. validate a form) you essentially have to write the code twice, once for Ruby and once for Javascript.
I say "essentially" because there are Rails plug-ins which do #1 and #2 for you in different ways. I'm not a Rails expert, and even if I was there are so many of these plug-ins that you really need to look for yourself to find out what exists and what makes sense for your codebase.
Hope that helps.
* EDIT *
I know I just said I wouldn't list libraries, but then I realized it'd be more helpful if I at least provided a few to get you started. Just don't take these as canon; they're simply some popular libraries at the moment, but they may or may not be right for you.
https://github.com/codebrew/backbone-rails
https://github.com/meleyal/backbone-on-rails
https://github.com/aflatter/backbone-rails
https://learn.thoughtbot.com/products/1-backbone-js-on-rails
http://kiranb.scripts.mit.edu/backbone-slides/
That last two aren't actually libraries, they're a book/presentation, but I thought they might be useful.

Backbone without Hashes?

I think I'm trying to use Backbone in an unintended way, and I couldn't really find much on it. Basically I have a Rails app that is serving up the views. I want to keep the regular navigation (as in page reloading), but let backbone see the route and setup certain parts of the templates on that page, handle the models, and all of that good stuff. So basically I'm using Backbone to handle all of my complicated javascript without making it a "single page app". Would enabling PushState break my absolute paths in older browsers? eg: "http://localhost:3000/projects" matching the route "projects".
PushState will not work in old browsers like IE6, but you could use different technique, for example you could use jQuery selectors and check whether you're on the particular page:
if ($('#login-page').length > 0) {
// we're on the login page
// ..initialize login page related backbone collections and views
}
..or you could store action/controller name somewhere in the html using data attribute: <body data-action="edit" data-controller="post"> and check it in javascript va4 $body = $('body'); if ($body.data('action') == 'edit' && $body.data('controller') == 'posts') {} etc.
..or you could have separate js file for each action/controller pair and include it on demand.

Resources