Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
clemens committed Jun 9, 2009
0 parents commit 7e28511
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 0 deletions.
20 changes: 20 additions & 0 deletions MIT-LICENSE
@@ -0,0 +1,20 @@
Copyright (c) 2009 Clemens Kofler

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41 changes: 41 additions & 0 deletions README
@@ -0,0 +1,41 @@
ExpireOnRestart
===============

ExpireOnRestart is a simple Rails plugin that deletes a given set of files whenever the server starts up. The main use
for this is to get rid of cached assets (such as javascripts and stylesheets) more easily without having to clean them
up manually or via a Capistrano task whenever you deploy a new version of your app.

Example
=======

If you only want to expire cached javascripts and/or stylesheets, you don't have to do anything at all. Just use the
regular caching syntax and ExpireOnRestart will handle the rest for you.

javascript_include_tag 'a', 'couple', 'of', 'javascript', 'files', :cache => true
javascript_include_tag 'more', 'javascript', 'files', :cache => 'my_cache'

stylesheet_link_tag 'a', 'couple', 'of', 'stylesheet', 'files', :cache => true
stylesheet_link_tag 'more', 'stylesheet', 'files', :cache => 'my_cache'

All javascript and stylesheet caches that are defined this way are automatically deleted when the server (re)starts.

If you want to manually expire files (for example if you create dynamic javascript/CSS and cache it), you can use the
expire_on_restart method:

expire_on_restart 'my_file'
expire_on_restart 'mulitple', 'files'
expire_on_restart ['array', 'of', 'files]

All paths given to expire_on_restart are considered to be relative your application root aka Rails.root.

Note
====

If you want to regenerate your caches right when you start up your server, this plugin is not for you!

Feedback
========

Feel free to send me any feedback you might have and also feel free to fork away! ;-)

Copyright (c) 2009 Clemens Kofler <clemens@railway.at>, www.railway.at, released under the MIT license
23 changes: 23 additions & 0 deletions Rakefile
@@ -0,0 +1,23 @@
require 'rake'
require 'rake/testtask'
require 'rake/rdoctask'

desc 'Default: run unit tests.'
task :default => :test

desc 'Test the expire_on_restart plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end

desc 'Generate documentation for the expire_on_restart plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'ExpireOnRestart'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README')
rdoc.rdoc_files.include('lib/**/*.rb')
end
7 changes: 7 additions & 0 deletions init.rb
@@ -0,0 +1,7 @@
require 'expire_on_restart'

ActionController::Base.send(:include, ExpireOnRestart::ExpirationHelper)
ActionView::Base.send(:include, ExpireOnRestart::ExpirationHelper)
ActionView::Helpers::AssetTagHelper.send(:include, ExpireOnRestart::AssetCacheExpirationHelper)

ExpireOnRestart::RestartExpirator.instance.expire_marked_files
63 changes: 63 additions & 0 deletions lib/expire_on_restart.rb
@@ -0,0 +1,63 @@
module ExpireOnRestart
LIST_FILE = File.join(Rails.root, 'tmp', '.expire_on_restart')

class RestartExpirator
include Singleton

def add(*files)
File.open(LIST_FILE, 'a+') { |f| f.write("\n" + Array(files).flatten.join("\n")) }
end

def expire_marked_files
return unless File.exist?(LIST_FILE) && File.readable?(LIST_FILE)

files_to_expire.each do |file|
path = File.join(Rails.root, file)
next unless File.exist?(path) && File.writable?(path)

File.delete(path)
end

File.delete(LIST_FILE)
end

def files_to_expire
File.new(LIST_FILE).readlines.collect { |f| f.gsub("\n", '') }.reject(&:blank?)
end
end

module ExpirationHelper
def expire_on_restart(*files)
RestartExpirator.instance.add(*files)
end
end

module AssetCacheExpirationHelper
include ExpirationHelper

def self.included(base)
base.alias_method_chain(:javascript_include_tag, :expiration_on_restart)
base.alias_method_chain(:stylesheet_link_tag, :expiration_on_restart)
end

def javascript_include_tag_with_expiration_on_restart(*sources)
options = sources.extract_options!.symbolize_keys
if options[:cache]
cache_file = File.join('public', 'javascripts', (options[:cache] == true ? 'all' : options[:cache].gsub('.js', '')) + '.js')
expire_on_restart(cache_file)
end

javascript_include_tag_without_expiration_on_restart(*(sources << options))
end

def stylesheet_link_tag_with_expiration_on_restart(*sources)
options = sources.extract_options!.symbolize_keys
if options[:cache]
cache_file = File.join('public', 'stylesheets', (options[:cache] == true ? 'all' : options[:cache].gsub('.css', '')) + '.css')
expire_on_restart(cache_file)
end

stylesheet_link_tag_without_expiration_on_restart(*(sources << options))
end
end
end
154 changes: 154 additions & 0 deletions test/expire_on_restart_test.rb
@@ -0,0 +1,154 @@
require 'test_helper'

class RestartExpiratorTest < ActiveSupport::TestCase
include ExpireOnRestart

def setup
path = File.join(Rails.root, 'tmp', '.expire_on_restart')
File.delete(path) if File.exist?(path)
@expirator = RestartExpirator.instance
end

test "is a singleton" do
assert RestartExpirator.included_modules.include?(Singleton)
end

test "#add creates a file named .expire_on_restart in tmp directory if none exists" do
assert !File.exist?(File.join(Rails.root, 'tmp', '.expire_on_restart'))
@expirator.add('some_file')
assert File.exist?(File.join(Rails.root, 'tmp', '.expire_on_restart'))
end

test "#add with a one file" do
@expirator.add('some_file')
assert @expirator.files_to_expire.include?('some_file')
end

test "#add with multiple files" do
@expirator.add('some_file', 'another_file')

assert @expirator.files_to_expire.include?('some_file')
assert @expirator.files_to_expire.include?('another_file')
end

test "#add with array" do
@expirator.add(['some_file', 'another_file'])

assert @expirator.files_to_expire.include?('some_file')
assert @expirator.files_to_expire.include?('another_file')
end

test "#add multiple times" do
@expirator.add('some_file')
@expirator.add('another_file')

assert @expirator.files_to_expire.include?('some_file')
assert @expirator.files_to_expire.include?('another_file')
end

test "#expire_marked_files deletes the marked files" do
File.open(File.join(Rails.root, 'tmp', '.expire_on_restart'), 'w') { |f| f.write("tmp/f0\ntmp/f1") }

path = File.join(Rails.root, 'tmp', 'f0')
File.new(File.join(Rails.root, 'tmp', 'f0'), 'w')
assert File.exist?(path)

path = File.join(Rails.root, 'tmp', 'f1')
File.new(File.join(Rails.root, 'tmp', 'f1'), 'w')
assert File.exist?(path)

@expirator.expire_marked_files
assert !File.exist?(File.join(Rails.root, 'tmp', 'f0'))
assert !File.exist?(File.join(Rails.root, 'tmp', 'f1'))
end

test "#expire_marked_files deletes the .expire_on_restart file" do
path = File.join(Rails.root, 'tmp', '.expire_on_restart')
File.open(path, 'w') { |f| f.write("tmp/f0\ntmp/f1") }
@expirator.expire_marked_files
assert !File.exist?(path)
end

test "#files_to_expire returns the files to expire" do
path = File.join(Rails.root, 'tmp', '.expire_on_restart')
File.open(path, 'w') { |f| f.write("tmp/f0\ntmp/f1") }
assert_equal ['tmp/f0', 'tmp/f1'], @expirator.files_to_expire
end
end

class ExpirationHelperTest < ActiveSupport::TestCase
include ExpireOnRestart::ExpirationHelper

def setup
path = File.join(Rails.root, 'tmp', '.expire_on_restart')
File.delete(path) if File.exist?(path)
@expirator = ExpireOnRestart::RestartExpirator.instance
end

test "#expire_on_restart appends a file to expire to the .expire_on_restart file" do
@expirator.expects(:add).with('some_file')
expire_on_restart('some_file')
end

test "#expire_on_restart with multiple files" do
@expirator.expects(:add).with('some_file', 'another_file')
expire_on_restart('some_file', 'another_file')
end
end

class ActionViewExtensionTest < ActionView::TestCase
include ActionView::Helpers::AssetTagHelper

def setup
path = File.join(Rails.root, 'tmp', '.expire_on_restart')
File.delete(path) if File.exist?(path)
@expirator = ExpireOnRestart::RestartExpirator.instance
end

test "#javascript_include_tag with :cache => true" do
javascript_include_tag(:all, :cache => true)
assert @expirator.files_to_expire.include?(File.join('public', 'javascripts', 'all.js'))
end

test "#javascript_include_tag with :cache => 'my_cache" do
javascript_include_tag(:all, :cache => 'my_cache')
assert @expirator.files_to_expire.include?(File.join('public', 'javascripts', 'my_cache.js'))
end

test "#stylesheet_link_tag with :cache => true" do
stylesheet_link_tag(:all, :cache => true)
assert @expirator.files_to_expire.include?(File.join('public', 'stylesheets', 'all.css'))
end

test "#stylesheet_link_tag with :cache => 'my_cache" do
stylesheet_link_tag(:all, :cache => 'my_cache')
assert @expirator.files_to_expire.include?(File.join('public', 'stylesheets', 'my_cache.css'))
end

test "#expire_on_restart with arbitrary files" do
expire_on_restart('file')
assert @expirator.files_to_expire.include?('file')

expire_on_restart('another_file', 'one_more_file')
assert @expirator.files_to_expire.include?('another_file')
assert @expirator.files_to_expire.include?('one_more_file')
end
end

class ActionControllerExtensionTest < ActiveSupport::TestCase
def setup
path = File.join(Rails.root, 'tmp', '.expire_on_restart')
File.delete(path) if File.exist?(path)
@expirator = ExpireOnRestart::RestartExpirator.instance
@controller = ApplicationController.new
end

test "#expire_on_restart with arbitrary files" do
@controller.expire_on_restart('file')
assert @expirator.files_to_expire.include?('file')

@controller.expire_on_restart('another_file', 'one_more_file')
assert @expirator.files_to_expire.include?('another_file')
assert @expirator.files_to_expire.include?('one_more_file')
end
end
4 changes: 4 additions & 0 deletions test/test_helper.rb
@@ -0,0 +1,4 @@
require 'rubygems'
require 'active_support'
require 'active_support/test_case'
require 'expire_on_restart'
1 change: 1 addition & 0 deletions uninstall.rb
@@ -0,0 +1 @@
File.delete(ExpireOnRestart::LIST_FILE) if File.exist?(ExpireOnRestart::LIST_FILE)

0 comments on commit 7e28511

Please sign in to comment.