how do I call the asset_pipeline for only 1 file? - ruby-on-rails

I'm trying to understand the asset_pipeline.
I basically have all my website-wide code in style.css.scss (which is #imported in application.css.scss)
I also have a few miscellaneous controllers that have code that I split off into a separate file only to be loaded on that page. For example, in my profiles_controller I have profiles.css.scss and profiles.js
in the documentation it says i can conditionally include those by typing:
<%= stylesheet_link_tag params[:controller] %>
and a similar one for javascript_include_tag
The problem is when i view source on any of my pages that dont have a controller stylesheet it is a 404 file not found. I dont want to have 10 blank files for each controller and 10 blank javascript files (those controllers have hardly any extra css or code, so i put it in the main scss file) ... am i doing this wrong?
Note:I have removed include tree from the JS and css files, as i dont want any file in that folder being included

In your application layout file, add yield statements like this to pull in the stylesheets you include as one-offs in different views:
= stylesheet_link_tag "application"
= yield :stylesheets
= javascript_include_tag "application"
= yield :javascripts
Then in each view file where you want to include stylesheets:
= content_for :stylesheets do
= stylesheet_link_tag "controller_name"
= content_for :javascripts do
= javascript_include_tag "controller_name"
This will include those files in only the views you need them in.

Related

using a different style for different views in rails

Rails 5.1
I mostly work on backend issues, but I am trying to learn more about front end stuff, and I am a little confused about how styles are handled in Rails (I read through a bunch of answers on stackolverflow, but that did not help narrow it down for me).
I have two different views
views/user/login.html.slim
views/user/new.html.slim
I would like to apply different body styles to each view. When the application was generated, Rails added:
assets/stylesheets/users.css.scss
Would I need to add a folder, called users and inside it, have different stylesheets? something like this:
assets/stylesheets/users/login.css.scss
would have in it:
body {
padding-top: 40px;
}
and
assets/stylesheets/users/new.css.scss
would have in it:
body {
padding-top: 80px;
}
Then, in
stylesheets/application.css.scss
I would add:
*= require_self
*= require_tree .
The styles and javascripts are loaded from your layout. Out of the box it looks like this:
app/views/layouts/application.html.erb
<%= stylesheet_link_tag 'application', media: 'all' %>
<%= javascript_include_tag 'application' %>
If you only want to include particular css files in a particular view, you would need to:
Create a layout for your particular view.
Include the appropriate styles and javascripts in the layout.
Specify the particular layout in your controller.
Here is some more information on layouts, but lets go through it for your login example.
app/controllers/sessions/create
class SessionsController < ApplicationController
layout 'login', only: [:new]
# you could also call the layout "sessions.html.erb" and it will be
# loaded automagically
If you want to use a different layout for different actions in the same controller, you can add other layout methods and specify the actions they apply to with the only action.
Then the layout
app/views/layouts/login.html.erb
...
<%= stylesheet_link_tag 'login', media: 'all' %>
...
And finally your stylesheet
app/assets/stylesheets/login.scss
#import "file_you_want";
If you require_tree . it will load all styles in the stylesheets directory which I don't think is what you are after.

Rails image broken link

I'm trying to link to an image in rails and its not working. I can link to assets/file.css as well as assets/file.js but assets/file.jpg doesnt work.
I tried a fresh rails new project_name and when i open the homepage and the rails logo doesnt show.
As a side question,
How do I get rails to load only the application.js and application.css
I see that if i remove ?body=1 from the URL it compresses them all into one file, but when I view the page using the following code it leaves those files blank and includes them one by one as separate scripts.
<%= stylesheet_link_tag "application", :media => "all" %>
<%= javascript_include_tag "application" %>
Ok so that's an expected behavior, you have to use Rails helpers:
<%= image_tag "file.jpg" %>
Your css files should be .erb. Within it, use:
.class { background-image: url(<%= asset_path 'image.png' %>) }
Reference is para 2.2.1 here.
I just checked my code and my CSS looks like this:
background-image:url(/assets/pinstripes.jpg);
with the image uploaded to app/assets/images, of course. This looks like what you're using, but I can't tell for sure.

how should I include a coffeescript file on only one page?

Edit: a year later if I was going to do this again I'd do it with curl.js instead of Rails asset pipeline.
Related: Best way to add page specific javascript in a Rails 3 app?
I'm writing an app and using coffeescript to generate all of the js. That's why the related question doesn't do what I need.
I'd like to be able to put a coffeescript file in a subfolder of my assets directory and have that .coffee file only get served up on one page. The page is on a named route
match 'myNotifications' => 'user#notifications'
The most obvious thing to do was to put the .coffee file in assets\javascripts\user\index.js.coffee. But after reading the docs about assets I'm unclear.
I read this line (from http://guides.rubyonrails.org/asset_pipeline.html):
You should put any JavaScript or CSS unique to a controller inside
their respective asset files, as these files can then be loaded just
for these controllers with lines such as <%= javascript_include_tag
params[:controller] %> or <%= stylesheet_link_tag params[:controller]
%>.
Ok cool, so I put the page specific js in assets\javascripts\user.js.coffee. Then I reloaded my home page, Ctrl F5. The user.js file is still being loaded on the homepage. Tested with $ -> alert 'ready from users controller'; seeing that alert when I load the homepage.
Does Rails have a way to have a per-page coffeescript file that will only be served up with that page? Am I reading the manual wrong? Is there a place in the assets folder that I can put .coffee files where they won't get loaded with every page?
Update: Looks like I might have an answer:
There are a couple of ways that we can get around this problem. We
could use require_directory instead of require_tree as this will only
load the files in the current directory and not in subdirectories. If
we want more control over the included files we can require them
separately instead of including the whole directory. Alternatively we
could move the JavaScript files that we want to be included on all
pages into a public subdirectory. We can then use require_tree
./public to include just those files.
I'll give that a shot in the AM.
Here's the approach I use to make controller/view specific Coffee:
application.html.haml:
%body{ :data => { :controller => params[:controller], :action => params[:action]} }
alt. application.html.erb
<%= content_tag(:body, :data => { :controller => params[:controller], :action => params[:action] }) do %>
...
<% end %>
application.js.coffee:
$(document).ready ->
load_javascript($("body").data('controller'),$("body").data('action'))
load_javascript = (controller,action) ->
$.event.trigger "#{controller}.load"
$.event.trigger "#{action}_#{controller}.load"
users.js.coffee
$(document).bind 'edit_users.load', (e,obj) =>
# fire on edit users controller action
$(document).bind 'show_users.load', (e,obj) =>
# fire on show users controller action
$(document).bind 'users.load', (e,obj) =>
# fire on all users controller actions
Sidenote:
This works great with PJAX as well as you can pass the controller/action names with the response header on PJAX requests and just fire these js functions based on that.
EDIT (2014/03/04):
This solution still works when using turbolinks.js.
Rather than only including the file on one page, you might want to just use logic that's conditional on the page markup. See my answer to a related question. That way, your users don't have to make an additional <script> request for the particular page.
If there's a lot of logic specific to that page (say, 10K+ minified), then yes, split it out. As you suggested in the edit to your question: Rather than doing require_tree . at the root of your javascripts directory, instead create a sub-directory called global and change the top of application.js from
require_tree .
to
require_tree global
Then put your page-specific CoffeeScript file in the root javascripts directory, and point to it with a javascript_include_tag call in that page's template.
include javascript tag into your view template, like show.html.haml
- content_for :javascripts do
= javascript_include_tag 'folder/coffee_file_name'

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.

rails javascript_include_tag

I have a few custom .js files in my public/javascripts folder. The 'javascript_include_tag' adds script tags to the head element. Are these executed in the order in which they appear. If yes, how do I control the order in which these script tags are added to my html.
thanks,
ash
Yes, they are executed in order of appearance, so you just write them in the order you want. If you are currently using javascript_include_tag :all then you can include individual scripts instead to get the order you want:
javascript_include_tag :defaults
javascript_include_tag "script_1"
javascript_include_tag "script_2"

Resources