Ruby on Rails - How to manage layouts in admin interface? - ruby-on-rails

I have a very simple site setup using awesome_nested_set and a single table called Pages.
I would like the ability to select different layouts in the admin when creating and updating Pages. What I envisioned is a drop down on the Pages form that allowed me to select a layout/template.
The only thing I know about layouts is you are required to add them to /views/layouts/ and specify the layout at the top of the controller. I need a way to manage layouts on a per Page basis inside the app itself.
Is that even possible? If so, can you explain on a high level how that might be done so I can have a starting point?
edit
Something like this:

You can easily change the layout at render by supplying the :layout key like so:
def some_action
#... stuff
render "some_action", :layout => "custom_layout"
end
You can also set layout to a symbol in the controller definition, and the controller will run the associated method to decide what layout to choose
class UsersController < ApplicationController
layout :decide_layout
private
def decide_layout
some_boolean ? "layout1" : "layout2"
end
end
You can also replace the symbol with a proc if you don't want the method located away from the usage. Finally, you can also call #layout in an action itself.

Assuming you have files in views/layouts called something like one_column.html.erb, two_column.html.erb, etc., and an attribute called layout on you page model, you could just do:
def show
#page = Page.find(params[:id])
render :action => "show", :layout => #page.layout
end
Is that what you're looking for?

Related

Best way to create views based on a theme

I need to create separate view pages for a controller that I have.
I have a CustomerController that, based on the customer I need to create custom pages, these pages have to be stored as a file not from the db.
So normally I would have this:
class CustomerController < ApplicationController
def show
#customer = Customer.find(param["id"])
end
end
And the views would be located at:
/views/customer/show.html.erb
What I want to do is have the view pages like:
/views/customer/1/show.html.erb
/views/customer/2/show.html.erb
/views/customer/3/show.html.erb
How can I reference the specific view pages to load from within the action?
Is there a way to just pass the value 1,2,3 to the view folder path?
You can specify a template to render in the show action. Rails, without you explicitly calling render, will render the template that corresponds to that specific action...so, in your case, it will automatically render customers/show.html.erb (Note: Your controller should be plural...ie CustomersController, not CustomerController)
To override this, you can do something like this
def show
#customer = Customer.find(params[:id])
render "customers/#{#customer.id}/show.html.erb"
end

Ruby on Rails views names dependences

May be my question it's a little weird for RoR developers, but i'm new in Ruby on Rails, and i only discover this world now - there is some dependencies in views names and definitions in controller?
If i have, for example, view called "parse-public-profile.html.erb" , should i add in controller definition with exactly this name? i mean "def parse-public-profile ... end"
I know, that this is basic, but simply i try to understand how controller knows, what views i have now; what i should change, if i will add/change-name of view, or how to define view, if in my "views" folder, i have another folder, for ex. "clients"
Thanks!
Rails follows REST this means methods as index, show, edit, update, destroy etc. are very common in an Rails controller. When you have a custom action(method) however on your controller Rails will look for the corresponding view file, so for example:
class UsersController < ApplicationController
def another_action
end
end
will try to render: app/views/users/another_action.html.erb
There is also the concept of partials which are normally called within a view file f.e. in users/index.html.erb
<% render :partial => 'form' %>
will try to render: app/views/users/_form.html.erb (note the _)
An in depth explanation can be found in the Rails guides
You can also use:
def index
render :template => "users/parse-public-profile"
end
The :template over rides the default file that Rails would have rendered.
For more info, see the Rails Guide on Layouts and Rendering at http://guides.rubyonrails.org/layouts_and_rendering.html.

Identical Files behave differently due to link with controller

I am building my first app with ROR and stumbled upon a couple of problems due to my understanding of the MVC
I have a page to add a new item, and this works fine, rails magically hooks it up to the items controller and somehow by magic it knows to look in the method 'new' as the page is called new.
But this layer is confusing me, as i need to now create a different version of new, same functionality but with a different look so to use a different layout to application.html.erb
So i attempt to create a copy of new.html.erb and create bookmarklet.html.erb - both contain exactly the same code: a link to a form. but of course bookmarklet will error on me because it does not have that link in the controller - how do i 'wire' up bookmarklet so that i can call the new method and so that it can behave in a similar way to the identical new.html.erb
Also, how can i tell the new bookmarklet.html.erb to ignore the application.html.erb and get its layout from another file?
thanks in advance
The magic happens in the routes. Rails uses something called RESTful routes, which is taking HTTP verbs and assigning standard actions to it. the new action is a GET request in HTTP speak, and if you are using scaffolding or following REST, will have the ruby call to build a new object in the controller, as an instance variable so you can use it in your view.
You have to tell rails routes that you want to BREAK this arrangement and to let /items/bookmarklet be used in the controller.
In your routes.rb file replace resources :items with
resources items do
member do
get 'bookmarklet'
end
end
In your controller put:
def bookmarklet
#item = Item.new
render :template => "bookmarklet", :layout => "different_layout" # or you can put this on the top of the controller, which is my style, but whatevs.
end
You should look into partials, if you are doing this as they clean up your code immensely.
A better way to think of things is to start with the controller instead of the view html.erb files. So each public method in your controller is effectively a page or action in the site. When you need a new action or page, add the method to the controller first. Then create the relevant view files.
So in your controller you'll need something like:
def bookmarklet
#item = Item.new(params[:item])
#item.save
render :template => "items/bookmarklet.html.erb", :layout => "different_layout.html.erb"
end
Normally you don't need to call render manually in the controller, but since you want a different layout than the default you need to specify it using render.

Asp.net MasterPage equivalent in Ruby on Rails, Trying to define a site wide layout

Asp.net WebForms and MVC has a concept of Masterpages which make it easy to define a one time layout for all the page of your site. In Rails I'm struggling to find an equivalent usage pattern or feature.
From what I've read it's really easy to define a layout in every action with:
layout: 'viewname'
Now that seemed pretty ceremonial to include in every controller so I added:
layout: 'application'
in the base ApplicationController.
So far this is working ok unless I have a more specific layout in the view pages. Is this common technique for getting a consistent style in your Rails application?
Imagine a simplified blog where we have a controller called PostsController which has two actions: index and show
The index action is called when the user hits http://yourwebsite.com/posts - this action displays all of the available blog posts.
The show action is called when a user gets a specific blog article - i.e. http://yourwebsite.com/posts/article-about-something-interesting
Let's say that we want the index page to have a two column layout and we want the show page for each blog-article to have a three column layout. To achieve this, we would simply define two separate layouts (in app/views/layouts folder) - we'll call the two column layout "application" and we'll call the three-column layout "alternate".
In order to get the index page to use the two-column layout and the show page to use the three-column layout, we could just do the following in our controller:
class PostsController < ApplicationController
def index
#posts = Post.all
render :layout => "application"
end
def show
#post = Post.find(params[:id])
render :layout => "alternate"
end
end
If we want all actions to use the same layout, we can just do this:
class PostsController < ApplicationController
layout "application"
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
end
end
Finally, if we do not specify which layout we want to use, then Rails will by default, render any layout which has the same name as the resource we are displaying. So in our example, where our resources are called "Posts", if we define a third layout called posts.html.erb (in app/views/layouts) then Rails will automatically use that layout when the user renders any of the actions in the PostsController - providing of course that we have not explicitly asked Rails to render another layout....
Hope it helps,
This PDF book excerpt from Rails for .Net Developers has a pretty good explanation of Rails layouts, along with a comparison to ASP.Net MasterPages. Since it seems to work pretty well, it's probably used fairly often, at least by developers familiar with the master page concept.

Rails Sub-controllers?

I'm pretty new to Rails and have an issue which I can't quite get my
head around as to the architecturally 'correct' way of doing it.
Problem relates to what I kinda call sub-controllers. The scenario is
this:
I have a series of pages, on which is a panel of some form containing
some information (think the user panel on gitHub top right).
So, in my app, I have controllers that generate the data for the pages
and render out the responses which is fine, but when it comes to this
panel, it seems to me that you would want some sort of controller action
dedicated to generating this panel and it's view.
Question is, how do you go about doing this? How do I render a 'sub
controller' from within a view?
I would put the logic in a helper or a module. (http://api.rubyonrails.org/classes/ActionController/Helpers/ClassMethods.html)
Then render partials where you want these things displayed. (http://api.rubyonrails.org/classes/ActionView/Partials.html)
Like Herman said, if it's logic that you need generated after the controller hands off to the view (ie, the Pages controller generates a page view, but you want a customized panel) then put it in a helper. Or, call a separate method in your Pages controller before handing off to the view. Or, if it's a lot of logic, create a Module and stick it in your /lib folder. So you could have a whole Panel module with methods that generate different parts of your Panel and which are called by your controller. But if you want to call these methods from within the view, then you should use a helper instead.
I dont think a module is what is required here, modules are required for shared behaviour across a small subset of your classes.
What I think is required here is the understanding of the inheritance of ApplicationController and also layouts
so, for example, my layout might look like:
<html>
<head><title>Foo</title></head>
<body>
<%= render :partial => (current_user ? "/shared/user_widget_bar" : "/shared/login_bar") %>
<%= yield %>
</body>
</html>
Any code that i want to use for it would go in my ApplicationController since it would be shared across the majority of my app:
before_filter :generate_user_widget
def generate_user_widget
if current_user
#avatar = ...
#unread_messages = ...
end
end
I understand that it might be cleaner for it to belong in a separate controller BUT honestly, unless the code is huge, it doesn't matter and can even still be put inside a module which is then included by ActionController. However it does need to be inside ApplicationController if you consider the scope of it.
If there are more related pages, say for example, you have a Rails app that manages multiple sites and you want shared behaviour across a particular site, try creating a parent controller which has no actions and only private methods, any controllers that need to have access to those methods can inherit off it. That way you can apply before filters to all controllers which inherit off it, saving you the pain of forgetting to add one in your non-parent controllers.
e.g:
class SiteA::SiteAParentController < ApplicationController
before_filter :generate_user_widget
...
end
class SiteA::ProductController < SiteA::SiteAParentController
def index
...
end
end
well, if you really need to call a controller action from the view, you can use components. They were part of the framework, now they only exist as plugins. One such plugin that seems to be well maintained is here: http://github.com/cainlevy/components/tree/master
from its docs:
== Usage
Note that these examples are very simplistic and would be better implemented using Rails partials.
=== Generator
Running script/generator users details will create a UsersComponent with a "details" view. You might then flesh out
the templates like this:
class UsersComponent < Components::Base
def details(user_or_id)
#user = user_or_id.is_a?(User) ? user_or_id : User.find(user_or_id)
render
end
end
=== From ActionController
class UsersController < ApplicationController
def show
return :text => component("users/detail", params[:id])
end
end
=== From ActionView
<%= component "users/detail", #user %>

Resources