Where should I put chart-drawing code in Rails? - ruby-on-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.

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.

Ember: How to link to sections and actions (without nesting routes)

Let's say that I have a blog where each post can have several sections and comments and I'd like to use a hard-links to navigate and operate on this. There are several samples using some pseudo-code, of course they doesn't work, just demonstring my intends :)
Of course /blog.html#/posts/1 uses PostRoute, PostController etc and uses :post_id for finding object - that's obvoius.
How can I pass (and then access) additional params which doesn't change the controller but I can use them to navigation. ie /blog.html#/posts/1?section=123 should use the same route, controller and view as it was just Post, but I'd like to read the section and just navigate to section with #123
/blog.html#/posts/1/?comments=456 - actually should behave like section from point 1, but navigates to comment and optionally add some class to the container.
Other case: I'd like to go to section 123 AND additionally edit it with link like: /blog.html#/posts/1?section=123&action=edit. Now I'm using a button with an action like {{action editSection section}} and {{#if isEdit}} but I'd like to be able to reflect this in URL and also go to this state from URL (de facto my post can have several different modes not only preview/edit, therefore it should be accesible by the link).
I hope that cases makes sense, TBH I have no idea in which direction should I go. Tried with nested routes, but I'd like to avoid changing the controller. Also have no concept how to reflect the action in the URL...
I'm using Ember 1.1.2
You can use the model method of the route to handle such parameters, separate them from the model parameter and set the appropriate controller state.
Another approach would be to use nested routes that will render un-nested views(and controllers) - as explained towards the bottom here.

ROR: Nested views

I have a page object and various template objects in my application. pages have names, descriptions urls etc and each have a relationship with a template. templates have different numbers of content boxes and relationships with other controllers (like blogs, galleries etc.).
When I am rendering a page I can work out what template is attached to the page, and what the relevant content is based on that. but I am not sure what the best way is to render the nested Items.
Are you meant to somehow render the templates view from within the other view? Or would you have to just rewrite the view altogether? In this case would I have to create an extra template view for each different template, bundle it with the page views, and then only include it if it is the right one?
Would this be the same for galleries and blogs? do they all need to be bundled with the page? Or can it be called from its proper location?
I'm not sure what the best practice is here and haven't had any luck googling it. I'm suspecting that the key words im using aren't correct. Or this is common knowledge that isn't worth documenting.
You can use shared partials to render views. Check out this guide.
In the views, you can render the partials based upon whatever condition you want.
For example:
- if params[:page] == "my_page"
= render "shared/my_page"
Naturally, you will still need to set up the needed data in the controller.
Shared logic for this can be placed in the Application Controller.

Still having a hard time with RoR MVC approach

I suppose it should do justice to state what I think I know so far as well as what I've done:
1) I created the app and did my first db migration; I now have my dev, test and production databases. The dev db has a table called 'wines'.
2) I made a scaffold which created the necessary files.
3) The basic index/update/destroy methods are set up and I can browse the pages.
4) From what I gather, the ActiveRecord class "Wine" automatically inherits properties from the database? Each column is a property and each row in the table 'wines' is a potentially instantiated object which is called from the wine_controller script.
The problem I'm having now is that I want to create a common layout that all controllers use. The only things that will change will be the page title, potentially some <link> tags in the header, the <body> attributes (javascript onload events most likely) and whatever lies inside the <body> tag.
I find myself looking up functions that will do what I want (like "favicon_link_tag", "stylesheet_link_tag" and "auto_discovery_link_tag"...) but I can't find the right place to PUT them! I know this has something to do with my lack of understanding of how things are executed/inherited. For example if I were to declare #pageTitle in application_controller.rb and use #pageTitle in ApplicationHelper it won't work. Or even using "stylesheet_link_tag" in application_controller.rb throws an error. I'm just not getting something.
How does each thing relate to another in terms of chronological execution, scope, etc.?
In your "app/views" directory there is a folder called "layouts." By default there should be an "application.html.erb" file in there, but if there isn't you can create it.
Your "application" layout file is the default layout file used by any view. However, if you want a particular controller to use a different view, you can override this. See this railscast, and this one is helpful too.
The main thing to understand is the content from any particular view will show up wherever the yield method appears in your application layout. The main 'yield' block gets the view file specified by your controller action, but you can mark anything inside any view to be passed to another yield block instead. For instance, the "title" example you gave could be passed to the head of your application layout. See this railscast for a detailed example of that.
For more, you should read the Rails Guide, and you might want to consider picking up a Rails starter book.
I got my feet wet with "Beginning Rails 3," which was a phenomenal introduction to the framework. A couple days with that book and it was all making sense to me, and I was developing faster than I ever had before. Rails rocks once you get to know it, but it's definitely worth going through a book.
Please continue to ask questions, I'll help if I can :)
-EDIT- To answer your question about control flow, it basically works like this:
Your browser sends a GET request for a particular URL.
The router takes that request, matches it to a controller action, triggers that controller action, and provides the controller any parameters associated with the request. For instance: if you requested example.com/posts/123?color=red this would trigger the SHOW action of your posts_controller, and would pass {:color => 'red'} to the params hash. You would access that using params[:color]
The controller action does its thing, and when it's done it renders output. By default it renders whatever view is located in app/<controller_name>/<action_name>, and will whichever file matches the extension appropriate to the request (ie an AJAX request would trigger <action_name>.js.erb and a GET request would trigger <action_name>.html.erb.
You can override this using the render method, for example by passing render 'foo/bar' to render using the view for FooController, Bar action instead of your current action.
Note that no matter what you render, the data available to the view is whatever is in the specific controller action the router triggered, not the controller action that would 'normally' render that view.
The view file is parsed using the data from the controller that called it. If you have any content_for methods then the view code that is inside the content_for block will go where you tell it, otherwise everything else will go to the main YIELD block in your application layout (or whatever layout your controller specified instead).
The application layout is parsed, and the content from the view is inserted into the appropriate areas.
The page is served to the user.
That's a simplification in some ways, but I think it answers your question. Again, feel free to keep asking :)

Rails: generating URLs for actions in JSON response

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.

Resources