Include html fragment if on one of a number of pages (Rails) - ruby-on-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.

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,

Does the `cache do` block belong in the calling template or the called template?

What is the convention for where the cache do block goes?
Does it belong in "calling" template or the "inner" template that the "outer" template renders?
In some examples, I see the responsibility for caching a template B going in the "parent" or "calling" template, A—the template that renders template B. For example, https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails.html says:
On the product index, we’ve wrapped each product partial in a cache block.
and shows this example:
# app/views/products/index.html.erb
<h1>Products</h1>
<% #products.each do |product| %>
<% cache product do %>
<%= render product %>
<% end %>
<% end %>
This identical example can also be found at https://edgeguides.rubyonrails.org/caching_with_rails.html#fragment-caching
But isn't that responsibility in the wrong place? That puts the burden on the user of the template rather than on the template itself (to cache itself). Which means that you have to remember, every time you render the product template, to wrap it in a cache block if you want it to be cached. This leads to duplicated code ("not DRY").
Wouldn't it be better, then, to put it in the template itself? Something like this, perhaps:
# app/views/products/_product.html.erb
<% cache product do %>
<article>
<h1><%= product.title %></h1>
…
</article>
<% end %>
There appears to be an examples of a template wrapping its own content in a cache block like this later on in the guide, at https://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching:
For example, take the following view:
<% cache product do %>
<%= render product.games %>
<% end %>
Which in turn renders this view:
<% cache game do %>
<%= render game %>
<% end %>
but it's not 100% clear in precisely which files those templates actually belong. What file names would those views conventionally live in?
I can easily imagine/assume that the 1º template would live in either app/views/products/show.html.erb or app/views/products/_product.html.erb depending on whether it's an action view or a partial (it doesn't really matter which it is for the purposes of this discussion):
# app/views/products/show.html.erb ?
# app/views/products/_product.html.erb ?
<% cache product do %>
<%= render product.games %>
<% end %>
Where I am actually confused is the 2º template:
# app/views/games/_game.html.erb ?
<% cache game do %>
<%= render game %>
<% end %>
From context, I think we know this is a partial for a game (rendered once for each game in the collection passed in the _product template), so I guess it would live at app/views/games/_game.html.erb by convention.
But if that is the case, which is the 3º template that it is rendering here with render game?? It looks like it is rendering a partial for game — but aren't we already inside of the partial for game? Wouldn't this result in infinite recursion (games/_game.html.erb rendering itself recursively)?
Am I missing something obvious? Or is this just a poor example that needs to be updated in the guide? If so, what would an improved example look like?

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

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.

Rails - best way to display code only in production?

I have a few pieces of code that I would like to display only in production, for instance, the showing of disqus comments. What is the best way to go about doing that? Currently I have:
<% if RAILS_ENV.eql?('production') %>
disqus code here
<% end %>
But I am not sure if that's the best method, or is that it? Seems pretty verbose and I would need this in a few different places in the application.
The effective check is
<% if Rails.env.production? %>
disqus code here
<% end %>
There is no need to put it as a constant in your environment.rb or an initializer. Just keep your code simple and use Rails.env.production? in your main code base I say.
I'd suggest writing a helper method in your application_helper.rb file:
def render_disqus
return '' unless Rails.env.production?
#render disqus stuff here...
end
Then, in your view it gets really simple:
<%= render_disqus %>
If you want to display something in production, but not on a certain page(s), you can do something like this:
<% if !current_page?(controller: 'home', action: 'dashboard') %>
<% if Rails.env.production? %>
<!-- contant go here -->
<% end %>
<% end %>

Where to put repeating display code for views in a Ruby on Rails app?

I have a Note model, which can contain have either an image link attachment (linktype = "image" or some text (linktype = "text). When I display the notes, the method of display changes depending on the linktype. An example is:
<% #notes.each do |q| %>
<h2 class="title"><%= q.name %></h2>
<% if q.linktype == "image"%>
<img src="<%= q.link %>" />
<% elsif q.linktype == "text"%>
<%= q.text %>
<% end %>
<% emd %>
I have to display the notes in a few different views in my site, so rather than have to repeat the viewing code multiple times, I want to have it in one place and refer to it from different views.
Should I do this in the application helper? If so, do I put the display code, like the code above, directly into the helper, or is there a better way? Thanks for reading.
For repeating view code that has nothing to do with some entity (is not, say, users/show.html.erb) make a widgets folder and write your partial there. I put in my widgets nav-bars and such.
/app/views/widgets/widget1.html.erb
/app/views/widgets/widget2.html.erb
...
# some_view.html.erb
<%= render :partial => 'widgets/widget1' %>
In an abstract way, I make the difference between helpers and this kind of partials as so:
helpers are for view-related logic (iterate in a special way etc)
view widgets are data image, they just match holes with data

Resources