asset-trip 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.
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
+