asset-trip 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.gitignore +2 -0
  2. data/History.txt +6 -0
  3. data/MIT-LICENSE.txt +19 -0
  4. data/README.rdoc +27 -0
  5. data/Rakefile +12 -0
  6. data/Thorfile +110 -0
  7. data/asset-trip.gemspec +107 -0
  8. data/init.rb +3 -0
  9. data/lib/asset_trip/asset.rb +82 -0
  10. data/lib/asset_trip/compressor.rb +36 -0
  11. data/lib/asset_trip/config.rb +60 -0
  12. data/lib/asset_trip/file_writer.rb +17 -0
  13. data/lib/asset_trip/helper.rb +83 -0
  14. data/lib/asset_trip/javascript.rb +29 -0
  15. data/lib/asset_trip/load_path.rb +39 -0
  16. data/lib/asset_trip/manifest.rb +40 -0
  17. data/lib/asset_trip/manifest_writer.rb +35 -0
  18. data/lib/asset_trip/memoizable.rb +28 -0
  19. data/lib/asset_trip/middleware.rb +109 -0
  20. data/lib/asset_trip/ssl_stylesheet.rb +15 -0
  21. data/lib/asset_trip/stylesheet.rb +44 -0
  22. data/lib/asset_trip/url_rewriter.rb +82 -0
  23. data/lib/asset_trip.rb +66 -0
  24. data/spec/asset_trip/asset_spec.rb +47 -0
  25. data/spec/asset_trip/compressor_spec.rb +46 -0
  26. data/spec/asset_trip/config_spec.rb +61 -0
  27. data/spec/asset_trip/helper_spec.rb +221 -0
  28. data/spec/asset_trip/javascript_spec.rb +24 -0
  29. data/spec/asset_trip/load_path_spec.rb +61 -0
  30. data/spec/asset_trip/manifest_writer_spec.rb +32 -0
  31. data/spec/asset_trip/middleware_spec.rb +72 -0
  32. data/spec/asset_trip/stylesheet_spec.rb +51 -0
  33. data/spec/asset_trip/url_rewriter_spec.rb +166 -0
  34. data/spec/asset_trip_spec.rb +13 -0
  35. data/spec/fixtures/app/javascripts/main/new.js +1 -0
  36. data/spec/fixtures/app/javascripts/main.js +1 -0
  37. data/spec/fixtures/app/javascripts/signup.js +1 -0
  38. data/spec/fixtures/app/stylesheets/new.css +0 -0
  39. data/spec/fixtures/config/asset_trip/assets.rb +0 -0
  40. data/spec/integration/bundle_spec.rb +243 -0
  41. data/spec/integration/prune_spec.rb +53 -0
  42. data/spec/spec.opts +3 -0
  43. data/spec/spec_helper.rb +24 -0
  44. data/spec/support/core_extensions.rb +13 -0
  45. data/spec/support/helpers.rb +33 -0
  46. data/spec/support/matchers.rb +67 -0
  47. data/spec/support/path_utils.rb +45 -0
  48. data/spec/support/sandbox_helper.rb +19 -0
  49. data/tasks/asset_trip.rake +11 -0
  50. data/vendor/yuicompressor-2.4.2.jar +0 -0
  51. metadata +128 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ tmp
2
+ pkg
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ == 0.1.0 / 2009-11-05
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008-2009 Bryan Helmkamp
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,27 @@
1
+ = Asset Trip
2
+
3
+ Asset Trip bundles JavaScript and CSS files at deploy time. The assets are
4
+ then served from a Git-esque object store in the application's public
5
+ directory.
6
+
7
+ Development of Asset Trip was kindly sponsored by Weplay[http://www.weplay.com]
8
+
9
+ == Features and Benefits
10
+
11
+ * Reliable cache busting strategy, even during graceful deploys
12
+ * Develop in app/, deploy to public/
13
+ * Efficient compression of JavaScript and CSS from the YUI Compressor
14
+ * Fast at runtime. No bundling or File mtime checking slowing down your
15
+ production requests
16
+ * Simple, powerful all-Ruby configuration files
17
+
18
+ == Hat tips
19
+
20
+ * bundle_fu
21
+ * asset_packager
22
+ * bundler
23
+ * sprockets
24
+ * git
25
+ * asset-hosting-with-minimum-ssl
26
+ * http://carsonified.com/blog/features/webapps/serving-javascript-fast/
27
+
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ require "spec/rake/spectask"
2
+
3
+ Spec::Rake::SpecTask.new do |t|
4
+ t.warning = true
5
+ end
6
+
7
+ task :default => :spec
8
+
9
+ desc 'Removes trailing whitespace'
10
+ task :whitespace do
11
+ sh %{find . -name '*.rb' -exec sed -i '' 's/ *$//g' {} \\;}
12
+ end
data/Thorfile ADDED
@@ -0,0 +1,110 @@
1
+ module GemHelpers
2
+
3
+ def generate_gemspec
4
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "lib")))
5
+ require "asset_trip"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "asset-trip"
9
+ s.version = AssetTrip::VERSION
10
+ s.author = "Bryan Helmkamp"
11
+ s.email = "bryan@brynary.com"
12
+ s.homepage = "http://github.com/brynary/asset-trip"
13
+ s.summary = "Rails asset bundling plugin that will expand your mind"
14
+ s.description = <<-EOS.strip
15
+ Asset Trip bundles JavaScript and CSS files at deploy time. The assets are
16
+ then served from a Git-esque object store in the application's public
17
+ directory.
18
+ EOS
19
+
20
+ require "git"
21
+ repo = Git.open(".")
22
+
23
+ s.files = normalize_files(repo.ls_files.keys - repo.lib.ignored_files)
24
+ s.test_files = normalize_files(Dir['spec/**/*.rb'] - repo.lib.ignored_files)
25
+
26
+ s.has_rdoc = true
27
+ s.extra_rdoc_files = %w[History.txt README.rdoc MIT-LICENSE.txt]
28
+ end
29
+ end
30
+
31
+ def normalize_files(array)
32
+ # only keep files, no directories, and sort
33
+ array.select do |path|
34
+ File.file?(path)
35
+ end.sort
36
+ end
37
+
38
+ # Adds extra space when outputting an array. This helps create better version
39
+ # control diffs, because otherwise it is all on the same line.
40
+ def prettyify_array(gemspec_ruby, array_name)
41
+ gemspec_ruby.gsub(/s\.#{array_name.to_s} = \[.+?\]/) do |match|
42
+ leadin, files = match[0..-2].split("[")
43
+ leadin + "[\n #{files.split(",").join(",\n ")}\n ]"
44
+ end
45
+ end
46
+
47
+ def read_gemspec
48
+ @read_gemspec ||= eval(File.read("asset-trip.gemspec"))
49
+ end
50
+
51
+ def sh(command)
52
+ puts command
53
+ system command
54
+ end
55
+ end
56
+
57
+ class Default < Thor
58
+ include GemHelpers
59
+
60
+ desc "gemspec", "Regenerate asset-trip.gemspec"
61
+ def gemspec
62
+ File.open("asset-trip.gemspec", "w") do |file|
63
+ gemspec_ruby = generate_gemspec.to_ruby
64
+ gemspec_ruby = prettyify_array(gemspec_ruby, :files)
65
+ gemspec_ruby = prettyify_array(gemspec_ruby, :test_files)
66
+ gemspec_ruby = prettyify_array(gemspec_ruby, :extra_rdoc_files)
67
+
68
+ file.write gemspec_ruby
69
+ end
70
+
71
+ puts "Wrote gemspec to asset-trip.gemspec"
72
+ read_gemspec.validate
73
+ end
74
+
75
+ desc "build", "Build a asset-trip gem"
76
+ def build
77
+ sh "gem build asset-trip.gemspec"
78
+ FileUtils.mkdir_p "pkg"
79
+ FileUtils.mv read_gemspec.file_name, "pkg"
80
+ end
81
+
82
+ desc "install", "Install the latest built gem"
83
+ def install
84
+ sh "gem install --local pkg/#{read_gemspec.file_name}"
85
+ end
86
+
87
+ desc "release", "Release the current branch to GitHub and Gemcutter"
88
+ def release
89
+ gemspec
90
+ build
91
+ Release.new.tag
92
+ Release.new.gem
93
+ end
94
+ end
95
+
96
+ class Release < Thor
97
+ include GemHelpers
98
+
99
+ desc "tag", "Tag the gem on the origin server"
100
+ def tag
101
+ release_tag = "v#{read_gemspec.version}"
102
+ sh "git tag -a #{release_tag} -m 'Tagging #{release_tag}'"
103
+ sh "git push origin #{release_tag}"
104
+ end
105
+
106
+ desc "gem", "Push the gem to Gemcutter"
107
+ def gem
108
+ sh "gem push pkg/#{read_gemspec.file_name}"
109
+ end
110
+ end
@@ -0,0 +1,107 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{asset-trip}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Bryan Helmkamp"]
9
+ s.date = %q{2009-11-05}
10
+ s.description = %q{Asset Trip bundles JavaScript and CSS files at deploy time. The assets are
11
+ then served from a Git-esque object store in the application's public
12
+ directory.}
13
+ s.email = %q{bryan@brynary.com}
14
+ s.extra_rdoc_files = [
15
+ "History.txt",
16
+ "README.rdoc",
17
+ "MIT-LICENSE.txt"
18
+ ]
19
+ s.files = [
20
+ ".gitignore",
21
+ "History.txt",
22
+ "MIT-LICENSE.txt",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "Thorfile",
26
+ "asset-trip.gemspec",
27
+ "init.rb",
28
+ "lib/asset_trip.rb",
29
+ "lib/asset_trip/asset.rb",
30
+ "lib/asset_trip/compressor.rb",
31
+ "lib/asset_trip/config.rb",
32
+ "lib/asset_trip/file_writer.rb",
33
+ "lib/asset_trip/helper.rb",
34
+ "lib/asset_trip/javascript.rb",
35
+ "lib/asset_trip/load_path.rb",
36
+ "lib/asset_trip/manifest.rb",
37
+ "lib/asset_trip/manifest_writer.rb",
38
+ "lib/asset_trip/memoizable.rb",
39
+ "lib/asset_trip/middleware.rb",
40
+ "lib/asset_trip/ssl_stylesheet.rb",
41
+ "lib/asset_trip/stylesheet.rb",
42
+ "lib/asset_trip/url_rewriter.rb",
43
+ "spec/asset_trip/asset_spec.rb",
44
+ "spec/asset_trip/compressor_spec.rb",
45
+ "spec/asset_trip/config_spec.rb",
46
+ "spec/asset_trip/helper_spec.rb",
47
+ "spec/asset_trip/javascript_spec.rb",
48
+ "spec/asset_trip/load_path_spec.rb",
49
+ "spec/asset_trip/manifest_writer_spec.rb",
50
+ "spec/asset_trip/middleware_spec.rb",
51
+ "spec/asset_trip/stylesheet_spec.rb",
52
+ "spec/asset_trip/url_rewriter_spec.rb",
53
+ "spec/asset_trip_spec.rb",
54
+ "spec/fixtures/app/javascripts/main.js",
55
+ "spec/fixtures/app/javascripts/main/new.js",
56
+ "spec/fixtures/app/javascripts/signup.js",
57
+ "spec/fixtures/app/stylesheets/new.css",
58
+ "spec/fixtures/config/asset_trip/assets.rb",
59
+ "spec/integration/bundle_spec.rb",
60
+ "spec/integration/prune_spec.rb",
61
+ "spec/spec.opts",
62
+ "spec/spec_helper.rb",
63
+ "spec/support/core_extensions.rb",
64
+ "spec/support/helpers.rb",
65
+ "spec/support/matchers.rb",
66
+ "spec/support/path_utils.rb",
67
+ "spec/support/sandbox_helper.rb",
68
+ "tasks/asset_trip.rake",
69
+ "vendor/yuicompressor-2.4.2.jar"
70
+ ]
71
+ s.homepage = %q{http://github.com/brynary/asset-trip}
72
+ s.require_paths = ["lib"]
73
+ s.rubygems_version = %q{1.3.5}
74
+ s.summary = %q{Rails asset bundling plugin that will expand your mind}
75
+ s.test_files = [
76
+ "spec/asset_trip/asset_spec.rb",
77
+ "spec/asset_trip/compressor_spec.rb",
78
+ "spec/asset_trip/config_spec.rb",
79
+ "spec/asset_trip/helper_spec.rb",
80
+ "spec/asset_trip/javascript_spec.rb",
81
+ "spec/asset_trip/load_path_spec.rb",
82
+ "spec/asset_trip/manifest_writer_spec.rb",
83
+ "spec/asset_trip/middleware_spec.rb",
84
+ "spec/asset_trip/stylesheet_spec.rb",
85
+ "spec/asset_trip/url_rewriter_spec.rb",
86
+ "spec/asset_trip_spec.rb",
87
+ "spec/fixtures/config/asset_trip/assets.rb",
88
+ "spec/integration/bundle_spec.rb",
89
+ "spec/integration/prune_spec.rb",
90
+ "spec/spec_helper.rb",
91
+ "spec/support/core_extensions.rb",
92
+ "spec/support/helpers.rb",
93
+ "spec/support/matchers.rb",
94
+ "spec/support/path_utils.rb",
95
+ "spec/support/sandbox_helper.rb"
96
+ ]
97
+
98
+ if s.respond_to? :specification_version then
99
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
100
+ s.specification_version = 3
101
+
102
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
103
+ else
104
+ end
105
+ else
106
+ end
107
+ end
data/init.rb ADDED
@@ -0,0 +1,3 @@
1
+ if File.exist?(Rails.root.join("config", "asset_trip", "manifest.rb"))
2
+ require Rails.root.join("config", "asset_trip", "manifest")
3
+ end
@@ -0,0 +1,82 @@
1
+ require "digest"
2
+
3
+ module AssetTrip
4
+ class Asset
5
+ extend Memoizable
6
+
7
+ attr_reader :files
8
+
9
+ def initialize(config, name, files = [], &block)
10
+ @config = config
11
+ @name = name
12
+ @files = files
13
+
14
+ instance_eval(&block) if block_given?
15
+ end
16
+
17
+ def contents
18
+ compressor.compress(joined_contents)
19
+ end
20
+
21
+ def paths
22
+ files.map do |file|
23
+ @config.resolve_file(asset_type, file)
24
+ end
25
+ end
26
+
27
+ def bundle!
28
+ if expired?
29
+ FileWriter.new(path).write!(contents)
30
+ else
31
+ last_package = packaged_files.sort_by { |path| File.mtime(path) }.last
32
+ @md5sum = File.dirname(last_package).last(12).gsub(/\//, '')
33
+ end
34
+ end
35
+
36
+ def md5sum
37
+ @md5sum ||= Digest::MD5.hexdigest(contents)
38
+ end
39
+
40
+ private
41
+
42
+ def expired?
43
+ packaged_files.empty? || generated_at <= last_change_at
44
+ end
45
+
46
+ def generated_at
47
+ packaged_file_mtimes.last
48
+ end
49
+
50
+ def packaged_file_mtimes
51
+ @packaged_file_mtimes ||= packaged_files.map { |path| File.mtime(path) }.sort
52
+ end
53
+
54
+ def packaged_files
55
+ Dir[AssetTrip.assets_path.join("**", name)]
56
+ end
57
+
58
+ def last_change_at
59
+ mtimes.last
60
+ end
61
+
62
+ def mtimes
63
+ @mtimes ||= paths.map { |path| File.mtime(path) }.sort
64
+ end
65
+
66
+ def include(name)
67
+ name += extension unless name.ends_with?(extension)
68
+ files << name
69
+ end
70
+
71
+ def path
72
+ dir.join(name)
73
+ end
74
+
75
+ def dir
76
+ part1 = md5sum[0..1]
77
+ part2 = md5sum[2..10]
78
+ AssetTrip.assets_path.join(part1, part2)
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ require 'popen4'
2
+
3
+ module AssetTrip
4
+ class Compressor
5
+
6
+ def initialize(type)
7
+ @type = type
8
+ end
9
+
10
+ def compress(contents)
11
+ out = nil
12
+ err = nil
13
+
14
+ status = POpen4.popen4(command) do |stdout, stderr, stdin, pid|
15
+ stdin.puts contents
16
+ stdin.close
17
+ out = stdout.read.strip
18
+ err = stderr.read.strip
19
+ end
20
+
21
+ raise CompressorError.new(err) unless status.success?
22
+ return out
23
+ end
24
+
25
+ private
26
+
27
+ def command
28
+ "java -jar #{jar_path} --type #{@type}"
29
+ end
30
+
31
+ def jar_path
32
+ AssetTrip.root.join("vendor", "yuicompressor-2.4.2.jar")
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,60 @@
1
+ module AssetTrip
2
+ class Config
3
+
4
+ def self.from_file(dir)
5
+ source = File.read(dir.join("assets.rb"))
6
+ eval "self.new {( " + source + "\n )}"
7
+ end
8
+
9
+ attr_reader :assets_hash
10
+ attr_reader :load_paths
11
+
12
+ def initialize(&block)
13
+ @load_paths = Hash.new { LoadPath.new }
14
+ @load_paths[:javascripts] = LoadPath.new(["app/javascripts"])
15
+ @load_paths[:stylesheets] = LoadPath.new(["app/stylesheets"])
16
+
17
+ @assets_hash = {}
18
+ instance_eval(&block)
19
+ end
20
+
21
+ def bundle!
22
+ assets.each do |asset|
23
+ asset.bundle!
24
+ end
25
+
26
+ ManifestWriter.new(assets).write!
27
+ end
28
+
29
+ def resolve_file(asset_type, file)
30
+ @load_paths[asset_type].resolve(file)
31
+ end
32
+
33
+ def assets
34
+ @assets_hash.values
35
+ end
36
+
37
+ private
38
+
39
+ def js_asset(name, &block)
40
+ asset = Javascript.new(self, name, &block)
41
+ @assets_hash[asset.name] = asset
42
+ end
43
+
44
+ def css_asset(name, &block)
45
+ asset = Stylesheet.new(self, name, &block)
46
+ @assets_hash[asset.name] = asset
47
+
48
+ ssl_asset = asset.ssl_stylesheet
49
+ @assets_hash[ssl_asset.name] = ssl_asset
50
+ end
51
+
52
+ def load(filename)
53
+ dirname = File.dirname(filename.to_s)
54
+ filename = File.basename(filename.to_s, ".rb") + ".rb"
55
+ source = File.read(AssetTrip.app_root.join(dirname, filename))
56
+ instance_eval(source)
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,17 @@
1
+ module AssetTrip
2
+ class FileWriter
3
+
4
+ def initialize(path)
5
+ @path = path
6
+ end
7
+
8
+ def write!(contents)
9
+ FileUtils.mkdir_p(File.dirname(@path))
10
+
11
+ File.open(@path, "w") do |file|
12
+ file << contents
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,83 @@
1
+ module AssetTrip
2
+ module Helper
3
+
4
+ def javascript_include_asset(*sources)
5
+ _javascripts_for_sources(sources).map do |javascript|
6
+ javascript_include_tag(javascript)
7
+ end.join("\n")
8
+ end
9
+
10
+ def stylesheet_link_asset(*sources)
11
+ options = sources.extract_options!
12
+
13
+ _stylesheets_for_sources(sources).map do |stylesheet|
14
+ stylesheet_link_tag(stylesheet, options)
15
+ end.join("\n")
16
+ end
17
+
18
+ def rewrite_asset_path(source)
19
+ if source =~ /assets/
20
+ source
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def _stylesheets_for_sources(sources)
29
+ sources.map { |source| _stylesheets_for_source(source) }.flatten
30
+ end
31
+
32
+ def _javascripts_for_sources(sources)
33
+ sources.map { |source| _javascripts_for_source(source) }.flatten
34
+ end
35
+
36
+ def _stylesheets_for_source(source)
37
+ if AssetTrip.bundle
38
+ _stylesheet_manifest_url(source)
39
+ else
40
+ _unbundled_stylesheet_urls(source)
41
+ end
42
+ end
43
+
44
+ def _javascripts_for_source(source)
45
+ if AssetTrip.bundle
46
+ AssetTrip.manifest.path_for(_source_with_extension(source, ".js"))
47
+ else
48
+ _unbundled_javascript_urls(source)
49
+ end
50
+ end
51
+
52
+ def _unbundled_stylesheet_urls(source)
53
+ asset = AssetTrip.config.assets_hash[_source_with_extension(source, ".css")]
54
+
55
+ asset.files.map do |file|
56
+ _unbundled_asset_url(:stylesheets, file)
57
+ end
58
+ end
59
+
60
+ def _unbundled_javascript_urls(source)
61
+ asset = AssetTrip.config.assets_hash[_source_with_extension(source, ".js")]
62
+
63
+ asset.files.map do |file|
64
+ _unbundled_asset_url(:javascripts, file)
65
+ end
66
+ end
67
+
68
+ def _unbundled_asset_url(asset_type, file)
69
+ "#{request.protocol}#{request.host}:#{request.port}/__asset_trip__/#{asset_type}/#{file}"
70
+ end
71
+
72
+ def _stylesheet_manifest_url(source)
73
+ source_with_extension = _source_with_extension(source, ".css")
74
+ source_with_extension.gsub!(/.css$/, ".ssl.css") if request.ssl?
75
+ AssetTrip.manifest.path_for(source_with_extension)
76
+ end
77
+
78
+ def _source_with_extension(source, extension)
79
+ File.basename(source.to_s, extension) + extension
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ module AssetTrip
2
+ class Javascript < Asset
3
+
4
+ def name
5
+ "#{@name}.js"
6
+ end
7
+
8
+ private
9
+
10
+ def joined_contents
11
+ paths.map do |path|
12
+ File.read(path)
13
+ end.join("\n\n")
14
+ end
15
+
16
+ def compressor
17
+ Compressor.new("js")
18
+ end
19
+
20
+ def asset_type
21
+ :javascripts
22
+ end
23
+
24
+ def extension
25
+ ".js"
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ module AssetTrip
2
+ class LoadPath
3
+
4
+ attr_reader :paths
5
+
6
+ def initialize(paths = [])
7
+ @paths = paths.map { |path| Pathname.new(path) }
8
+ end
9
+
10
+ def ==(other)
11
+ self.class == other.class &&
12
+ @paths == other.paths
13
+ end
14
+
15
+ def <<(path)
16
+ @paths << Pathname.new(path)
17
+ end
18
+
19
+ # TODO: Refactor
20
+ def resolve(file)
21
+ raise UnknownAssetError.new("Could not find #{file} in paths: #{@paths.inspect}") if file.nil?
22
+
23
+ file_paths = @paths.map do |path|
24
+ path.join(file).expand_path
25
+ end
26
+
27
+ result = file_paths.detect do |file_path|
28
+ File.exist?(file_path)
29
+ end
30
+
31
+ if result
32
+ return result
33
+ else
34
+ raise UnknownAssetError.new("Could not find #{file} in paths: #{@paths.inspect}")
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ module AssetTrip
2
+ class Manifest
3
+
4
+ def initialize(md5sums = {})
5
+ @md5sums = md5sums
6
+ end
7
+
8
+ def path_for(key)
9
+ md5sum = self[key]
10
+ "/" + File.join("assets", md5sum[0..1], md5sum[2..10], key)
11
+ end
12
+
13
+ def []=(key, value)
14
+ @md5sums[key] = value
15
+ end
16
+
17
+ def [](key)
18
+ unless @md5sums.has_key?(key)
19
+ raise UnknownAssetError.new("Can't find #{key} in the manifest")
20
+ end
21
+
22
+ @md5sums[key]
23
+ end
24
+
25
+ def prune!
26
+ Pathname.glob(AssetTrip.assets_path.join("**", "*.*")).each do |file|
27
+ part2 = file.dirname.split.last.to_s
28
+ part1 = file.dirname.split.first.split.last.to_s
29
+
30
+ md5sum = part1 + part2
31
+
32
+ if !@md5sums.has_key?(file.basename.to_s) || @md5sums[file.basename.to_s][0..10] != md5sum
33
+ File.unlink(file)
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+