Ruby on Rails: 'don't show on all these pages' condition - ruby-on-rails

I am new to Ruby on Rails and am having a difficult time figuring out how to not show a div on more than one page. Currently, I've only been able to make the following work for a single page:
<% if signed_in? %>
<% unless current_page?(account_setup_path) %>
<!--job seeker options-->
<% if current_user.job_seeker? %>
test
<% end %>
<!--end job seeker options-->
<!--employer options-->
<% if current_user.employer? %>
<% end %>
<!--end employer options-->
<% end %>
<% end %>
Any help will be much appreciated! Thanks in advance!

Since you want a div to not be shown on multiple pages you have several options. First, if the div is only meant to be shown on pages with a certain controller you'll want to move that div into a partial and reference it from all the associated views. If you want in only shown on one page you should put it in the view directly. If you need it shown on several different pages accross your app. you can simply check if the controller in your params hash matches. For example:
#I want this div shown on any pages handled by my `Admins` and `Users` controllers.
<% if params[:controller] == 'admins' || params[:controller] == 'users' %>
div here
<% end %>
This will add overhead to maintenance so you should think hard whether this div should be in a partial, in a specific view, or in the layout/application file etc.

Related

How much should I avoid computations in my views?

I am building an application where n users can talk to each other (like a messaging application) in public. Because you might want to have a different bio for each talk you do (for example a discussion about me regarding Ruby on Rails would need a different bio than one about Psychology) I have a Spkr model which has a User and a Tlk. The below code successfully means that on the users profile page, for each instance of them being a Spkr, the Tlk, and it's participants is visible with each Spkr's image (so if a Tlk has three participants, then all three images will be visible).
The setup is such where the default image is the User's image, but the Spkr can also customise their image by uploading one as a Spkr. I am worried that I am loading the front end with too much computation. Right now everything works... so is it ok? Or should I be limiting the computation happening when building views?
Thank you
<% #user.spkrs.each do |spkr| %>
<%= link_to show_tlk_path(spkr.tlk) do %>
<h4><%= spkr.tlk.title %></h4>
<% spkr.tlk.spkrs.each do |speaker| %>
<div class="tlk-tlking-image spkr-image image-spkr-<%= spkr.id %>"
<% if speaker.image.present? %>
style="background-image: url(<%= rails_blob_url(speaker.image) %>)"
<% elsif speaker.user.image.present? %>
style="background-image: url(<%= rails_blob_url(speaker.user.image) %>)"
<% end %>
>
</div>
<p><%= speaker.name %></p>
<% end %>
<% end %>
<% end %>
It tends to be considered good practice to keep the view as free of 'back end' calculations as possible. These files are often worked on by front end developers who may not even know how to code ruby, so the less of it that is in the view the better. It's also just not where it belongs in rail's Model Controller View framework.
First of all the code you've put can be simplified to:
<% #user.spkrs.each do |spkr| %>
<%= link_to show_tlk_path(spkr.tlk) do %>
<h4><%= spkr.tlk.title %></h4>
<% spkr.tlk.spkrs.each do |speaker| %>
<div class="tlk-tlking-image spkr-image image-spkr-<%= spkr.id %>"
style="background-image: url(<%= rails_blob_url((speaker.image || speaker.user.image) %>)"
>
</div>
<p><%= speaker.name %></p>
<% end %>
<% end %>
<% end %>
But as you say, if you want to handle this in a more appropriate place, I'd add a method to the Speaker class:
# app/models/speaker.rb
class Speaker << ApplicationBase
def image_for_view
image || user.image
end
end
This will let you call speaker.image_for_view which I think reads nicely in the view file itself.
Along with the great answer let me just add something that might help you to make views more clear. Might not be relevant to your question directly but might help you to get some idea how you can make views beautiful.
The first thing to make views look good are helpers. Though rails provide helpers for every controller, helpers are global meaning it can be used anywhere in any views. So, global formatings should be done with helpers. Like if you want a date formatter that needs to be used in a lot of view files, you can create a helper called date_helper.rb in app/helpers and put you desired date formatting -
module DateHelper
def formatted_date(date)
date.strftime([%m/%d/%Y')
end
end
Next is what rails people like to call a Presenter pattern. This is helpful when you don't want some logic to be shared across all views. Some logic that doesn't feel like belongs in controller or model are put there to make views readable. Suppose you have a view like below which is a bit messy -
<p>
Post title: <%= post.title.gsub("forbidden word", "") %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{post.draft? ? "orange" : "green"} border-#{post.draft? ? "orange" : "green"}" %>
</p>
To make this more beautiful you can create a presenter class named post_presenter.rb which should reside in app/presenters and write some code like -
class PostPresenter
def initialize(post)
#post = post
end
def title_without_forbidden_words
#post.title.gsub("forbidden word", "")
end
def css_color
#post.draft? ? "orange" : "green"
end
end
and in the view -
<% presenter = PostPresenter.new(post) %>
<p>
Post title: <%= presenter.title_without_forbidden_words %>
<%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
</p>
Such way a view might be more clear and also it can be lifesaver for frontend developers. This are the best two methods I found till now that makes a rails view beautiful which I always try to use.
Examples are taken from rubyguides website. Thanks to them,

Updating all with a dictionary in Rails

I have a small CMS-like program that has multiple pages that act like blog posts. Each page has content, and a position integer that identifies in what order they will appear on the page.
On my admin side, I have a draggable list of pages that I can reorder similar to how wordpress orders plugins. The page works as functions, and assigns the value of the dragged position to each page correctly. However, since all sortable pages have their own form, I cannot submit them all at once - only one at a time.
As an example, my code looks like this currently:
<div class="sortable">
<% #pages.each do |page| %>
<div class="dragBox">
<%= form_for(page) do |f| %>
<%= f.number_field :position, class: 'inPosition' %>
<% end %>
</div>
<% end %>
</div>
Because I can get every page_id tied to its new position, is it possible to submit those values in a new hash to get updated all at once in the controller? How would I go about doing this? Is there a better or easier way to do this? Thanks.

Application.html.erb template inheritance

I have a situation in rails (version 4.04, ruby version 2.1) where I've been using the standard application.html.erb to define the main framework for my site, header, footer, nav bar, etc. When I got to an inner div, call it, inner-content, thats where I put a <% yield %> statement so that the sub template can take over and place its content in the correct place (for example products#show or products#index have show.html.erb and index.html.erb respectively which just the content for those actions).
The problem is I realized I was duplicated some code in those sub templates. In ever one of them (except one) I always was starting off like this:
<div class="columns large-6 medium-6 center-small">
<div class="inner_wrapper">
And I was always ending like this:
</div>
</div>
So I was thinking, I shouldn't be repeating all this code. I should move this into application.html.erb so that every template automatically gets the inner-content set up correctly.
The problem is that one action I was talking about. There is one action that has a different setup. I don't want to have to type in those extra 2 divs for every sub-layout except one. Is there a better way to do this?
One way could be to check which controller your currently using this in your application.html.erb
<% if params[:controller] == "controller name" %>
<div>
<%= yield %>
</div>
<% else %>
<div class="different div">
<%= yield %>
</div>
<% end %>
Not sure if this the best way, but its one way to do it.
Create a different layout file and call it maybe products_layout.html.erb.
Then in the controller
class ProductsController < ApplicationController
layout: 'products_layout'
....
end
Or do it on a per action
def show
render 'show', layout: 'products_layout'
end
http://guides.rubyonrails.org/layouts_and_rendering.html

Include html fragment if on one of a number of pages (Rails)

I have a pretty standard Rails 4 application at the moment with a number of static pages (home, about, contact etc) as well as the database backed pages. I want to include a html partial across most of the site but not on static pages (apart from the home page). I can get this working if I was stating just one page to exclude, for example the about page, using:
<% if !current_page?(about_url) %>
include the partial
<% end %>
I'm not sure how to write this to have a list of urls, i.e. about_url, contact_url etc. I tried something along the lines of:
<% if [about_url, contact_url, ...].!include? current_page
but maybe not surprisingly it didn't work.
Is there a way to achieve this within the erb itself without going back to the controller? (I'm thinking I'd have to create the array for each controller)
Thanks for any help.
-- Update --
I just tried (following the comment below):
<% if not [about_url, contact_url].include? request.original_url %>
<%= render partial %>
<% end %>
it doesn't appear to like this either. I'm not sure if my syntax if off again or it can't be done this way.
You can go about doing this in many ways, one of which can be:
<% if current_page?(controller: 'YOUR_CONTROLLER_NAME') &&
(current_page?(action: 'about') || current_page?(action: 'contact') || ... ) %>
include partial
<% end %>
I think it should work but your syntax is off.
<% if [about_url, contact_url, ...].!include? current_page
should be
<% if not [about_url, contact_url, ...].include? current_page
Or if you prefer (but I prefer "not" since it's more readable)
<% if ![about_url, contact_url, ...].include? current_page
However, for readability and testability you should perhaps put that logic in the application controller or in a helper.
Okay, thanks to the commenters I got it to work using the following:
<% if not [about_path, contact_path].include? request.path %>
<%= render 'layouts/minibar' %>
<% end %>
** the main points being to use *_path to compare against request.path.

is it possible to access value from a model databasetable in the application.html.erb?

im trying to make a app
with users
this users can join multiple groups - every group has the same menu on the page
their group page is accessable about group/1 or group/2
so i wanted to put the the menu in the application.html.erb, with lnks depending on the group.id - but i dont know how to acces this id in the application.html.erb
This is often done using content_for in the layout. Let's say you want your menu in a certain div in application.html.erb:
# application.html.erb
<div id="menu_div>
<ul>
... etc ...
</ul>
</div>
Replace the inner content with a yield statement:
<div id="menu_div>
<%= yield :group_menu %>
</div>
Then in the view template add the content_for block:
# page
<% content_for :group_menu do %>
<ul>
... etc ...
</ul>
<% end %>
Each page template can then define its own menu code in a content_for block. This can be further generalized by using a helper method in the block and passing in instance variables.
EDIT
Assuming #group is assigned in the controller, you might do something like:
<% content_for :group_menu do %>
<%= show_me_the_menu(#group) %>
<% end %>
and in the helper (obviously contrived example):
def show_me_the_menu(group)
content_tag :ul do
group.users.collect do |user|
concat(content_tag(:li, user.some_method))
end
end
end
The correct approach would be to set this in ApplicationController via before_filter and just use it as an instance variable in views.
So in controller set something like #links = some logic where you calculate your links based on current user.
In view you do something like:
<ul>
<%- for link in #links -%>
<li><%= link.title =>
<%- end -%.
</ul>
Of course, you set your #links in ApplicationController only if you want your links to be available to all your controllers/views, which I think you do.
Rails Cells could also be used here http://cells.rubyforge.org/
i solved my problem in a different way now. i created in the group model a "menu" and this i render partial in the application.html.erb

Resources