Rails - best way to display code only in production? - ruby-on-rails

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 %>

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,

Strings passed from controller to view in Rails arrive empty

I am trying to pass a string to my view from controller like this:
controller:
def index
#str = 'foo'
end
view:
String: <% #str %>
The variable itself seems to arrive because I get no error. However, it arrives empty (only "String" is in html, nothing else). And it seems to work great with other built-in types, e.g. Time. What am I missing here? I use Ruby 2.2.1 and Rails 4.
As others have said, you need to use
<%= #str %>
I'll give you an explanation as well - you use <% %> for when you need to run some Ruby code that you don't want displayed to the screen. For example, you might have conditional logic like
<% if user_signed_in? %>
<%= #welcome_string %>
<% end %>
Use <%= %> when you want to output, drop the '=' for conditional logic or anything that doesn't need to display.
in your view
String: <%= #str %>
In view user following code:
String: <%= #str %>
In your view, use:
<%= #str %>
As the other users have pointed out, you need to use <%=
The = is an ERB flag to so export the result of the code inside of the tags and put it into the DOM.
If you want to put some logic into your page that you don't want to evaluate, you leave the = out.
<% if user_wants_to_see_output? %>
<%= "User will see this" %>
<% end %>

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 there any way to avoid context switching in erb files

Newbie here coming from Microsoft MVC3+razor.
Say I had:
<% if foo %>
<%= bar %>
<% end %>
Is there a way I can have:
<% if foo
magic_function_that_goes_to_output_buffer bar
end %>
Does this function exist?
Edit:
To be absolutely clear, I am looking for a solution I can use multiple times within <% %> blocks. Having a solution between <%= %> blocks just moves the problem.
An answer of "No" is acceptable as then I can stop being annoyed by it and just move on.
Solution accepted:
My application_helper looks like
def out(content)
#output_buffer.safe_concat(content)
end
My view looks like
prevReview = nil
#review.each do |review|
out render :partial => 'review',
:locals => { :review => review, :showDate => (prevReview.nil? ? true : prevReview.updated_at === review.updated_at) }
prevReview = review
end
Yes there is a way.
But before I tell it, please don't do this unless you have a really good reason. This is because you'll be tied to an implementation detail that may change anytime.
Just post a new question that target your specific problem, you'll certainly get good answers.
Here it is:
<% if foo
#output_buffer.concat("any string will do")
bar = call_what_you_want
#output_buffer.concat(bar.to_s)
end
%>
Okay, this works well on Rails 2.x but with Rails 3 you'll concat to a safe output buffer so you should maybe call #safe_concat instead of #concat on the #output_buffer variable.
It does, try this:
<%= bar if foo %>
Edit:
You can do that too:
<%= if true
"true"
else
"false"
end %>
But if you have much logic involved, you should better use a helper. So in you helper file:
def magic_function_that_goes_to_output_buffer(condition)
if condition
"true"
else
"false"
end
end
In your view:
<%= magic_function_that_goes_to_output_buffer(foo) %>
Maybe you want to look at the haml. With haml code looks cleaner and stylish.
<% if foo %>
<%= bar %>
<% end %>
This erb code transforms to:
- if foo
= bar
That's all. Couple of useful links haml site and haml-rails plugin page. Good luck!

Help me refactor my admin user views vs. non admin user views

I have a list of books that displays edit buttons and a bunch of extra info if the user that's logged in is an admin. Right now I have two separate partials that are rendered depending on what type of user is logged in. I used to have just one partial with a bunch of if user.admin? statements, but it started to get real ugly. Now I am juggling around two files, with little bits of duplicate data in each. Is there any better way to do this?
index.html.erb
<ul>
<% if #current_user.admin? %>
<%= render :partial => "book", :collection => #books %>
<% else %>
<%= render :partial => "non_admin_book", :collection => #books %>
<% end %>
</ul>
_book.html.erb
Title: <%= book.title %> EDIT BUTTON
<!-- Awesome extra info for admins -->
Author: <%= book.author %>
<!-- Awesome extra info for admins -->
_non_adminbook.html.erb
Title: <%= book.title %>
Author: <%= book.author %>
This question is like : should I only use I18n keys all over one partial/view or should I use X views/partials for each language ?
There is no good or bad solution. My opinion is that you should begin by using conditionals like <% if admin? %> blah blah <% end %>...
Then, if your admin view grandly differs from your non admin views, delete the conditionals and make two views : my_view / my_view_admin.
#192 Authorization with CanCan this cancan gem may help you
I really do not like any kind of duplication, but sometimes it is the easiest solution.
In your case, I can tell that
an administrator has the option to edit fields (inline?)
an administrator sees more fields
Generally I use the on_the_spot gem for inline editing, and I then work with a helper like this:
def on_the_spot_edit_if_allowed(object, field, options)
if current_user.admin?
on_the_spot_edit object, field, options
else
object.send(field)
end
end
And in that case my views becomes something like
Title: <%= on_the_spot_edit_if_allowed book, title %>
<%- if current_user.admin? %>
<!-- Awesome extra info for admins -->
<% end %>
Author: <%= book.author %>
<%- if current_user.admin? %>
<!-- Awesome extra info for admins -->
<% end %>
Unless it is otherwise (design/UI constraints) impossible, I would refactor that view to the following:
Title: <%= on_the_spot_edit_if_allowed book, title %>
Author: <%= book.author %>
<%- if current_user.admin? %>
<%= render :partial => 'extra_admin_fields'
<% end %>
Hope this helps.
Keep it like it is.
Your duplications are not that big.
The #current_user.admin? condition will be run only once with your solution.
If you put #current_user.admin? in a shared partial, it will be run for every single member of this collection. Not cool.

Resources