Check in view what page the user is on - ruby-on-rails

Is there any way in Rails to check which page a user is currently viewing?
my example: In the head section of my website I need to know if a user has opened a blog post. If a user is viewing a post then in the head section facebook open graph meta tags need to be added because user has the option to like the blog post.
It would be invaluable if someone could help me with this.

As other answers mention you can check current page by examining request object or params hash.
But there is better way to set up Facebook meta tags without explicitely checking page context. content_for helper method is a perfect hit here. See the docs here: http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for
Basically, content_for takes a block as argument and captures the output which can be displayed elsewhere with yield.
So you can capture some content in your views:
# index.html.erb (blog index)
<% content_for :facebook_meta do %>
<meta content='My blog' property='og:site_name'>
...
<% end %>
# show.html.erb (single blog post)
<% content_for :facebook_meta do %>
<meta content='<%= #post.title %>' property='og:site_name'>
...
<% end %>
And display them in layout:
# application.html.erb (application layout)
<html>
<head>
<%= yield :facebook_meta %>
</head>
</html>
The above is much cleaner than checking current controller/action in this scenario. You only define Facebook meta tags in places they apply to.

With help from Radar in the #RubyOnRails IRC channel, I used the following code:
params[:action] and params[:controller]

You can check all the context of the request (action, controller, headers...) using the request helper that is available in the controller
http://api.rubyonrails.org/classes/ActionDispatch/Request.html

Related

Rails: How to put stuff in header

In my Rails App I included OneSignal which requires the following link to be placed in the head of the document:
<link href='/manifest.json' rel='manifest'>
Unfortunately with Rails I cannot put this link there, because the entire layout gets rendered inside the body.
While this ...
$(document).ready(function(){
$("head").append("<link href='/manifest.json' rel='manifest'>");
});
seems to work in development mode, it does not work in production.
How can I add this link to my head section of the document?
If you don't want to make a site wide change (adding the script to every page on the site) you can pick the page you want to put it on and add to the .htmlerb file
<%= content_for :header %>
<%= javascript_include_tag "script.js" %>
<%= end %>
javascript_include_tag will allow you to send content from the controller to the header and if script.js is in your app/assets/javascript directory, that will be added to the header.
Another option is to make a specific layout and have the pages that need the script use that layout, by calling it in the controller like so:
def users_index
#user= User.find(params[:id])
layout: users_index layout
end
That will load the users_index_layout.html.erb file from app/views/layouts...if it exists, and complain loudly if it does not.
You can add <link href='/manifest.json' rel='manifest'> in app/views/layouts/application.html.erb.

Setting up meta data for a Rails Website

I have been struggling to figure out how to place my meta data for all pages. The head tag where the meta data must be placed is on application.html.erb. My approach at first which I later found as a bad practice was to check the URL path, like: if request.original_fullpath == '/faq' to show the meta data for the FAQ page.
Despite being a bad practice, this couldn't help on pages that are dynamic (like the user's profile pages). Then I decided to take a simple approach and make it work. I added the meta tags on each page individually. I was naive thinking that can be overridden by using the head tag on each page. So I was wondering, how do I set the meta tags/data for each page on Rails? What's your approach?
The content_for helper might work well.
# app/views/layouts/application.html.erb
<head>
<%= content_for :meta_tags %>
</head>
# app/views/posts/show.html.erb
<% content_for :meta_tags do %>
...
<% end %>

rails navigation and partial refresh

Thanks for your time!
I get some reports data on my hand and I want to present these data on the web. The view of the HTML will be divided into two parts, the left part and the right part. There's a tree view in the left part consisting of the report names. In the right part presents the contents of the report.
What I want to achieve is when I click the report name on the left part, it will call an Action in the Controller, and passed the report name as parameter. The Action will fetch the data from the database and represent the data in the right part. And now I am stuck on how to realize this kind of view.
I've Googled a lot on the Internet and found Frameset, Partials or Ajax may capable of this. Because I've never developed web applications before and also new to Rails. So can anyone give me some advise or suggestion?
Things I've already known :
I've used Frameset to accomplish a view like this. But I found it needs a lot of .html files and all these .html files are static. And many people don't suggest it at all.
Then I've Googled Partials. But it seems Partials don't call the Action. It directly loads the _partials.html.erb to the main view. And besides, how can I control the layout? Using CSS?
I've never used Ajax.
If you want a fluid, seamless transition between one report and another, you should use both AJAX and Partials.
The way that it works is something like:
Make a left column in the html that has some links
Make the right column inside a partial
Assign the links to jQuery listeners to call the AJAX.
I'll put a bit of code here to show how it works:
Controller:
def index
reports = Report.all
if params[:report_id]
reports = Report.find(params[:report_id]
end
respond_to do |format|
format.html
format.js { render :template => "update_reports" }
end
end
update_reports.js.erb (in the same folder as the report views):
$('#report_viewer').html('<%= escape_javascript render :partial => "report_detail" %>');
In your view:
<div style=float:left>
<ul>
<li><%= link_to "Some report", "#", :class => "ajax" %></li>
</ul>
</div>
<div style=float:right id="report_viewer">
<%= render :partial => "report_detail" %>
</div>
<script type='text/javascript'>
$(document).ready(function() {
$(".ajax").click(function(e) {
$(this).ajax("your route to the action");
}
});
</script>
I think it's basically this, now let me explain a few things:
I don't remember if you have to do this, but in my case I created a new custom route to force the call to the action to be a json call instead of a html one. You can do this by adding :format => "js" to your route
You must name all your partials like "_yourname.html.erb". Rails won't recognize partials without the leading underscore.
In the controller, everything that comes after "format.js" is optional, you don't need to specify the template name, and if you don't Rails will look for the file index.js.erb.
The update_reports.js.erb file is basically a callback javascript that executes to update the current page. It finds the div where the partial is, and updates it rendering a new partial with the new report.
In the view, the link to change the report don't need to be a link at all if you're using the jQuery.click listener, but if it is a link, it must have the href as "#", or else the browser will just try to redirect to that location.
There are several ways to hook your link to the ajax function, I just chose the one I like it better, but you also could have a named function and call it in the html tag "onClick='yourFunction()'".
You need jQuery to call ajax like this. If you're sing Rails 3.0 or lower, you should replace the default Prototype with jQuery, because it's much better (IMHO), but I think prototype also have some ajax features.
It may seem complicated, but once you get the idea of it it'll become simple as writing any other action.
In the js callback file you could also add an animation to smooth the transition, like a fading. Look for the jQuery fade function for more info on this.
This is quite an open question so don't take this answer verbatim, but merely as a guide.
app/views/layouts/reports.html.erb
<!DOCTYPE html>
<html itemscope itemtype="http://schema.org/">
<head>
# omitted
</head>
<body>
<div id="body-container">
<div id="left-column">
<ul id="reports-list">
<% for report in #reports %>
<li><%= link_to report.name, report %></li>
<% end %>
</ul>
</div>
<div id="right-column">
<%= yield %>
</div>
</div>
</body>
</html>
app/controllers/reports_controller.rb
class ReportsController < ApplicationController
before_filter { #reports = Report.all }
def index
end
def show
#report = Report.find(params[:id])
end
def edit
#report = Report.find(params[:id])
end
def new
#report = Report.new
end
def update
# omitted
end
def create
# omitted
end
def destroy
#report = Report.find(params[:id])
end
end
routes.rb
YourApp::Application.routes.draw do
resources :reports
root to: "reports#index"
end
This would achieve the effect your after using just rails, of course adding ajax could add a better user experience.

Is there a workaround for ignored content_for blocks with caches_action and :layout => false?

When you use caches_action :layout => false in Rails 3, any content_for blocks that are populated in the cached view and used in your layout wind up empty. Is there any workaround for this?
E.g. in my application I have the following rather typical setup.
A helper method called from my views which sets the page title:
# application_helper.rb
def page_title(title)
content_for(:page_title) { title }
end
A line in my layout file as follows
# application.html.erb
<head>
<title><%= yield(:page_title) %></title>
</head>
And in a view I might call
# index.html
<% page_title 'Hello!' %>
Of course, if you cache your action with :layout => false, this results in having blank page titles, since action caching ignores all content_for blocks.
Is there no workaround for this? Action caching with :layout => false is so close to being brilliantly useful, but this glitch renders it quite awkward.
Other folks asking or commenting about this same issue:
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/d8d72f050701d44b
http://www.golygon.com/2011/04/tips-and-tricks-in-ruby-on-rails/
https://rails.lighthouseapp.com/projects/8994/tickets/4140-action-caching-with-caches_action-and-layout-false
And the Rails documentation that notes this behavior:
"WARNING: content_for is ignored in caches. So you shouldn’t use it for elements that will be fragment cached."
I believe the way I got around this was to create a fragment cache of the parts of the header that are dependent on the content_for being populated.
so it looks something like this:
# application.html.erb
<head>
<% cache("#{request.env['PATH_INFO']}/header") do %>
<title><%= yield(:page_title) %></title>
<% end %>
so this cached fragment should be populated at the same time the action is cached.
I had exactly the same problem and used this gist and it works fine!

Rails: using "content_for" after the corresponding "yield" inside layout

I think this has been asked before but even though I searched Google I haven't come up with a solution.
So this is what I'm trying to do in Rails 2.3.5:
layouts/application.html.erb:
<html>
<head>
... some other stuff
<%= yield :head %>
</head>
<body>
<% content_for :head, "something that belongs in the head" %>
</body>
</html>
Notice the yield before the content_for.
I know that Rails - by default - doesn't allow the content of :head to be defined after yield has been used - makes sense.
I even tried hooking into the template render process but no success so far.
So my goal is to be able to define content_for inside partials/templates and have the "yield" somehow delayed and executed just before the response is send to the browser.
Has somebody come up with a solution?
Greetings and thanks,
Frank
Update
I'll go with weppos's idea and try myself on rack middleware. thanks
The rendering process first loads and executes the action template, then decorates the template with the selected layout.
The layout is rendered from top to botton, thus you can't add more content to :head after :head is already rendered.
You need to change your strategy. Either place the fragment in a partial and attach it to your action views or use a post-processing strategy such as a Rack module/after_filter to alter the html code directly.
I probably would try to find a better solution based on what I actually need. If you are encountering this issue, chances are the error is somewhere else, perhaps in the app architecture.
There shouldn't be an equals sign in your content_for statement. It should be:
<% content_for :head, "Something that belongs in the head" %>
If you define the content within your templates and partials then it should work. This technique was covered in Railscast episode 8.

Resources