Setting up meta data for a Rails Website - ruby-on-rails

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

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.

What is the difference between render and yield in Rails

Can someone explain the difference between "<%= render %>" and "<%= yield %> with <% content_for :partial do %>/<% end %>"? specifically how the routing changes when switching from one to another, the benefits of using one over the other, when is it practical to use one over the other. THIS is the closest explanation I have found, but isn't quite clear enough for me.
I have been trying for several days to wrap my head around this, but it seems that each configuration I try either comes close, or errors out.
If theres are three views, aaa and bbb and ccc, and each has an index.html.erb, but bbb and ccc have a _content.html.erb partial (signified by the underscore) how can you accomplish getting the bbb or ccc partial in aaa using either render or yield?
The following works:
aaa's index.html.erb :
<div">
<%= render 'bbb/content' %>
</div>
and bbbs _content.html/erb :
<p>Content from bbb.</p>
BUT this does NOT:
aaa's index.html.erb :
<div">
<%= yield :container %>
</div>
and bbbs _content.html/erb :
<% content_for :container do %>
<p>Content from bbb.</p> ### viewed in aaa
<% end>
and cccs _content.html.erb would have nothing, or the content_for, but I still dont get aaa's index.html to be populated with content.
If I use the render, I can explicitly place the content in. But I thought that the benefit of using the yield :whatever would allow me to choose what to populate it with, and I can't get it to populate anything as soon as I change it from render to yield. Do I also have to update the routes file? If so, how do I choose which one to populate it with? Does that mean its in the controller? and needs an action?
I also have though that it depends on which file is initially routed to, but like I said, I think I need to understand the difference between the two before I can begin to use the partials to my advantage.
First of all, yield is ruby, render is rails. Usually one uses a common layout for the application whose inner content changes according to action/context. The problem usually lies in defining where our layout ends and context-specific template begins. Take, for instance, the HTML title tag. Let's say you have an application called Cities. In most cases, you want your page title to be "Cities" all the time. But, if you're for instance, inside Amsterdam page, then you would like the have "Amsterdam" as your page title.
# application.html.erb
<html>
<head>
<%= content_for?(:page_title) ? yield(:page_title) : "Cities" %>
......
# city/index.html.erb
<% content_for :page_title do %>
<%= #city.name %>
<% end %>
<div class="bla"...
Within Rails you usually define your application title in your application layout. One strategy for changing the page title would be to use content_for in the specific cities template and change accordingly.
Render, on the other hand, accomplishes different rendering strategies. Straight. When you call render, it renders. content_for/yield doesn't render automatically, it is stored somewhere and then fills up the missing spots in the due places. So, you can think of it as more as a "store/search/replace" in comparison to render, which just plain renders.
Good rule of thumb to use one over the other is: if the template you are writing needs to present different information per context, strongly consider using content_for.
yield
Ruby code (Proc class) and takes your block and does what it is supposed to do with it. Yield is also fast compared with other Ruby based ways of doing the same thing.
I'd assume (and I only) use it in the layouts because it's quick and I mindlessly do what's normal in Rails. yield is also used to pass content to a specific spot in your layout. I often have <%= yield :head %> in the head, just above the head tag, so that I can pass random weirdness that sometimes comes up.
Common Uses:
Mostly just used in layouts
(if you are fancy/inclined to do so in a Model) as a true Ruby Proc
statement.
render
Rails code that you pass arguments to that, as the docs say, "Renders the content that will be returned to the browser as the response body". partials, actions, text, files...etc.
Common Uses:
Used in both views and the controller.
When your controller method exits, it renders the associated file. So the edit controller renders edit.html.erb. It uses the specified layout or application.html.erb if none is specified.
Within your layout file, when you call yield it will fill in the information from your render. If you call yield with a parameter, it will look for a content_for section in your render file matching that parameter. I'm not completely sure, but I don't think you can call yield from outside of your layout file, and I don't think it will fill in any information except that found in your render file.
Anywhere in your layout file or your rendered file, you can render a partial by calling render with the partial name minus the underscore.
I hope that helps.
Edit to answer question in comment:
yield and render perform similar functions however yield only looks in the render file whereas render specifies which file to render. Also, render outputs the entire file, but yield with a parameter can output just a subsection of the file.
Here's a visual to put them both in perspective:
The render method is called at the end of a controller action and orchestrates what block is passed to the method that is actually rendering the application.html.erb by yielding the passed block.
https://richstone.io/debunk/

Check in view what page the user is on

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

Best way to handle dynamic css in a rails app

I'm researching a problem for handling dynamic css in a rails app. Within the app, individual users and/or groups of users can have customized look and feel that is accomplished via CSS. There will not be any fixed number of "look and feels" or css files, the number will grow as the number of users and groups grows and the look and feel is defined by the users via the application's admin interface. Throughout the course of a typical day thousands (it not tens of thousands) of different variations of the css will be served up. The app will store the pre-built css in mongodb, so there it will not have to pay the price of constructing the css for every request, the question is more about how is the best way to serve up this dynamic css content. I've seen other questions like [this one][1] that speak to using erb or sass, but some of these answers are dated by several years so I wanted to make sure there wasn't a better answer with Rails 3.
You can treat your CSS files as resources, store them on the database, and serve them with page caching, so that you only need to hit the db once when the CSS is modified. All later requests will be served directly by the web server from the cache, without ever touching your app or db.
# stylesheet.rb
class Stylesheet < ActiveRecord::Base
validates_presence_of :contents
end
# stylesheets_controller.rb
class StylesheetsController < ApplicationController
caches_page :show # magic happens here
def show
#stylesheet = Stylesheet.find(params[:id])
respond_to do |format|
format.html # regular ERB template
format.css { render :text => #stylesheet.contents, :content_type => "text/css" }
end
end
# the rest is your typical RESTful controller,
# just remember to expire the cache when the stylesheet changes
end
# routes.rb
resources :stylesheets
# layouts/application.html.erb
…
<link href="<%= stylesheet_path(#current_user.stylesheet) %>" rel="stylesheet" type="text/css" />
Well, I have worked with this a couple of times but they were definitely fixed no of CSS files to choose from. Its should be the same more or less.
One of things I used alot was the content_for blocks. Basically
<% content_for :css do %>
// some css file or css content
<% end %>
And in the layout
<%= yield :css %>
very simple way for managing the layouts.
This might give you a few ideas: Multiple robots.txt for subdomains in rails
I had a similar problem - but needed to serve the modified CSS only once.
I store a couple of constants in a module 'Site' - which I can then use as constants in CSS or as constants throughout the Rails application. I auto-generate the CSS files whenever the Rails application restarts and the CSS input files were modified.
You could do something similar, but reference symbolic names in site_settings.rb
and then fetch those on a per-user basis from MongoDB
http://unixgods.org/~tilo/Ruby/Using_Variables_in_CSS_Files_with_Ruby_on_Rails.html
Now let's say you have some dynamic styling called dynamic.css.scss.erb (the .erb at the end is important!) in app/assets/stylesheets. It will be processed by erb (and then by Sass), and as such can contain stuff like
.some_container {
<% favorite_tags do |tag, color| %>
.tag.<%= tag %=> {
background-color: #<%= color %>;
}
<% end %>
}

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