Ruby on Rails layouts and rendering - ruby-on-rails

I'm new to RoR and I'm a little bit confused with Rails MWC. I feel like I misunderstand something.
For example, I want to have home page where I could render top 5 articles and top 5 products. Products and articles have no relations at all, it is totally separate data.
So what I try to do is, i crate 2 sacffolds products and articles, and 1 controller for home page. I root to homepage controller. Then in homepage template i try to render products and article template. I get an error that methods which are used in products and articles controllers are undefined.
I don't understand where is problem. Is this kind of template rendering one template inside another is not Rails convention. Or I have bugs in my code.

I don't see your code but in this case I'm quite sure you have bugs in it.
app/controllers/home_controller.rb
class HomeController < ApplicationController
def index
#products = Product.top5 # Your logic to fetch top 5
#articles = Article.top5
end
end
app/views/home/index.html.erb
<% #products.each do |product| %>
<%= product.name %>
<% end %>
<% #articles.each do |article| %>
<%= article.name %>
<% end %>
This is perfectly fine, I've done that multiple times. Consider that in Rails you don't have any relation between controller and models, there are convention but Rails controller is not bound at all to any model

First, you need to instantiate your variables #products and #articles (this is an example) on your controller method. Then you can render the view.
Pay attention to add an # before. Only variables with an # will be available on your rendering view.
By default, when you call a GET for /products you'll arrive on the index method. At the end of this method, if not any view is specified, Rails will render views/products/index. In this view, you'll access all variables instantiate with an # and do whatever you want with.

First, yes, a template rendering another controller's template (not a partial) is not within Rails conventions.
A scaffold is a "single-resource" controller: it takes your model definition and generates a basic controller for editing and displaying that particular model (i. e., Product).
What you really need to do is use the two models you've generated in the home page controller, kinda like this:
class HomePageController < ApplicationController
def index
#articles = Article.top_5
#products = Product.top_5
# Render the #articles and #products in the view.
end
end

Related

rails best way to display button only for admin

I have an application with Post model with associated PostsController and Admin::PostsController under the admin namespace in routes. Controllers share the same index action with controller concern, similar to this approach. I'm using a shared view partial under the shared/posts/_posts_list to list all posts on site and also in the admin dashboard. All this is working as I expected.
I'm asking what is the best approach to add for instance: edit post button only for admin user, that view doesn't get bloated with conditionals like <% if current_user.admin? %> to display this edit button.
If you don't want to put conditionals in your view go for two
separate views (one for regular users, one for admins).
Use conditionals in view and
include necessary elements depending on is_admin?. This is the shortest way to go if the only conditional is is_admin? check, imho. If there are more conditionals (roles, etc.) you are right, this will bloat views. You can check redmine (it is a big, open source rails project) and see that it uses conditionals in views too. Check its base layout here.
You can use helpers to generate elements depending on is_admin?. Lets say you need to show menu elements in your view. You can generate menu elements in your helper. And you can call the helper from your view. Such as:
view.html.erb
<ul>
<%= main_menu_items("main_menu", current_user) %>
</ul>
some_helper.rb
def main_menu_items(menu_name, user = current_user)
allowed_items(menu_name, user).map { |menu_item| content_tag(:li, menu_item) }.join
end
def allowed_items(menu_name, user)
menus = {
main_menu: ["Menu Item 1", "Menu Item 2"],
main_menu_admin: ["Menu Admin Item 1"]
}
menu_to_return = menus[menu_name.to_sym]
menu_to_return += menus[(menu_name + "_admin").to_sym] if user.admin?
end
This is just some quick code. If you want to use it in production, you must make some heavy changes (refactoring, getting items from models, nil checks, etc).
I'd say you've got two approaches:
The one you mentioned. Your view shouldn't get too bloated, and this is the one I'd go with.
You could use the current_user.admin? Check to put an "is_admin" CSS class on your page body. Then, you can add "admin_only" classes to your buttons etc, and use CSS to hide the relevant buttons and stuff when no "is_admin" class exists.
Either approach is fine, but 1 is a bit simpler and immediately more obvious to anyone reading the code, so I'd start with that. Kinda depends on how many admin-only features your UI will have though... If there'll be heaps, option 2 might be worth it.
You need an authorization library(https://github.com/CanCanCommunity/cancancan)
Gem install
gem 'cancancan', '~> 1.10'
rails g cancan:ability
config
class Ability #model/ability.rb manage roles
include CanCan::Ability
def initialize(user) # user is devise current_user
if user.blank?
else
if user.role_id == 1 # role_id 1 is admin
can :manage, Post #Post is you model
end
end
end
view
<% if can? :update, #post %>
<%= link_to "Edit", edit_post_path(#post) %>
<% end %>
controller
class ArticlesController < ApplicationController
load_and_authorize_resource
def index
#posts = Post.accessible_by(current_ability).order("id ASC") #posts is filtered by current_user's roles
end
def show
# #post is already loaded and authorized
end
end

How to display a resource from a different controller?

I'm creating a mostly static home page for a website, except for one little container that needs to pull web links from a model database. The home page view is sitting in home_controller which I created earlier in development. I've now created the link resource and links_controller.
I don't know much about rails and what I've been doing up to now is displaying resources in the index view for their designated controller. However, i need to display this resource in the index view for the home_controller.
Can someone guide me in the right direction towards doing this?
Do a query to fetch the required link objects in the index action of home_controller and display it in the view, as you want:
# HomeController
def index
# other code
# #links = Link.all # or any other query you may want to do
end
# in the view index.html.erb
<% #links.each do |link| %>
<%= link.url %> <br/>
<% end %>
Customize it as per your needs.

rails rendering partial with collection / other class is in charge?

Somewhat new to rails, longtime programmer. I've got a question about views, controllers and partials really - wondering if I have this setup well.
I've got a pages controller, and on the index page (really the pages index method) I've got a partial in layouts called featured (ie app/views/layouts/_featured.html.erb) -- I've also got a Featured class. I would like basically the index of the featured class to be drawn here. But of course it's not working. SO the question is:
In the page itself I've got the <%= render 'features/index' %> which I'm beginning to think is the wrong way to go..
Do I axe this partial method and just call <%= render 'features/index' %> and let everything progress natively or
What would be the proper way of routing the featured collection to the partial? Since the controller is actually Pages it seems like I'm fighting against the tide.
<%= render 'features/index' %>
Doing this is wrong given your description. This will try to render a partial from app/views/features/_index.html.erb which you haven't mentioned.
To render the partial at app/views/layouts/_featured.html.erb you would do (perhaps a bit more verbose that is necessary)
<%= render partial: "layouts/featured" %>
The best suggestion I can offer is to pass a collection to this partial
<%= render partial: "layouts/featured", locals: { features: #features } %>
Since it seems your intention is for this partial to appear as a piece of a layout I will assume you wish for this partial to appear on multiple pages. This means on multiple actions you will need to have assigned the set of Feature instances this #features instance variable. One way to do this is a before_action.
before_action :setup_features
# ...
private
def setup_features
#features = Feature.all
end
A good place to start learning more about filters is in the Rails Guide
The partial at "app/view/layouts/_featured.html.erb" can only be rendered with
render 'featured'
and not 'featured/index'
render 'featured/index' will render "app/views/layouts/featured/_index.html.erb
Since the pages controller is really rendering the main index page in it's def index, all I had to do was #features = Feature.all and the variable is available for the partial pulled into the index page.
I need to get used to how simple rails is coming from other languages / frameworks.

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.

Rails file structure: 2 index lists in one view

I'm just helping a friend to create a little project. There are 2 models he likes to put in one view (kind of a summary for both of them, see code below)
class UnnamedController < ApplicationController
def index
#models1 = Model1.all
#models2 = Model2.all
end
end
Then in the view
<% #models1.each do |book| %>
...
<% #models2.each do |book| %>
...
Is that the right way to do it?
How do I name the controller and the view (Rails convention)?
Hope my English is not to bad and Thanks for any help!
Classes in Ruby are conventionally named using CamelCase, so "UnnamedController" is the proper name for your controller, just like "ApplicationController" also is.
The views are named after the action that calls them, so if your action is called "index", then your view filename should be "index.html.erb".
So you're doing it the right way.

Resources