I'm upgrading a Rails 3 app to 3.2 and setting up the asset pipeline. It's great for css/js but I don't really see the point in using it for images, and unfortunately I have css with a ton of references /images/*.png and the like.
Is there a way to disable the asset pipeline just for images so image_tag("x.png") will go back to returning <img src="/images/x.png"> instead of <img src="/assets/x.png">? Thanks!
You can monkey-patch ActionView::Base, try this in rails console:
helper.image_path "foo" #=> "/assets/foo"
module OldImagePath
def image_path(source)
asset_paths.compute_public_path(source, 'images')
end
alias_method :path_to_image, :image_path
end
ActionView::Base.send :include, OldImagePath
helper.image_path "foo" #=> "/images/foo"
You can place this in an initializer for example. By default ActionView::Base includes ActionView::Helpers::AssetTagHelper and Sprockets::Helpers::RailsHelper which both define image_path but the latter take precedence. I'm including my own module which take precedence over all of them (the code inside is taken from ActionView::Helpers::AssetTagHelper).
Although, it makes sense to use asset pipeline for images too. They get hash sum in their filenames so that they can be cached forever on the client side without asking the server whether the file was changed.
Have a look at this gem:
https://github.com/spohlenz/digestion, it should do what you need :).
Otherwise you can move the assets you don't want included in the asset pipeline from app/assets back into public (e.g. public/images). Everything should work as you expect without the need for a gem.
Related
I'm trying to move my full-stack rails app over to Angular a page at a time. I'm using ui-router (https://github.com/angular-ui/ui-router) and angular-rails-templates (https://github.com/pitr/angular-rails-templates). I assumed the nghaml extension would allow me to continue to use rails helpers such as link_to, paths, etc. in my haml so just copied and pasted my haml page into the template; in an ideal world I would now be at a point where one page was being served client-side and every other page (including the ones it's linked to) were still being served server-side. Instead, I'm getting errors such as:
undefined local variable or method `dashboard_patients_path' for #<Object:0x007fc87865cff0>
and link_to, etc.
I thought this (angularjs with client side haml) would be a solid solution, specifically sharpper's response since it seemed directly applicable.
module CustomHamlEngine
class HamlTemplate < Tilt::HamlTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Rails.application.assets.register_engine '.haml', CustomHamlEngine::HamlTemplate
However, even after restarting the server, no dice.
Thoughts?
Got stuck with the same problem and found a solution after investigating angular-rails-templates, attentively reading their documentation and using the solution proposed by sharpper in angularjs with client side haml.
angular-rails-templates needs to recreate a mimeless version of the haml engine. So they extend the classes that are registered with Tilt instead of using the engines that were added to the asset pipelines. Therefore the new CustomHamlEngine that we create and register with the asset pipeline is never used by angular-rails-template. What we need to do instead is to register the engine with Tilt.
Create a file called angular_rails_templates.rb in the config/initializers folder and put this code in it.
# config/initializers/angular_rails_templates.rb
module CustomHamlEngine
class HamlTemplate < Tilt::HamlTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Tilt.register CustomHamlEngine::HamlTemplate, '.haml'
That will override the usual .haml engine with the one we just created. Angular-rails-templates will then process your haml file and it will support the rails helpers as well as the path helpers.
Don't forget to restart the server after including the file.
I've got somefile.js.coffee.erb file which is processed by Rails asset pipeline. My ERB code returns some string that cannot be parsed by Coffee which result in SyntaxError exception. I would like to peek into generated somefile.js.coffee file, or in general any intermediary file processed by asset pipeline.
I've tried to examine Sprockets with no luck:
environment = Sprockets::Environment.new
MyApplication::Application.config.assets.paths.each {|p| environment.append_path p}
rerb = environment['somefile.js.coffee.erb']
rerb.source #=> it's already preprocessed
Or to look into \tmp\cache\assets but there are also only preprocessed files, additionaly obscured by fingerprinted name.
Maybe there is a way to hook into asset-pipeline I have no idea how..
Why I need ERB? To generate client-side-model stubs with fields and validations matching Rails model using KnockoutJS (https://github.com/dnagir/knockout-rails extended -> https://github.com/KrzysztofMadejski/knockout-rails).
I am using Rails '~> 3.2.12', sprockets (2.2.2).
Edit: I've ended up injecting erb code in ### comments, to sneak-peak at generated code while coffeescript file is still compiling:
###
<%= somefun() %>
###
Altough I would suggest using #Semyon Perepelitsa answer as it produces coffee script file as it is seen by coffee compiler.
Just remove "coffee" from the file extension temporarily: somefile.js.erb. You will see its intermediate state at /assets/somefile.js as it won't be processed by CoffeeScript.
I wonder if you can put <% binding.pry %> just before the line and mess around till you get it right. Never tried during a compile and don't use coffeescript. In theory, it should work (or is worth a shot) so long as you put gem pry in your Gemfile and run bundle first.
I need to be using double quotes on my attributes due to a Javascript framework issue I am having, and I have tried setting the Haml::Template.options hash as the documentation recommends when using Rails, however isn't taking affect for the 'assets' folder, regardless of where I am setting the option. Note, it is working on normal ActionView templates being rendered by Rails controllers, but not in templates I have under {Rails.root}/app/assets/javascripts/templates/*.html.haml
This is what I have in {Rails.root}/config/initializers/haml.rb:
Haml::Template.options[:attr_wrapper] = '"'
# Adds the ability to use HAML templates in the asset pipeline for use with
# Batman.js partials
Rails.application.assets.register_mime_type 'text/html', '.html'
Rails.application.assets.register_engine '.haml', Tilt::HamlTemplate
I've also tried changing the register_engine to use Haml::Engine and Haml::Template, which both render, but still don't take my options I set above.
How do I set the Haml options for rendering in the asset pipeline? It seems like I need to pass in options for the Sprocket engine?
I found the solution here.
It's all about monkey patching.
I prefer this variation, as it keeps the options already set for Haml::Template.
Put this at the end of your haml initializer.
class Tilt::HamlTemplate
def prepare
options = #options.merge(:filename => eval_file, :line => line)
# Use same options as Haml::Template
options = options.merge Haml::Template.options
#engine = ::Haml::Engine.new(data, options)
end
end
Is there a way to always run the ERB preprocessor on a Javascript file?
I'm using Mustache to use the same templates on the client and server. I'd like to include these templates in my application.js files so they're available on the client. So I'm preprocessing my Javascript file (templates.js.erb, which then gets required in application.js) with erb:
App.templates.productShow = <%= MustacheController.read("product/show").to_json %>;
This works great but when I edit the "product/show.html.mustache" template I need to also edit "templates.js.erb" so Rails knows to recompile this file which then picks up the latest changes from the mustache template.
There's no issue running this in production since the assets get compiled when I deploy, but it's annoying when developing. Ideally I could set the preprocessor to run on "templates.js.erb" every time I reload. My current solution is to inline the Javascript in the application layout but it would be nice to keep it separate.
I ended up writing a guardfile for this that adds a timestamp to the end of the file. Just touching the file is enough for sprockets to recompile but if you're using git you need to actually alter the file. Otherwise anyone else who pulls in the code won't get the latest preprocessed files. Here's the code...
#!/usr/bin/ruby
guard 'mustache' do
watch(%r{app/templates/.+\.mustache})
end
require 'guard/guard'
module ::Guard
class Mustache < ::Guard::Guard
def run_on_change(paths)
# file to be updated when any mustache template is updated
file_name = "./app/assets/javascripts/templates.js.erb"
# get array of lines in file
lines = File.readlines(file_name)
# if last line is a comment delete
lines.delete_at(-1) if lines[-1].match /\/\//
# add timestamp
lines << "// #{Time.now}"
# rewrite file
File.open(file_name, "w") do |f|
lines.each{|line| f.puts(line)}
end
end
end
end
This seems to be a common complaint with the pipeline - that you have to touch a file that references variables for those changes to be reflected in development mode. A few people have asked similar questions, and I do not think there is a way around it.
Forcing Sprockets to recompile for every single request is not really a viable solution because it takes so long to do the compilation.
Maybe you could set up guard to watch your mustache directory and recompile templates.js.erb when you make changes to it. Similar to how guard-bundler watches your Gemfile and rebundles on change.
I am working on a rails app that has a bunch (hundreds) of images that are hosted on an S3 server. To have helpers like image_tag point here I had to add this to by config/environments/development.rb test.rb and production.rb:
config.action_controller.asset_host = "http://mybucket.s3.amazonaws.com"
However, this also means that it looks there for CSS and Javascript. This is a huge pain because each time I change the CSS I have to re-upload it to Amazon.
So.. Is there an easy way I can make my app look to Amazon for images, but locally for CSS/Javascript?
(I'm using Rails 3.0)
You can pass a Proc object to config.action_controller.asset_host and have it determine the result programmatically at runtime.
config.action_controller.asset_host = Proc.new do |source|
case source
when /^\/(images|videos|audios)/
"http://mybucket.s3.amazonaws.com"
else
"http://mydomain.com"
end
end
but left as it is, this would give you http://mybucket.s3.amazonaws.com/images/whatever.png when you use image_tag :whatever.
If you want to modify the path as well, you can do something very similar with config.action_controller.asset_path
config.action_controller.asset_path = Proc.new do |path|
path.sub /^\/(images|videos|audios)/, ""
end
which would give you http://mybucket.s3.amazonaws.com/whatever.png combined with the former.
There's nothing stopping you from passing full url to image_tag: image_tag("#{IMAGE_ROOT}/icon.png").
But to me moving static images (icons, backgrounds, etc) to S3 and leaving stylesheets/js files on rails sounds kinda inconsistent. You could either move them all to S3 or setup Apache for caching (if you're afraid users pulling big images will create too much overhead for Rails).
BTW, you don't have to put config.action_controller... into config files for all three environments: placing that line just in config/environment.rb will have the same effect.