Is it possible to require a class in an erb template? - ruby-on-rails

I have an erb template in which I need to use:
CGI.unescapeHTML(someEscapedHTML)
So I need to require 'cgi', however the following fails:
<% require 'cgi' %>
With the error:
can't dup NilClass

I would personally never put a require statement in a view, because 1) it's ugly and 2) what if another view needed that require?
A better place for this is in config/application.rb at the bottom, or in a file in config/initializers.

First of all do not require gems or libraries in ERB please. Then CGI is required by Rails itself already.
If you want to prevent Rails 3 from auto-escaping consider using
<%= data.html_safe %>
instead.

Related

link_to does not work in production (Heroku)

I don't have any problems using link_to locally however, as soon as I deploy to Heroku I get the following error:
users#show (ArgumentError) "arguments passed to url_for can't be handled. Please require routes or provide your own implementation"
app/views/users/show.html.erb:176:in `_app_views_users_show_html_erb__222687663100622833_69928454693640'
I am using ruby '2.2.0' and rails '4.2.0'
Any ideas on how to further debug this or replicate it locally?
Update 1. Here's the actual view code which displays pagination links. The resulting route should be /users/1?page=1 etc.
<div class="row text-center">
<%= will_paginate collection, renderer: BootstrapPagination::Rails %>
</div>
The issue was with url_helpers included in one of my models - everything worked after I removed the following include.
include Rails.application.routes.url_helpers
Using the correct url_for method in a Rails engine spec
I had the same issue with ActiveAdmin in production environment. In my case the issue was with ActionView::Helpers::FormTagHelper included at root level in one of my helpers.
I solved it moving the include statement inside a class defined in the same file.

Rails path-helpers doesn't work in js.coffee.erb

In my Rails 3.2 app (Ruby 1.9) I get following error when using path helpers in Coffeescript.
undefined local variable or method `new_user_session_path'
In my partial _usermenu.html.haml that works fine:
= link_to t('user.login'), new_user_session_path
In my app/assets/javascripts/metamenu.js.coffee.erb that throws above error:
$.get("<%= new_user_session_path %>")
Isn't it possible to use x_path and x_url helpers in coffeescript erb's?
This is because you are not within the view context inside of your assets. Adding an erb extension to the file doesn't change this, it simply allows you to evaluate embedded ruby.
If this is a one-off scenario, your best bet is to simply use the string itself.
$.get("/sign_in")
If you really wanted to you could create a partial that output a script tag that output your helper methods into js variables and access them that way.
# in your layout
<%= render 'url_helpers' %>
# in app/views/layouts/_url_helpers.html.erb
<script>
window.new_user_session_path = "<%= new_user_session_path %>";
# add more if necessary
</script>
# in your coffeescript
$.get(#new_user_session_path)
Also worth keeping in mind that this will obviously never work for member routes where your passing an instance of a model to the url helper as that is definitely not available to coffeescript. Remember, in production assets are precompiled so you can't use anything dynamic. For that you can only really rely on setting up actions in your controller to respond to JS calls.
Old post, but still accessible from Google.
In rails 4 (and certainly at least 3 too) you can use the route helpers to insert your js files easily:
assets/javascript/my_file.js.coffee.erb
<% self.class.include Rails.application.routes.url_helpers %>
window.index_route = '<%= index_path %>'

Rails 3.1 asset pipeline: how to load controller-specific scripts?

If I generate a new controller in Rails 3.1, also a javascript file with the name of the controller will added automatically. Firstly, I thought this javascript file will used only, when the related controller is called.
By default there is the instruction //= require_tree . in the application.js-file, that include every javascript file on it's tree.
How could I load only the controller specific script?
To load only the necessary name_of_the_js_file.js file:
remove the //=require_tree from application.js
keep your js file (that you want to load when a specific page is loaded) in the asset pipeline
add a helper in application_helper.rb
def javascript(*files)
content_for(:head) { javascript_include_tag(*files) }
end
yield into your layout:
<%= yield(:head) %>
add this in your view file:
<% javascript 'name_of_the_js_file' %>
Then it should be ok
An elegant solution for this is to require controller_name in your javascript_include_tag
see http://apidock.com/rails/ActionController/Metal/controller_name/class
<%= javascript_include_tag "application", controller_name %>
controller_name.js will be loaded and is in the asset also, so you can require other files from here.
Example, rendering cars#index will give
<%= javascript_include_tag "application", "cars" %>
where cars.js can contain
//= require wheel
//= require tyre
Enjoy !
I always include this inside my layout files. It can scope your js to action
<%= javascript_include_tag params[:controller] if AppName::Application.assets.find_asset("#{params[:controller]}.js") %>
<%= javascript_include_tag "#{params[:controller]}_#{params[:action]}" if AppName::Application.assets.find_asset("#{params[:controller]}_#{params[:action]}.js") %>
Your problem can be solved in different ways.
Add the assets dynamically
Please consider that this isn't a good solution for the production mode, because your controller specifics won't be precompiled!
Add to our application helper the following method:
module ApplicationHelper
def include_related_asset(asset)
# v-----{Change this}
if !YourApp::Application.assets.find_asset(asset).nil?
case asset.split('.')[-1]
when 'js'
javascript_include_tag asset
when 'css'
stylesheet_link_tag asset
end
end
end
end
Call the helper method in your layout-file:
<%= include_related_asset(params[:controller].to_param + '_' + params[:action].to_param . 'js') %>
Create specific assets for your controller actions. E. g. controller_action.js
Please don't forget to change YourApp to the name of your app.
Use yield
Add <%= yield :head%> to your layout head
Include your assets from your action views:
<% content_for :head do %>
<%= javascript_include_tag 'controller_action' %>
<% end %>
Please see the Rails guides for further information.
I like albandiguer's solution. With which I've found that javascript/coffeescript assets are not individually precompiled. Which causes all sorts of errors trying to use javascript_path. I'll share my solution to that problem after I address an issue a few people mentioned in his comments. Mainly dealing with only a partial set of controller named JavaScript files.
So I built an application helper to detect if the file exists in the javascript directory regardless of .coffee/.js extension:
module ApplicationHelper
def javascript_asset_path(basename)
Sprockets::Rails::Helper.assets.paths.select{|i|
i =~ /javascript/ and i =~ /#{Rails.root}/
}.each do |directory|
if Dir.entries(directory).map {|i| i.split('.')[0]}.compact.
include? basename
return File.join(directory, basename)
end
end
nil
end
end
This method will return the full path to the javascript file if it exists. Otherwise it returns nil. So following Pencilcheck's comment you can add this method for a conditional include:
<%= javascript_include_tag(controller_name) if javascript_asset_path(controller_name) %>
And now you have a proper conditional include. Now for the issue of precompiled assets. Generally for optimization you don't want assets precompiled individually. You can however do it if you must:
# Live Compilation
config.assets.compile = true
You can add this do your environment config file. Test it in your development environment file first. Again this is ill-advisable. The Rails asset pipeline uses Sprockets to optimize everything:
Sprockets loads the files specified, processes them if necessary,
concatenates them into one single file and then compresses them (if
Rails.application.config.assets.compress is true). By serving one file
rather than many, the load time of pages can be greatly reduced
because the browser makes fewer requests. Compression also reduces
file size, enabling the browser to download them faster.
PLEASE READ the documentation for further details of the mechanics of Sprockets (Asset Pipeline) http://guides.rubyonrails.org/asset_pipeline.html
Assets aren't precompiled individually. For example when I try:
<%= javascript_include_tag 'event' %>
I get:
Sprockets::Rails::Helper::AssetFilteredError: Asset filtered out and
will not be served: add Rails.application.config.assets.precompile +=
%w( event.js ) to config/initializers/assets.rb and restart your
server
So you can include which assets to be precompiled individually. We just need to add the relevant controller named javascript files in our asset initializer. Well we can do this programatically.
To get a list of controller names I will use ecoologic's example:
all_controllers = Dir[
Rails.root.join('app/controllers/*_controller.rb')
].map { |path|
path.match(/(\w+)_controller.rb/); $1
}.compact
And now to get the name of all javascript files that match the basename of the controller name you can use the following:
javascripts_of_controllers = Sprockets::Rails::Helper.assets.paths.select{|a_path|
a_path =~ /javascript/ and a_path =~ /#{Rails.root}/
}.map {|a_path|
Dir.entries(a_path)
}.flatten.delete_if {|the_file|
!the_file['.js']
}.collect {|the_file|
the_file if all_controllers.any? {|a_controller| the_file[a_controller]}
}
Then you can try:
# config/initializers/assets.rb
Rails.application.config.assets.precompile += javascripts_of_controllers
This will get you a list of all javascript files, without directory path, that match your controller name. Note if your controller name is plural, the javascript name should be as well. Also note if the controller is singular and the javascript file is plural this will still include it because of the_file[a_controller] will succeed on a partial match.
Feel free to try this out in your Rails.application.config.assets.precompile setting. I know that this gets you the list of files correctly. But I'll leave you to test it. Let me know if there are any nuances involved with precompiling this way as I am curious.
For a very thorough explanation on how assets precompile see this blog: http://www.sitepoint.com/asset-precompile-works-part/
I recently found a simple approach to use generated scripts for specific controller. I use for that solution gem gon. Add in a controller:
class HomesController < ApplicationController
before_filter :remember_controller
private
def remember_controller
gon.controller = params[:controller]
end
end
After that open your homes.js.cofee and add in the beginning of file:
jQuery ->
if gon.controller == "sermons"
# Place all functions here...
That is all.

How can I include an arbitrary file into a HAML template?

I want to have independent .markdown files that I then include in my haml templates. So I want to somehow include -- not render -- an external file into the template. I want the parent file to have :markdown in it, with the inclusion directly below that, and then the .markdown file to just be pure markdown.
Or: Is there a way to just use markdown as a rails template language (same way i can write templates or partials in erb or haml and rails just figures it out)?
This is similar to your solution, but using the :markdown filter. Haml does string interpolation on any filtered text, so you can read the markdown file like this.
:markdown
#{File.read(File.join(File.dirname(__FILE__), "foo.markdown"))}
You could put this into a helper, but you'd have to be careful with the file paths.
The simplest way I could think of is to create a custom template handler for Markdown. That you get to use Markdown code as partials (also getting support for locals for free).
module Markdown
class Template < ActionView::Template::Handler
include ActionView::Template::Handlers::Compilable
self.default_format = Mime::HTML
def compile(template)
'"' + Maruku.new(template.source).to_html + '".html_safe'
end
end
end
And then register it with markdown extension (in application.rb or custom initializer):
ActionView::Template.register_template_handler(:md, Markdown::Template)
And then user render like you would for any partial :)
# for file foo.md
= render 'foo'
Here's the best I can come up with (no haml filter involved at all):
=raw Maruku.new(File.read(File.dirname(__FILE__)+'/foo.markdown')).to_html
This is something I asked the HAML developers a while back. I suggested we needed an :include filter for HAML. Their response was we should load the file into a variable and then use the variable like we would any other.
Extending ActionView::Template::Handler is deprecated in at least Rails 3.1.0. Instead the following worked for me:
In lib/markdown_views.rb:
require "rdiscount"
class MarkdownViews
def call template
'md = ERB.new(<<\'EOF\'%s
EOF
).result( binding)
RDiscount.new( md).to_html.html_safe'% template.source
end
end
In config/application.rb:
require "markdown_views"
ActionView::Template.register_template_handler :markdown, MarkdownViews.new
In views/public/home.html.markdown:
# H1
+ Bullets.
+ screaming.
+ from out of nowhere
<%= "Embedded Ruby" %>

modify erb file on deployment

I have a fragment of JavaScript that I want to add to a page, but only in the production environment. Does rails have a way to insert or conditionally include on deployment. I know I could do "if Rails.env.production?" But I'd rather not do this condition check every time the page is loaded.
I wouldn't be worried about the overhead of one if statement.
Why not use a custom helper method:
def snippet
if RAILS_ENV == "production"
javascript_tag "whatever"
elsif . . .
end
then you can use the same syntax:
<%= snippet %>
and you get a couple benefits:
access to other rails helpers
your config file won't be littered with raw html
What I do in this situation is create a constant in each environment's config file:
#config/environments/development.rb
SNIPPET = ""
#config/environments/production.rb
SNIPPET = "<script src='whatever.js'></script>"
#app/views/file.html.erb
<%= SNIPPET %>

Resources