What are the best practices to generate "widgetized" content in Rails - ruby-on-rails

On my previous projects built with usage of Zend Framework I extensively used Zend_view's "Action View" helper. It basically allows to initiate a separate cycle of request->dispatch ti action->view_rendering from a view script.
Here is the link to an appropriate page of zend framework reference guide (search for "Action View Helper").
This helper is quite a convenient way to work with widgetized content for example on pages with portal layout where you can stuff a page with different widgets (like advertising blocks, currency informers etc).
Although such an approach negatively affects response time of the page as it involves a lot of additional routing/dispatching activity, it allows to organizes widget code and views scripts into a rational structure. One widget is just a controller action, with linked view script.
Unfortunatelly, I have not found any similar view helpers in Rails.
Is there any way to elegantly solve this task in Rails?

In Rails, the render method of ActionController takes a Hash as arguments and outputs a simple string. You can use this in your view like so:
<%= render :partial => "shared/widgets/_#{widget.widgettype.name}", :collection => #current_user.widgets %>
In this case Rails will iterate through the list of #current_user.widgets, find a widget partials in "app/views/shared/widgets/_widgettypename.html.erb", and call the partial for each attached widget.
For further reading on Rails partials, see the ActionView::Partials documentation.

It seems that I've found an answer to my questions myself. It is "render_component" rails plugin. There is also a fork which apparently supports Rails3.

Related

How to find where a view partial is used?

How do you go about easily finding out where a Rails view partial is used?
In what views, controllers etc.
This is handy when working on an app that someone else wrote. You don't necessarily know what views are using a particular partial, or where to find where the partial is used when navigating the app in the browser.
Currently, I am using the Sublime Text editor to project-wide search for the partial name "form" or "_form", or for "render ", but this gives an unnecessary amount of useless results.
You could try putting this caller inside the partial, and then running your test suite:
#haml
- p caller[x]
#erb
<%- p caller[x] %>
I used x because you'll have to play around with which index you're calling to get useful information.
There's no built-in solution, but I wrote a gem to try to solve this exact problem: https://github.com/Negotiatus/Partial-Finder
It's a rake task that will recurse through your project and attempt to create the full render chain (and routes!) for a given partial. Hope it helps!

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.

How do you handle widgets in rails?

StackOverflow, for example, has a user's reputation displayed up top. Clearly, this is grabbed from the database, and it's displayed on every page. Of course, this isn't in every controller action on every page, because that would be incredibly redundant.
How do you handle this kind of situation in rails? The only way I can think of it is to use before_filters to pass the models into the page, but that just seems like abuse of that feature. There seems to be the cells gem that does what I want, but I'd imagine this is a common problem and there must be a simple solution for it in rails without having to resort to plugins or gems.
What you are looking for is the layout. In rails this is where you define headers, footers, and sidebars that frame your site. Look for app/views/layouts/application.html.erb in your generated rails code. Towards the bottom you will see:
<body>
<%= yield %>
</body>
The yield is where rest of the app gets invoked. Everything before and after the yield will appear on every page. So, using your example, you might query the database and set the instance variable #reputation in the application controller:
#reputation = User.find( current_user ).reputation
then display it in the layout like this:
<body>
<%= #reputation %>
<%= yield %>
</body>
This is covered thoroughly in the book "Agile Web Development With Rails". If you are going to develop in Rails I recommend getting the latest edition.
I would just make a partial with the widget in it and render it in the layout(s) where you want it to appear. Let it do whatever it needs to do, eg connect to the db, run some js to connect to an external site, etc.
If you're concerned about optimisation then deal with it when it becomes a problem.
I guess, you can put the code you need into a view helper. And then render some partial, like it was said before, in the layouts where you want it to appear, calling helper's function.
Look here:
Rails view helpers in helper file

I'm looking for a good Rails source for this

I'd like a good source on how to set up controller actions and forms for creating a resource inside the view of another resource that it belongs_to...
Set up your controllers as you would normally. You'll need to use the nested attributes feature of Rails. This enables you to create children objects at the same time as creating their parent using one form.
This is my go-to link for nested attributes. The only change you will need to make if you are running Ruby 1.9.2 is in the setup_person helper. returning has been deprecated so you can change it to:
def setup_person(person)
person.tap do |p|
p.children.build if p.children.empty?
end
end
In typical Rails style, this will just work using standard controllers for each of your resources.
Other links
http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
http://jeffperrin.com/2009/06/04/rails-nested-forms-and-collection_select/
I don't have a web source that documents what I usually do, but I created a gist that documents what I do most often here: https://gist.github.com/900241
The premise of the gist is that you have a project model with many project roles, and you want to edit many project roles in the project form. This is pretty much the classic accepts_nested_attributes_for scenario, and just about any page that talks about it will give you a decent writeup. The problem is, the solutions I've seen have always involved some seriously messy obtrusive JavaScript that escaped your entire form view and threw it in the onClick method of a link. I recently came up with a cleaner unobtrusive approach using jQuery templates.
You don't have to do a thing to your ProjectsController when you move to a nested model. Everything Just Works at the controller level, and you don't even need a ProjectRolesController. (This is why I didn't bother including them in the gist.) At the model level, it's just standard accepts_nested_attributes_for. Where it gets interesting is in the view.
The project form has two form_for blocks: one rendering a jQuery template, and another rendering the project roles form. The jQuery template in turn just renders the project roles form (mmm DRY!), but from within a <script> tag, and with a blank project role. Because the form is within a script tag, it won't get submitted along with the project form, and because the script type is "text/x-jquery-tmpl", this is completely valid markup.
When the user clicks on "Add a Project Role", it fires some jQuery that takes the form within the template, replaces the index with the current date (this is all so this project role can be uniquely identified), and appends it to the end of the project roles section of the form.
When the user clicks on "Delete" next to a project role, it checks to see if this project role is a new record, and if not, it appends a "_delete" hidden field to the end of the form. In either case, it removes the project role div from the DOM.

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 :)

Resources