Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails static html template files in the asset pipeline and caching in development mode

I'm building a website using AngularJS and Rails. The HTML files that I'm using for templates are stored under /app/assets/templates and each time I update a route or change something inside of a nested partial inside of a template I need to "touch" the highest level file in the /app/assets/templates directory for the html file I'm changing.

So if I have a page "edit.html" which loads a partial "_form.html" then whenever I update a route or change something in _form.html I need to make sure that edit.html is touched.

This is annoying and very finicky. Is there any way to inform the asset pipeline/sprockets to avoid caching for the app/assets/templates directory?

like image 257
matsko Avatar asked Aug 24 '12 20:08

matsko


5 Answers

You can try gem js_assets (https://github.com/kavkaz/js_assets).

This allows you to function asset_path in javascript code that emulates a similar method of sprockets.

like image 80
kavkaz Avatar answered Oct 03 '22 15:10

kavkaz


The best solution I've found to this is not to use the asset pipeline for HTML template files.

Instead make a controller called TemplatesController and create only one action. Then map all template URLs to that using a route such as:

get /templates/:path.html => 'templates#page', :constraints => { :path => /.+/  }

Then move all the template files into app/views/templates

Then inside the controller, setup the following:

caches_page :page

def page
  @path = params[:path]
  render :template => 'templates/' + @path, :layout => nil
end

This way all of your template files will be served from the controller and then will be cached into public/templates. To avoid cache problems, you can create a timestamp path into the template route so that your cached files are delivered with a version:

get '/templates/:timestamp/:path.html' => 'templates#page', :constraints => { :path => /.+/ }

This way you can have a new timestamp each time you upload the website and you can store the templates folder anywhere you like. You can even store the templates folder on S3 and have an assets URL for that. Then wherever your template files are addressed, you can use a custom asset method:

templateUrl : <%= custom_asset_template_url('some/file.html') %>

Where:

def custom_asset_template_url(path)
  "http://custom-asset-server.website.com/templates/#{$some_global_timestamp}/#{path}"
end

Then just make the asset redirect to the Rails server if it's not found and it will be generated. Or all template files can be pre-generated once uploaded.

like image 41
matsko Avatar answered Oct 03 '22 16:10

matsko


There's a much (much!) better way to deal with this.

<%= path_to_asset("template_name.html") %>

That will return a fully working file from the asset pipeline, which can use ERB, etc. It's undocumented, but it's a part of sprockets / the asset pipeline.

like image 44
RandallB Avatar answered Oct 03 '22 16:10

RandallB


To my mind, several things are needed here:

  1. The templates should be namespaced, so people templates go in the app/views/people/templates directory
  2. The templates are entirely static, so no before filters should be called.
  3. The templates should be cached, making them very fast.

Here's a possible solution using a Rails concern:

# Allows static content to be served from the templates
# directory of a controller
module HasTemplates

  extend ActiveSupport::Concern

  included do
    # Prepend the filter
    prepend_before_filter :template_filter, only: [:templates]
    # Let's cache the action
    caches_action :templates, :cache_path => Proc.new {|c| c.request.url }
  end

  # required to prevent route from baulking
  def templates;end

  # Catch all template requests and handle before any filters
  def template_filter
    render "/#{params[:controller]}/templates/#{params[:template]}", layout: 'blank'
    rescue ActionView::MissingTemplate
      not_found layout: 'blank'
    false
  end
end

Notice we are returning the template in a prepended filter. This allows us to return the static content without hitting any other filters.

You can then create a route, something like this:

resources :people do
  collection do
    get 'templates/:template' => 'people#templates', as: :templates
  end
end

Your controller becomes simply:

class PeopleController < ApplicationController
  include HasTemplates
end

Now any file placed in the /app/views/people/templates can be served at speed from a url.

like image 37
superluminary Avatar answered Oct 03 '22 15:10

superluminary


Expanding on RandallB's answer a bit; this is mentioned explicitly in the documentation on the Asset Pipeline: http://guides.rubyonrails.org/asset_pipeline.html

Note that you have to append the extension .erb to your .coffee file to have this work. (e.g., application.js.coffee.erb)

like image 29
dzuc Avatar answered Oct 03 '22 15:10

dzuc