virtual_asset_path 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source :rubygems
2
+
3
+ group :dev do
4
+ gem 'actionpack', ENV['RAILS']
5
+ gem 'redgreen'
6
+ gem 'rspec', '~>2'
7
+ gem 'rake'
8
+ gem 'jeweler'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,55 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionpack (3.0.8)
6
+ activemodel (= 3.0.8)
7
+ activesupport (= 3.0.8)
8
+ builder (~> 2.1.2)
9
+ erubis (~> 2.6.6)
10
+ i18n (~> 0.5.0)
11
+ rack (~> 1.2.1)
12
+ rack-mount (~> 0.6.14)
13
+ rack-test (~> 0.5.7)
14
+ tzinfo (~> 0.3.23)
15
+ activemodel (3.0.8)
16
+ activesupport (= 3.0.8)
17
+ builder (~> 2.1.2)
18
+ i18n (~> 0.5.0)
19
+ activesupport (3.0.8)
20
+ builder (2.1.2)
21
+ diff-lcs (1.1.2)
22
+ erubis (2.6.6)
23
+ abstract (>= 1.0.0)
24
+ git (1.2.5)
25
+ i18n (0.5.0)
26
+ jeweler (1.6.2)
27
+ bundler (~> 1.0)
28
+ git (>= 1.2.5)
29
+ rake
30
+ rack (1.2.3)
31
+ rack-mount (0.6.14)
32
+ rack (>= 1.0.0)
33
+ rack-test (0.5.7)
34
+ rack (>= 1.0)
35
+ rake (0.9.2)
36
+ redgreen (1.2.2)
37
+ rspec (2.6.0)
38
+ rspec-core (~> 2.6.0)
39
+ rspec-expectations (~> 2.6.0)
40
+ rspec-mocks (~> 2.6.0)
41
+ rspec-core (2.6.4)
42
+ rspec-expectations (2.6.0)
43
+ diff-lcs (~> 1.1.2)
44
+ rspec-mocks (2.6.0)
45
+ tzinfo (0.3.28)
46
+
47
+ PLATFORMS
48
+ ruby
49
+
50
+ DEPENDENCIES
51
+ actionpack
52
+ jeweler
53
+ rake
54
+ redgreen
55
+ rspec (~> 2)
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require 'rake/testtask'
2
+ Rake::TestTask.new(:default) do |test|
3
+ test.libs << 'lib'
4
+ test.pattern = 'test/**/*_test.rb'
5
+ test.verbose = true
6
+ end
7
+
8
+ task :all do
9
+ sh "RAILS=2.3.12 bundle && bundle exec rake"
10
+ sh "RAILS=3.0.8 bundle && bundle exec rake"
11
+ # sh "RAILS=3.1.0.rc4 bundle && bundle exec rake"
12
+ end
13
+
14
+ begin
15
+ require 'jeweler'
16
+ Jeweler::Tasks.new do |gem|
17
+ gem.name = 'virtual_asset_path'
18
+ gem.summary = "Instantly expired, cacheable assets, without query params"
19
+ gem.email = "michael@grosser.it"
20
+ gem.homepage = "http://github.com/grosser/#{gem.name}"
21
+ gem.authors = ["Michael Grosser"]
22
+ end
23
+
24
+ Jeweler::GemcutterTasks.new
25
+ rescue LoadError
26
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
27
+ end
data/Readme.md ADDED
@@ -0,0 +1,56 @@
1
+ Virtual asset paths for great caching.
2
+
3
+ - cacheable without query params
4
+ - cached by MD5 of content (only change when the content changes / same on all servers)
5
+
6
+
7
+ # Results
8
+
9
+ /foo/bar.jpg ... will be ...
10
+
11
+ # default + simple rewrite rules
12
+ /asset-v1afd23/foo/bar.jpg # style: folder method: MD5
13
+
14
+ # rails 3.1-ish, but complicated rewrite rules
15
+ /foo/bar-asset-v1afd23.jpg # style: suffix method: MD5
16
+
17
+ # no rewrite rule needed (great for development)
18
+ /foo/bar.jpg?1afd23 # style: query method: MD5
19
+
20
+ # different methods ...
21
+ /asset-v1308942465/foo/bar.jpg # style: folder method: mtime
22
+ /asset-vxxx/foo/bar.jpg # style: folder method: ENV['RAILS_ASSET_ID'] = 'xxx'
23
+
24
+ # configure
25
+ VirtualAssetPath.style = :folder # default
26
+ VirtualAssetPath.style = :MD5 # default
27
+
28
+ # Install
29
+ (only tested on rails 2 atm)
30
+
31
+ ./script/plugin install git://github.com/grosser/virtual_asset_path.git
32
+ Or
33
+ gem install virtual_asset_path
34
+
35
+ # Nginx config
36
+
37
+ rewrite ^/asset-v[a-z0-9]+(.*) $1; # style: folder
38
+ ??? # style: suffix
39
+
40
+ # Apache config
41
+
42
+ ??? # style: folder
43
+ ??? # style: suffix
44
+
45
+ # Tips
46
+ - [Speed] MD5 takes ~0.1 ms/file (results are cached per instance if cache_asset_timestamps is active)
47
+ - [Speed] mtime takes ~0.005 ms/file (...)
48
+
49
+ # TODO
50
+ - Rails 3.1 support -- things got much more complicated ...
51
+
52
+ Author
53
+ ======
54
+ [Michael Grosser](http://grosser.it)<br/>
55
+ michael@grosser.it<br/>
56
+ Hereby placed under public domain, do what you want, just do not hold me accountable...
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'virtual_asset_path'
@@ -0,0 +1,59 @@
1
+ module VirtualAssetPath
2
+ VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
3
+
4
+ class << self
5
+ attr_accessor :method, :style
6
+ end
7
+ end
8
+
9
+ module ActionView::Helpers::AssetTagHelper
10
+ def rewrite_asset_path_with_virtual_folder(*args)
11
+ asset_path = rewrite_asset_path_without_virtual_folder(*args)
12
+ id = virtual_asset_folder(asset_path)
13
+ if id.present?
14
+ case VirtualAssetPath.style
15
+ when :folder, nil then "/asset-v#{id}#{asset_path}"
16
+ when :query then "#{asset_path}?#{id}"
17
+ end
18
+ else
19
+ asset_path
20
+ end
21
+ end
22
+ alias_method_chain :rewrite_asset_path, :virtual_folder
23
+
24
+ def virtual_asset_folder(source)
25
+ if asset_id = ENV["RAILS_ASSET_ID"]
26
+ asset_id
27
+ else
28
+ if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
29
+ asset_id
30
+ else
31
+ path = File.join(ASSETS_DIR, source)
32
+ asset_id = File.exist?(path) ? virtual_asset_folder_id(path) : ''
33
+
34
+ if @@cache_asset_timestamps
35
+ @@asset_timestamps_cache_guard.synchronize do
36
+ @@asset_timestamps_cache[source] = asset_id
37
+ end
38
+ end
39
+
40
+ asset_id
41
+ end
42
+ end
43
+ end
44
+
45
+ # overwrite to disable / use mtime ?
46
+ def virtual_asset_folder_id(path)
47
+ case VirtualAssetPath.method
48
+ when :md5,nil then Digest::MD5.file(path).hexdigest[0..6]
49
+ when :mtime then File.mtime(path).to_i.to_s
50
+ else
51
+ raise "unsupported hashing method"
52
+ end
53
+ end
54
+
55
+ # no longer needed <-> would conflict
56
+ def rails_asset_id(*args)
57
+ nil
58
+ end
59
+ end
File without changes
@@ -0,0 +1,76 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'active_support/all'
4
+ require 'action_pack'
5
+ require 'action_controller'
6
+ require 'test/unit'
7
+ require 'redgreen'
8
+ $LOAD_PATH << 'lib'
9
+ require 'init'
10
+
11
+ class ActionView::Base
12
+ def debug(*args)
13
+ args.inspect
14
+ end
15
+
16
+ include ActionView::Helpers::AssetTagHelper
17
+ include ActionView::Helpers::TagHelper
18
+ end
19
+
20
+ silence_warnings do
21
+ ActionView::Helpers::AssetTagHelper::ASSETS_DIR = 'test/public'
22
+ end
23
+
24
+ if ActionPack::VERSION::MAJOR > 2
25
+ require 'action_dispatch/testing/test_process'
26
+
27
+ # fake checks
28
+ require 'ostruct'
29
+ module ActionController::UrlFor
30
+ def _routes
31
+ helpers = OpenStruct.new
32
+ helpers.url_helpers = Module.new
33
+ helpers
34
+ end
35
+ end
36
+
37
+ ROUTES = ActionDispatch::Routing::RouteSet.new
38
+ ROUTES.draw do
39
+ match ':controller(/:action(/:id(.:format)))'
40
+ end
41
+ ROUTES.finalize!
42
+
43
+ # funky patch to get @routes working, in 'setup' did not work
44
+ module ActionController::TestCase::Behavior
45
+ def process_with_routes(*args)
46
+ @routes = ROUTES
47
+ process_without_routes(*args)
48
+ end
49
+ alias_method_chain :process, :routes
50
+ end
51
+
52
+ class ActionController::Base
53
+ self.view_paths = 'test/views'
54
+
55
+ def self._routes
56
+ ROUTES
57
+ end
58
+
59
+ def url_for(*args)
60
+ 'xxx'
61
+ end
62
+ end
63
+ else
64
+ require 'action_controller/test_process'
65
+
66
+ ActionController::Routing::Routes.reload rescue nil
67
+ ActionController::Base.cache_store = :memory_store
68
+
69
+ class ActionController::Base
70
+ before_filter :set_view_paths
71
+
72
+ def set_view_paths
73
+ @template.view_paths = 'test/views'
74
+ end
75
+ end
76
+ end
@@ -0,0 +1 @@
1
+ <%= image_tag params[:image], :alt => '' %>
@@ -0,0 +1,61 @@
1
+ require 'test/test_helper'
2
+
3
+ class VirtualAssetPathTestController < ActionController::Base
4
+ def index
5
+ end
6
+ end
7
+
8
+ #noinspection RubyArgCount
9
+ class VirtualAssetPathTest < ActionController::TestCase
10
+ def setup
11
+ VirtualAssetPath.method = nil
12
+ VirtualAssetPath.style = nil
13
+ ENV["RAILS_ASSET_ID"] = nil
14
+ @controller = VirtualAssetPathTestController.new
15
+ ActionView::Base.send :class_variable_set, '@@asset_timestamps_cache', {}
16
+ ActionView::Base.send :class_variable_set, '@@cache_asset_timestamps', true
17
+ end
18
+
19
+ test "adds folder to found assets" do
20
+ get :index, :image => '/bar/foo.jpg'
21
+ @response.body.strip.should == "<img alt=\"\" src=\"/asset-vd41d8cd/bar/foo.jpg\" />"
22
+ end
23
+
24
+ test "does not add virtual path to unknown assets" do
25
+ get :index, :image => '/bar/not_found.jpg'
26
+ @response.body.strip.should == "<img alt=\"\" src=\"/bar/not_found.jpg\" />"
27
+ end
28
+
29
+ test "adds RAILS_ASSET_ID to found assets" do
30
+ ENV["RAILS_ASSET_ID"] = 'abc'
31
+ get :index, :image => '/bar/foo.jpg'
32
+ @response.body.strip.should == "<img alt=\"\" src=\"/asset-vabc/bar/foo.jpg\" />"
33
+ end
34
+
35
+ test "caches found asset ids" do
36
+ file = 'test/public/cached.jpg'
37
+ hash = '0cc175b'
38
+
39
+ begin
40
+ File.open(file,'w'){|f| f.write 'a' }
41
+ get :index, :image => '/cached.jpg'
42
+ @response.body.strip.should == "<img alt=\"\" src=\"/asset-v#{hash}/cached.jpg\" />"
43
+
44
+ File.open(file,'w'){|f| f.write 'b' }
45
+ get :index, :image => '/cached.jpg'
46
+ @response.body.strip.should == "<img alt=\"\" src=\"/asset-v#{hash}/cached.jpg\" />"
47
+ ensure
48
+ `rm #{file}`
49
+ end
50
+ end
51
+
52
+ test "caches with mtime" do
53
+ VirtualAssetPath.method = :mtime
54
+ get :index, :image => '/bar/foo.jpg'
55
+ @response.body.strip.should == "<img alt=\"\" src=\"/asset-v#{File.mtime('test/public/bar/foo.jpg').to_i}/bar/foo.jpg\" />"
56
+ end
57
+
58
+ test "has a version" do
59
+ VirtualAssetPath::VERSION.should =~ /^\d+\.\d+\.\d+$/
60
+ end
61
+ end
@@ -0,0 +1,42 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{virtual_asset_path}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Michael Grosser"]
12
+ s.date = %q{2011-06-25}
13
+ s.email = %q{michael@grosser.it}
14
+ s.files = [
15
+ "Gemfile",
16
+ "Gemfile.lock",
17
+ "Rakefile",
18
+ "Readme.md",
19
+ "VERSION",
20
+ "init.rb",
21
+ "lib/virtual_asset_path.rb",
22
+ "test/public/bar/foo.jpg",
23
+ "test/test_helper.rb",
24
+ "test/views/virtual_asset_path_test/index.erb",
25
+ "test/virtual_asset_path_test.rb",
26
+ "virtual_asset_path.gemspec"
27
+ ]
28
+ s.homepage = %q{http://github.com/grosser/virtual_asset_path}
29
+ s.require_paths = ["lib"]
30
+ s.rubygems_version = %q{1.6.2}
31
+ s.summary = %q{Instantly expired, cacheable assets, without query params}
32
+
33
+ if s.respond_to? :specification_version then
34
+ s.specification_version = 3
35
+
36
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
37
+ else
38
+ end
39
+ else
40
+ end
41
+ end
42
+
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: virtual_asset_path
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Michael Grosser
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-25 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description:
23
+ email: michael@grosser.it
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - Gemfile
32
+ - Gemfile.lock
33
+ - Rakefile
34
+ - Readme.md
35
+ - VERSION
36
+ - init.rb
37
+ - lib/virtual_asset_path.rb
38
+ - test/public/bar/foo.jpg
39
+ - test/test_helper.rb
40
+ - test/views/virtual_asset_path_test/index.erb
41
+ - test/virtual_asset_path_test.rb
42
+ - virtual_asset_path.gemspec
43
+ has_rdoc: true
44
+ homepage: http://github.com/grosser/virtual_asset_path
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.6.2
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Instantly expired, cacheable assets, without query params
77
+ test_files: []
78
+