Rails implicit render in templates from multiple controllers - ruby-on-rails

I have code like render #posts to render my posts collection in an index template which the PostsController renders.
Now I have an Admin::PostsController that also should render the collection but when my posts controller renders #posts it looks for the admin/posts/_post.html.erb partial. Do I now have to write the partial path explicity? Is this feature by design or a bug? It doesn't seem to make sense.

Yes, you need to supply the path explicitly. And yes, this is by design.
It actually makes sense because Rails is a MVC framework and if you create a controller under a different namespace one would expect separate views for that controller too. Think about convenience, if you wanted to quickly bootstrap an application with a few simple commands, an application where there's a public view of posts and an admin view where all of the admin goodies for editing are, you would EXPECT to have a different directory to store all that admin views.

render #posts is a shortcut for a longer method signature.
In case of PostsController, it is a short cut for render :partial => "post", :collection => #posts; the partial is _post.html.erb and it is expected to be in app/views/posts folder.
In case of Admin::PostsController, it is a short cut for render :partial => "admin#post/post", :collection => #posts; the partial is _post.html.erb, and it is expected to be in app/views/admin/posts folder.
If you want a different partial to be used, you should specify it explicitly.
See the Rendering Collections section of Rails Guides page on Layouts & Rendering for detailed explanation.

Related

Render :layout is searching for partial instead of layout

I understand this is a bad idea, but from what I've seen in ApplicationControllers, using:
render :layout => "something" ...
Should render using a layout located at views/layouts/something.html.erb
However, when I am making this call from inside of a view, it errors out with:
Missing partial my_controller_name/something with ...
Searched in:
* "{path here}/app/views"
Which seems to me its looking for a partial, instead of a layout as I specified. Does anyone know what is going on with that?
A sufficient example small enough to reproduce it:
<%= render :layout => 'something' do %>
<div>Hello</div>
<% end %>
This is all under Rails vs 4.0.2
render works differently in controllers than it does in views. In controllers, it's primarily for rendering action templates, while in views, it's primarily for rendering partial templates. When you want to render a specific layout for an action, you have a few options, but all of them are in the controller.
If you want every action in a particular controller to use that layout, you can either specify layout 'something' in that controller (usually near the top) or for a ApplesController, you can create a new layout in app/views/layouts/apples.html.erb and this will automatically be used as the default layout for the ApplesController.
If you want just a single action in a controller to use that layout, you can use your render layout: 'something' inside of a controller action, where the action to render is implied to be the current action.
Links from the Rails docs:
Action Rendering
Partial Rendering
Nested Layouts

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.

Render a view of another controller

What I want to do is have 2 different controllers, client and test_client. The client controller is already built and I want to create a test_client controller that i can use to play around with the UI of the client and adjust it as needed. I am mainly trying to get around the validation i have built into the client and its dependence on an admin controller that loads the data.
so i want the test_client controller to load a sample data set and then render the client controller's index view so i can adjust the clients UI. That is all.
I tried this in the test_clients index method:
class TestClient
def index
render :template => 'client/index'
end
end
but i get an error because it cannot find the client partials as it is looking in the current controllers view for them...
So I have looked into this already and most people say that you should never make such a call but i think that this case is a reasonable usage...I just need to figure out how to get it to work.
You will need to adjust your view so that the path to the partial you need is in the form 'controller/partial'. In this case probably 'client/partial'. Then you can simply use render 'client/index' as before.
So say somewhere in your view you have this:
<%= render :partial => 'info' %>
You will want to change it to this:
<%= render :partial => 'client/info' %>
Convert your client controller views to partials, create empty views for all actions in test_client controller, render client partials for respective test_client views.
Example:
client view
index.html.erb to _index.html.erb
test_client view
index.html.erb
in this view, <%=render :partial => 'clients/index', :locals =>{ }%>
You could do this in a number of ways and everyone is different. One way you could do it, is by putting your finders into a presenter. Then turn some data in the index view into a partial or you can render the template with layout set to false.
Then in the client_test view you can render that index with the presenter associated with it.

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.

Where to put partials shared by the whole application in Rails?

Where would I go about placing partial files shared by more than one model?
I have a page called crop.html.erb that is used for one model - Photo.
Now I would like to use it for another model called User as well.
I could copy and paste the code but that's not very DRY, so I figured I would move it into a partial.
Since it's shared between two models - where would I place that partial?
Thanks!
The Rails convention is to put shared partials in /app/views/shared.
Update
Layout inheritance is now in the guides under layout and rendering
Template inheritance works similarly.
Rails 3.1 and following versions implement template inheritance, so I think the correct place for shared partials is now /app/views/application/, say you are in products#index you can do the following:
-# products#index
= render #products.presence || 'empty'
-# /app/views/application/_empty.html.haml
There are no items
btw it's application because the connection is the controller inheritance, so this assumes ProductsController < ApplicationController
This way if you implement /app/views/products/_empty.html.haml that will be taken, the above is a fallback for all the missing partials, and I can't check right now, but I think even for the template itself...
Railscast: template inheritance!
TL;DR
Rails 3.1, Rails 4, Rails 5 and whatever comes next
app/views/application
The engine searches this path automatically if the view is not found in the controller path.
Rails 3 and prior
app/views/shared
The engine does NOT search this path automatically.
Long story
Rails 3 (and prior version) have no default location for storing shared views.
The unofficial convention is to store shared views in app/views/shared. Wherever you'd end up storing them though, you have to specify the path
# render app/views/shared/menu.html.erb
<%= render :partial => "shared/menu" %>
This suggestion was popularized by Agile Web Development with Rails.
Rails 3.1 introduces an official standard for where to store shared views:
app/views/application
Thanks to this standard, the engine now automatically looks for templates in app/views/application. As a result, you don't have to use the full path anymore.
Those curious can follow here the thought process behind this decision.
Old syntax
# render app/views/application/menu.html.erb
# unless menu.html.erb is found in appp/views/my_controller
<%= render :partial => "menu" %>
New syntax
# render app/views/application/menu.html.erb
# unless menu.html.erb is found in appp/views/my_controller
<%= render partial: "menu" %>
Of course, you can still place your shared views wherever you want and reference them by path
<%= render :partial => "my_own_special_shared_folder/menu" %>
Unless you have a very good reason to do this though, please stick to the new standard and store your shared views in app/views/application.
The Rails View uses app/views/layouts for shared partials like header and footer, but the Ruby on Rails Guide uses app/views/shared in an example. I guess it comes down to personal preference. I would use layouts for high-level stuff like a main nav or a footer, but shared for more narrow controller-level stuff like a form.
I general have a shared folder in my views that contains commonly used partials.
I arrived here in 2021 (rails 6) and got confused by the answers (many different ways).
I asked some senior rails developers what they'd do and they also gave me 2 different answers.
But TL;DR
Create a folder called 'shared' or 'application' inside your views folder (e.g. app/views/shared or app/views/application.
Then simply move the partial there, and access it with either
<%= render partial: 'shared/socials' %>
# or
<%= render partial: 'application/socials' %>
or even simpler
<%= render 'shared/socials' %>
# or
<%= render 'application/socials' %>
It doesn't matter where you put them. You can render any partial at any arbitrary location by providing the file's path to render - it doesn't need to be associated with the controller that's rendering it. I use a directory simply called partials under the view directory, and call partials in it like this:
render :partial => 'partials/mypartial'

Resources