{
  "$type": "site.standard.document",
  "description": "If you’ve read the Rails 3.1 asset pipeline docs, you’re probably aware that you can add preprocessors to your asset files by appending extra file extensions. For example, to write your JS files in CoffeeScript you need to add the suffix .coffee, and if you also want to pass something from Rails to those files, like paths to image files, you also need to add the .erb suffix. All the extensions are added together, so you end up with e.g. profile.js.coffee.erb (it’s simpler with stylesheets, because by adding a Sass preprocessor you get a bunch of asset path helpers for free).\n\nWhat the docs don’t tell you is that Sprockets can also be configured to include preprocessors implicitly based on a content type.\nWriting a preprocessor\n\nFor example, let’s say you want to automatically replace every occurrence of a string HOSTNAME in your JavaScript with the actual domain name of your site. To do that you need to create a preprocessor class with a correct interface that the content will pass through:\n\nclass RootUrlPreprocessor < Sprockets::Processor\n  def evaluate(context, locals)\n    data.gsub(%r\"\\bHOSTNAME\\b\", \"myserver.com\")\n  end\nend\n\nThe evaluate method gets two arguments, context and locals. As far as I can tell, locals is always an empty hash, so it’s probably useless here. The context is an instance of Sprockets::Context; you can use it to e.g. read the current file’s path or content type, generate paths to asset files or access asset pipeline configuration. You also have access to several asset helpers from ActionView, like javascript_include_tag, image_tag etc.\n\nThe most important thing is the data method - it returns the contents of the asset file that you get on the input. The job of the evaluate method is to process these contents the way you want and return the processed text.\n\nWhen you have that ready, you just need to tell Sprockets to apply the preprocessor to all JavaScript files (this will also include JavaScript template files, if you have any):\n\n# in config/initializers/sprockets.rb\nRails.application.assets.register_preprocessor('application/javascript',\n                                               RootUrlPreprocessor)\n\nNow when you start the server and open a page, this code:\n\nvar socket = new WebSocket(\"ws://HOSTNAME/feed\");\n\nWill be coverted on the fly to:\n\nvar socket = new WebSocket(\"ws://myserver.com/feed\");\n\nYou don’t need to add any extra extensions or anything to trigger the preprocessor - all preprocessors registered for a given MIME type will be used automatically.\n\nOne thing that might trip you up here is the asset caching feature - Rails won’t realize that after a change in the preprocessor it needs to regenerate any relevant asset files, so it will keep using the ones it has in cache. You’ll need to delete the tmp/cache/assets directory in which the cache is stored and restart the server (or alternatively, making any change in the JS file and reloading it in the browser should have the same effect).\n\nAdding an asset pat…",
  "path": "/2012/05/06/extending-asset-pipeline-with-custom-preprocessors/",
  "publishedAt": "2012-05-06T19:49:00Z",
  "site": "at://did:plc:oio4hkxaop4ao4wz2pp3f4cr/site.standard.publication/3mn5mackuba26",
  "tags": [
    "Ruby/Rails",
    "JavaScript"
  ],
  "textContent": "If you’ve read the Rails 3.1 asset pipeline docs, you’re probably aware that you can add preprocessors to your asset files by appending extra file extensions. For example, to write your JS files in CoffeeScript you need to add the suffix .coffee, and if you also want to pass something from Rails to those files, like paths to image files, you also need to add the .erb suffix. All the extensions are added together, so you end up with e.g. profile.js.coffee.erb (it’s simpler with stylesheets, because by adding a Sass preprocessor you get a bunch of asset path helpers for free).\n\nWhat the docs don’t tell you is that Sprockets can also be configured to include preprocessors implicitly based on a content type.\nWriting a preprocessor\n\nFor example, let’s say you want to automatically replace every occurrence of a string HOSTNAME in your JavaScript with the actual domain name of your site. To do that you need to create a preprocessor class with a correct interface that the content will pass through:\n\nclass RootUrlPreprocessor < Sprockets::Processor\n  def evaluate(context, locals)\n    data.gsub(%r\"\\bHOSTNAME\\b\", \"myserver.com\")\n  end\nend\n\nThe evaluate method gets two arguments, context and locals. As far as I can tell, locals is always an empty hash, so it’s probably useless here. The context is an instance of Sprockets::Context; you can use it to e.g. read the current file’s path or content type, generate paths to asset files or access asset pipeline configuration. You also have access to several asset helpers from ActionView, like javascript_include_tag, image_tag etc.\n\nThe most important thing is the data method - it returns the contents of the asset file that you get on the input. The job of the evaluate method is to process these contents the way you want and return the processed text.\n\nWhen you have that ready, you just need to tell Sprockets to apply the preprocessor to all JavaScript files (this will also include JavaScript template files, if you have any):\n\n# in config/initializers/sprockets.rb\nRails.application.assets.register_preprocessor('application/javascript',\n                                               RootUrlPreprocessor)\n\nNow when you start the server and open a page, this code:\n\nvar socket = new WebSocket(\"ws://HOSTNAME/feed\");\n\nWill be coverted on the fly to:\n\nvar socket = new WebSocket(\"ws://myserver.com/feed\");\n\nYou don’t need to add any extra extensions or anything to trigger the preprocessor - all preprocessors registered for a given MIME type will be used automatically.\n\nOne thing that might trip you up here is the asset caching feature - Rails won’t realize that after a change in the preprocessor it needs to regenerate any relevant asset files, so it will keep using the ones it has in cache. You’ll need to delete the tmp/cache/assets directory in which the cache is stored and restart the server (or alternatively, making any change in the JS file and reloading it in the browser should have the same effect).\n\nAdding an asset path helper\n\nI know, putting the hostname in JavaScript files isn’t very useful. Want something more useful? How about an image-path helper like the one you have in your .sass files? Simple, just make a preprocessor that will replace any occurrence of e.g. $imagePath('...') with the asset path:\n\nclass AssetPathPreprocessor < Sprockets::Processor\n  def evaluate(context, locals)\n    data.gsub(%r\"\\$imagePath\\('(.*?)'\\)\", \"'\" + context.image_path($1) + \"'\")\n  end\nend\n\nRegister the new preprocessor in Sprockets for application/javascript files as in the previous example. Now you can write:\n\n$('.start').mouseover ->\n  $(this).attr 'src', $imagePath('buttons/start-active.png')\n\nAnd your final JS will look like this:\n\n$('.start').mouseover(function() {\n  return $(this).attr('src', '/assets/buttons/start-active.png');\n});\n\nOf course in production mode you will get something like /assets/buttons/start-active-9934e5b1b3b2b184c5128b9987406285.png instead. And like in the previous example, the file you use it in may be called profile.js.coffee (or just profile.js if you don’t use CoffeeScript), you don’t need to add .erb - I got used to two extensions in one filename, but three is kind of too much for my taste…",
  "title": "Extending asset pipeline with custom preprocessors",
  "updatedAt": "2025-06-30T01:49:16Z"
}