I generated a Rails application, and am playing around with the internals. Previously my application.html.erb was rendering properly, but now it seems like Rails is totally ignoring it because it won't even generate an error.
There have been a bunch of questions on Stack Overflow regarding this problem. I've looked at what I think is all of them, but none have helped.
My routes:
Rails.application.routes.draw do
# static_pages from rails tutorial ch. 3
get 'static_pages/home'
get 'static_pages/help'
get 'static_pages/about'
end
Here is the views/layout/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>This Title is not showing up</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<p> why isnt this showing up?? </p>
<%= yield %>
</body>
</html>
Here is the static_pages_controller:
class StaticPagesController < ApplicationController
layout 'application' #<- I know this shouldn't be necessary, but I thought i'd try
def initialize
#locals = {:page_title => 'Default'}
end
def about
#locals[:page_title] = 'About'
render #locals
end
def help
#locals[:page_title] = 'Help'
render #locals
end
def home
#locals[:page_title] = 'Home'
render #locals
end
end
Here is the Application Controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
end
There are no other layouts. My Views folder has the following structure:
-Views
|-layouts
||-application.html.erb
|
|-static_pages
||-about.html.erb
||-home.html.erb
||-help.html.erb
I've tried purposefully generating an error in the application.html.erb, calling variables that don't exist and whatever other shenanigans. Rails is totally ignoring me and I'm feeling insecure.
All I wanted to do is to display the page name in the <title>, but I can't even get plaintext to render correctly. How can I get this to work so that I can properly fail at getting the controller variable in the title?
You should not override the controller initialize method. Doing this will break the base class behavior.
While, I believe, just calling the super from the initialize will fix your issue, the correct Rails way to initialize a controller for a specific action is to use a before filter instead.
Example:
class StaticPagesController < ApplicationController
layout 'application'
before_action :load_locals
def load_locals
#locals = {:page_title => 'Default'}
end
...
end
Related
I am stuck at what I think is a very simple/common usecase in a Rails web application. I want to use "caches_action, layout:false" and display, from the layout, dynamic tags that will be set by the action (either from the view or the controller).
I could not find any standard rails way to do this as content_for does not work with caches_action, instance variables are not cached (?), and the metatags helper gems that I have tried (metamagic and meta-tags) do not support this usecase.
Is there any way to do this ?
Example
I am using caches_action, layout:false on a SandboxController#show method
#app/controllers/sandbox_controller.rb
class SandboxController < ApplicationController
caches_action :show, layout: false, expires_in: 1.minute
def show
#meta_title = "Best page ever"
do_some_expensive_operation
end
end
The view
#app/views/sandbox/show.html.erb
We are in show action.
The layout
#app/views/layouts/application.html.erb
<title><%= #meta_title %></title>
Debug: <%= #meta_title %> <br/>
<%= yield %>
Thanks !
I found a way to make it work, it's not as pretty as I would like it to be but it helps using caches_action and setting HTML meta tags from the view.
Also, for the record, it seems that this was forgotten and buried deep down in the pipeline, as I did not find any recent mentions of this problem, only that caches_action and content_for together are not expected to work.
Solution: I simply add a before_action to set the meta tags by using as less computation as possible.
#app/controllers/sandbox_controller.rb
class SandboxController < ApplicationController
caches_action :show, layout: false, expires_in: 1.minute
before_action :seo_show, only: :show
def seo_show
#meta_title = "Best page ever"
end
def show
do_some_expensive_operation
end
end
It's worth noting that it can be used in combination with metamagic gem too.
Layout:
#app/views/layouts/application.html.erb
<%= default_meta_tags && metamagic %>
<%= yield %>
And helper:
#app/helpers/application_helper.rb
module ApplicationHelper
def default_meta_tags
meta title: #meta_title || "Default meta-title of my website"
end
end
Hope this helps someone out there !
I wrote a basic test to ensure pages and titles are rendered as expected.
static_pages_controller_test.rb
require 'test_helper'
class StaticPagesControllerTest < ActionController::TestCase
test "should_get_contact" do
get :contact
assert_response :success
assert_select "title", "Contact"
end
end
routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get 'static_pages/help'
get 'static_pages/about'
get 'static_pages/contact'
end
contact.html.erb
<% provide(:title, "Contact")%>
<h1>Help</h1>
<p>
Some text.
</p>
application.html.erb
<!DOCTYPE html>
<html>
<head>
<title><%= yield(:title) %></title>
...
</head>
<body>
<%= render 'layouts/header' %>
<div class="container">
<%= yield %>
<%= render 'layouts/footer' %>
</div>
</body>
</html>
and this is my static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
def about
end
end
As you notice there is no "contact" action so I expected test not to pass, whilst I keep getting green light.
davide#davidell:~/Desktop/app/sample_app$ bundle exec rake test
Run options: --seed 62452
# Running:
....
Finished in 0.194772s, 20.5369 runs/s, 41.0737 assertions/s.
4 runs, 8 assertions, 0 failures, 0 errors, 0 skips
Why? What am I missing? Thanks a lot for your answers, and happy new year :)
For better or for worse, this is one of the various "features" exposed by Rails as part of the convention over configuration approach. In my experience, this specific feature is mostly an unwanted side effect.
If your request matches a route and there is a template for that route matching the controller and the request format, then Rails will pretend the action to be defined as a simple empty method in the controller.
In your case, because you created the contact.html.erb template and you created a route that matches to the #contact action, this is equivalent to the same controller
class StaticPagesController < ApplicationController
def home
end
def help
end
def about
end
def contact
end
end
When I run my tests I get this error:
1) Error:
CategoriesControllerTest#test_new:
ActionView::Template::Error: No such file or directory # unlink_internal - C:/Users/Javi/AppData/Local/Temp/execjs201504
29-7044-qg60v0json
app/views/layouts/application.html.erb:6:in `_app_views_layouts_application_html_erb__20669174_54234900'
test/controllers/categories_controller_test.rb:6:in `test_new'
4 runs, 3 assertions, 0 failures, 1 errors, 0 skips
Here is my application.html.erb file:
<!DOCTYPE html>
<html>
<head>
<title>TestingBasics</title>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
I created a categories/new.html.erb file and saved it
This is my controller test that is failing:
require "test_helper"
class CategoriesControllerTest < ActionController::TestCase
def test_new
get :new
assert_response :success
end
end
EDIT::
Here is my controller:
class CategoriesController < ApplicationController
def new
#category = Category.new
end
end
I also have the following in my routes file:
Rails.application.routes.draw do
resources :categories
end
I have a new.html.erb on my views but its not working...
Does anyone know what is happening? It looks like there is a problem with my application file picking up json? I am new to testing so not sure what this is.
#coderwannabe2 your view should be named 'test_new.html.erb' not 'new.html.erb'
The error is stating that it cannot find a template matching 'test_new' which is true. Your template right now is 'new' so change 'new.html.erb' to 'test_new.html.erb' and you should have magick!
You should always remember that if in your controller you have inside the class
def view_one
end
def view_two
end
then your views must match the method name so in this case you would have
view_one.html.erb
view_two.html.erb
This is really watered down explanation but it should help get your mind around it all and how the method names relate to views and vice versa.
I am new to ruby on rails.
I recently converted my erb files to haml files manually. I am facing a weird issue now. I have two controllers and a couple of views. The home page shows the layout correctly but the other views do not. I am trying to use "application.html.haml" for the layout (which is picked up by default).
application.html.haml resides in app/views/layouts
All the controllers except ApplicationController have ApplicationController as their parent class. ApplicationController has ActionController::Base as its parent class
The home page shows all the tags from the application.html.haml layout. However, the other pages do not include those tags. Their source code does not contain html, body, head etc.
I do not have the layout specified in any of the controllers.
When I add the tags to the views, everything works. But, I shouldn't have to do that, right?
application.html.haml
!!! 5
%html
%head
%title XYZ
= stylesheet_link_tag "application", :media => "all"
= javascript_include_tag "application"
= csrf_meta_tags
%body
= yield
my_controller.rb
require 'somefile'
class MyController < ApplicationController
def initialize
#information = Information.new # inside models folder
end
def fetch_information
input = params[:search]
input = input.strip
flash[:error] = nil
#hosts = #information.host(input)
if #hosts.count == 0
flash[:error] = "N/A: #{input}!"
else
# add to cookies
end
end
end
Found the issue! It was because I did not call super in the initialize method of my controller.
I have a bunch of different controllers, and I want to be able to do the standard "Welcome, User." How do I assign the user variable to make it possible to access from any controller?
Here's what I have so far in the application controller:
class ApplicationController < ActionController::Base
before_filter :authorize
protect_from_forgery
private
def current_user
User.find(session[:user_id])
end
protected
def authorize
unless User.find_by_id(session[:user_id])
redirect_to login_url, :notice => "Please Login"
end
end
end
Here's my application.html.haml file:
!!!
%html
%head
%title Pears
= stylesheet_link_tag "application", :media => "all"
= javascript_include_tag "application"
= csrf_meta_tags
%body
%header
= link_to('Home', '/')
- if session[:user_id]
Welcome,
= current_user.firstname
= link_to('Logout', logout_path, method: :delete)
- else
= link_to('Login', login_path)
= link_to('Signup', signup_path)
= yield
What's the best approach?
Thanks!
If all you want is to be able to embed the username into a welcome message, I would probably just store it in the session (as an optimization, to keep from fetching it every time), and then create a partial that displays
Hello, <%= session[:username] %>
and include the partial in your layout, or wherever you want to display this message.
If you're looking for more than a username or small amounts of data like that, a partial is still your best bet to display it, but to fetch the information from the database a before_filter to load the data (as suggested by MrYoshiji), is probably a good idea.
current_user is a private method, do it public, and your code probably will be working.
Normally, views can't call controller methods. However you can allow this for specific methods if you so desire, for example
class ApplicationController < ActionController::Base
...
def current_user
...
end
helper_method :user
end
Whether the user is fetched from the database each time or has some data stored in the session doesn't really matter, but your view shouldn't be coupled to that detail.
I would also be slightly wary of stashing just the user name in the session as an optimisation without first understanding how much of a performance difference it makes