Rails application view isn't displaying what i want - ruby-on-rails

I'm probably doing something really stupid but i'm unsure what i'm doing wrong.
I'm making a counter that sees how many times the user has been on the index page in their current session.
The following is in a store_controller.rb
class StoreController < ApplicationController
def increment_counter
if session[:counter].nil?
session[:counter] = 0
end
session[:counter] += 1
end
def index
#products = Product.order(:title)
#counter = increment_counter
#counter_msg = "You've visited this page #{pluralize(#counter, "time")}"
end
end
And the following here is in application.html.erb layout view.
<%= #counter_msg %>
Of course with other code but that seems irrelevant for now.
Nothing at all is displayed from #counter_msg
What am i doing wrong?
Thanks.

pluralize is a helper method. You must use the line bellow in application.html.erb
<%= "You've visited this page #{pluralize(#counter, "time")}" %>
or, include helper in your controller:
include ActionView::Helpers::TextHelper

The pluralize method is a view helper and should be called from inside the view. Also views are exactly designed for this purpose so a display string should be in the view anyway.
<%= "You've visited this page #{pluralize(#counter, "time")}" %>
Delete the #counter_msg line from the controller.

It's looking like you are calling method in wrong place, if you want to show #counter_msg then it should be defined inside application controller first include helper
include ActionView::Helpers::TextHelper
into controller
also, the current code is telling you can use your variable inside store index page.

Related

Display navbar on all pages except home page

I want to show two different navbars. One will be displayed, on all pages except home page, when you're logged in. While the other navbar will be displayed just on my landing page.
I am thinking that I will probably need to write an if statement.
If (current user is not logged in) or maybe (current user is viewing home page) do
<nav>Second navbar</nav>
else
<nav>First navbar</nav>
end
I am very new to rails, so I could be wrong. (And yes, I know that's not how to correctly write an if statement in Ruby)
Home page is located at:
home/index.html.erb
I normally do following setup:
create partial shared/_nav_menu.html.erb
inside partial I put logic like:
<% if current_user %>
// nav bar for logged in user
<% else %>
// nav bar for non logged in users
<% end %>
Then inside application.html.erb file I render the partial like this:
<%= render :partial => 'shared/_nav_menu' if show_menu? %>
Inside my application_controller I put logic like this:
def show_menu?
true
end
helper method: show_menu?
If I don't want to show the menu for static_pages then inside static_pages_controller I overwrite show_menu? method to return false.
class StaticPagesController < ApplicationController
def show_menu?
false
end
helper_method: show_menu?
end
You don't have to use exactly this setup but I like this setup because my nav menu logic is kept seperate in partial. All logic required to nav menu lives in this file.
This approach don't bloat my application.html.erb file with lots of if..else.
In the case of two menus, but leaving yourself open to more, I would use a similar-but-slightly-different approach than Reboot's answer.
In the layout:
<%= render :partial => #nav_bar_partial %>
Then in my application controller, define the default nav:
def standard_nav
#nav_bar_partial = "path/to/standard/nav/partial"
end
From there, you can override that nav partial any time you need to (with any partial you want) from your controller
#nav_bar_partial = "path/to/new/nav/partial" if condition_that_requires_a_different_nav
That way, you have a little more flexibility. If for some reason you want to add a third nav bar for some other condition, you can just override the partial elsewhere without changing any of the above code.
You can prepare two layouts, one for the landing page and another for rest of the pages. Take a look at the official documentation: http://guides.rubyonrails.org/layouts_and_rendering.html.

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

Ruby on Rails layouts and rendering

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

Rails view and controllers, (if/else statements)

I've just started my own rails project from scratch. I'm trying the view to display hello if the minutes variable is set to a certain value.
The code is in the controller right now, and I want it to display the output in the view. is this possible? or do I write it in the view? Not sure of doing it right.
home_controller.rb
class HomeController < ApplicationController
def index
#minutes = 23
#minutes = params[:minutes]
end
end
index.html.erb
<% if #minutes == 23 %>
<%= "helllo" %>
<% else %>
Anonymous
<% end %>
#minutes = params[:minutes] || 23
It's good enought in this simple case put some logic in the view but usually is better to add helper methods (Read the Getting Started guide is a good idea).
Probably you've already seen the app/helpers directory, in these helper files you can define methods which are available in the view layer, methods related to view layer but do stuff that would be weird or dirty to put in templates files.
For example in your case you could have a /app/helpers/time_helper.rb:
module TimeHelper
# I know the method name sucks a little
def show_hello_if_minutes_is_23(minutes = #minutes)
if minutes==23
"Hello"
else
"Anonymous"
end
end
end
and then use in your index.html.erb template:
<%= show_hello_if_minutes_is_23 %>
As you can see:
You can read the method name and understand what it does (at high level)
Logic is put in a ruby method
The method take a minutes argument but it's optional
And remember: usually repetition is evil (The DRY thing) but in view-land sometimes one time is too much (not in this simple case however).
UPDATE: I've just seen you put set the #minutes variable to 23 and then you overwrite it making the previous assignment useless. I don't know what you're trying to do in your controller but if your question is about having a default value for the minutes variable go with the Yuri's answer and use use the ||= operator: #minutes ||= params[:minutes].

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