dassets 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.md +56 -2
  2. data/bin/dassets +7 -0
  3. data/dassets.gemspec +9 -3
  4. data/lib/dassets/asset_file.rb +65 -0
  5. data/lib/dassets/cli.rb +109 -0
  6. data/lib/dassets/digests_file.rb +70 -0
  7. data/lib/dassets/root_path.rb +12 -0
  8. data/lib/dassets/runner/cache_command.rb +46 -0
  9. data/lib/dassets/runner/digest_command.rb +65 -0
  10. data/lib/dassets/runner.rb +42 -0
  11. data/lib/dassets/server/request.rb +48 -0
  12. data/lib/dassets/server/response.rb +36 -0
  13. data/lib/dassets/server.rb +37 -0
  14. data/lib/dassets/version.rb +1 -1
  15. data/lib/dassets.rb +32 -2
  16. data/test/helper.rb +3 -1
  17. data/test/support/app/assets/.digests +4 -0
  18. data/test/support/app/assets/public/file1.txt +1 -0
  19. data/test/support/app/assets/public/file2.txt +1 -0
  20. data/test/support/app/assets/public/grumpy_cat.jpg +0 -0
  21. data/test/support/app/assets/public/nested/file3.txt +0 -0
  22. data/test/support/app.rb +10 -0
  23. data/test/support/app_public/.gitkeep +0 -0
  24. data/test/support/config/assets.rb +7 -0
  25. data/test/support/example.digests +3 -0
  26. data/test/support/public/file1-daa05c683a4913b268653f7a7e36a5b4.txt +1 -0
  27. data/test/support/public/file2-9bbe1047cffbb590f59e0e5aeff46ae4.txt +1 -0
  28. data/test/support/public/grumpy_cat-b0d1f399a916f7a25c4c0f693c619013.jpg +0 -0
  29. data/test/support/public/nested/file3-d41d8cd98f00b204e9800998ecf8427e.txt +0 -0
  30. data/test/system/rack_tests.rb +78 -0
  31. data/test/unit/asset_file_tests.rb +76 -0
  32. data/test/unit/config_tests.rb +27 -0
  33. data/test/unit/dassets_tests.rb +49 -0
  34. data/test/unit/digests_file_tests.rb +90 -0
  35. data/test/unit/runner/cache_command_tests.rb +62 -0
  36. data/test/unit/runner/digest_command_tests.rb +83 -0
  37. data/test/unit/runner_tests.rb +29 -0
  38. data/test/unit/server/request_tests.rb +76 -0
  39. data/test/unit/server/response_tests.rb +70 -0
  40. data/test/unit/server_tests.rb +17 -0
  41. metadata +130 -10
data/README.md CHANGED
@@ -1,10 +1,64 @@
1
1
  # Dassets
2
2
 
3
- Digest and serve asset files.
3
+ Digest and serve HTML asset files.
4
4
 
5
5
  ## Usage
6
6
 
7
- TODO: Write code samples and usage instructions here
7
+ You have some css, js, images, etc files. You want to update, deploy, and serve them in an efficient way. Dassets can help.
8
+
9
+ ### Setup
10
+
11
+ ```ruby
12
+ # in config/dassets.rb
13
+ require 'dassets'
14
+
15
+ Dassets.configure do |c|
16
+
17
+ # tell Dassets what the root path of your app is
18
+ c.root_path '/path/to/app/root'
19
+
20
+ # it works best to *not* keep the asset files in your public dir
21
+ c.files_path '/path/to/not/public' # default: '{root_path}/app/assets/public'
22
+
23
+ # you can choose the file to write the digests to, if you want
24
+ c.digests_file_path '/path/to/.digests' # default: '{files_path}/app/assets/.digests'
25
+
26
+ end
27
+ ```
28
+
29
+ ### Digest
30
+
31
+ ```
32
+ $ dassets digest # rebuild the .digests for all asset files, OR
33
+ $ dassets digest /path/to/asset/file # update the digest for just one file
34
+ ```
35
+
36
+ Use the CLI to build your digests file. Protip: use guard to auto rebuild digests every time you edit an asset file. TODO: link to some guard tools or docs.
37
+
38
+ ### Link To
39
+
40
+ ```rb
41
+ Dassets.init
42
+ Dassets['css/site.css'].href # => "/css/site-123abc.css"
43
+ Dassets['img/logos/main.jpg'].href # => "/img/logos/main-a1b2c3.jpg"
44
+ ```
45
+
46
+ ### Serve
47
+
48
+ In development, use the Dassets middleware to serve your digested asset files:
49
+
50
+ ```ruby
51
+ # `app` is a rack application
52
+ require 'dassets/server'
53
+ app.use Dassets::Server
54
+ ```
55
+
56
+ In production, use the CLI to cache your digested asset files to the public dir:
57
+
58
+ ```
59
+ # call the CLI in your deploy scripts or whatever
60
+ $ dassets cache /path/to/public/dir
61
+ ```
8
62
 
9
63
  ## Installation
10
64
 
data/bin/dassets ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Copyright (c) 2013-Present Kelly Redding and Collin Redding
4
+ #
5
+
6
+ require 'dassets/cli'
7
+ Dassets::CLI.run *ARGV
data/dassets.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
8
8
  gem.version = Dassets::VERSION
9
9
  gem.authors = ["Kelly Redding", "Collin Redding"]
10
10
  gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
11
- gem.description = %q{Digest and serve asset files}
12
- gem.summary = %q{Digested Assets}
11
+ gem.description = %q{Digest and serve HTML asset files}
12
+ gem.summary = %q{Digested asset files}
13
13
  gem.homepage = "http://github.com/redding/dassets"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
@@ -17,6 +17,12 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_development_dependency("assert")
20
+ gem.add_development_dependency("assert", ["~> 2.0"])
21
+ gem.add_development_dependency('assert-rack-test', ["~> 1.0"])
22
+ gem.add_development_dependency("sinatra", ["~> 1.4"])
23
+
24
+
25
+ gem.add_dependency('ns-options', ["~> 1.1"])
26
+ gem.add_dependency("rack", ["~> 1.0"])
21
27
 
22
28
  end
@@ -0,0 +1,65 @@
1
+ require 'digest/md5'
2
+ require 'rack/utils'
3
+ require 'rack/mime'
4
+
5
+ module Dassets; end
6
+ class Dassets::AssetFile
7
+
8
+ def self.from_abs_path(abs_path)
9
+ rel_path = abs_path.sub("#{Dassets.config.files_path}/", '')
10
+ md5 = Digest::MD5.file(abs_path).hexdigest
11
+ self.new(rel_path, md5)
12
+ end
13
+
14
+ attr_reader :path, :md5, :dirname, :extname, :basename
15
+ attr_reader :files_path, :cache_path, :href
16
+
17
+ def initialize(rel_path, md5)
18
+ @path, @md5 = rel_path, md5
19
+ @dirname = File.dirname(@path)
20
+ @extname = File.extname(@path)
21
+ @basename = File.basename(@path, @extname)
22
+
23
+ file_name = "#{@basename}-#{@md5}#{@extname}"
24
+ @files_path = File.join(Dassets.config.files_path, @path)
25
+ @cache_path = File.join(@dirname, file_name).sub(/^\.\//, '').sub(/^\//, '')
26
+ @href = "/#{@cache_path}"
27
+ end
28
+
29
+ def content
30
+ @content ||= if File.exists?(@files_path) && File.file?(@files_path)
31
+ File.read(@files_path)
32
+ end
33
+ end
34
+
35
+ def mtime
36
+ @mtime ||= if File.exists?(@files_path) && File.file?(@files_path)
37
+ File.mtime(@files_path).httpdate
38
+ end
39
+ end
40
+
41
+ # We check via File::size? whether this file provides size info via stat,
42
+ # otherwise we have to figure it out by reading the whole file into memory.
43
+ def size
44
+ @size ||= if File.exists?(@files_path) && File.file?(@files_path)
45
+ File.size?(@files_path) || Rack::Utils.bytesize(self.content)
46
+ end
47
+ end
48
+
49
+ def mime_type
50
+ @mime_type ||= if File.exists?(@files_path) && File.file?(@files_path)
51
+ Rack::Mime.mime_type(@extname)
52
+ end
53
+ end
54
+
55
+ def exists?
56
+ File.exists?(@files_path) && File.file?(@files_path)
57
+ end
58
+
59
+ def ==(other_asset_file)
60
+ other_asset_file.kind_of?(Dassets::AssetFile) &&
61
+ self.path == other_asset_file.path &&
62
+ self.md5 == other_asset_file.md5
63
+ end
64
+
65
+ end
@@ -0,0 +1,109 @@
1
+ require 'dassets/version'
2
+ require 'dassets/runner'
3
+
4
+ module Dassets
5
+
6
+ class CLI
7
+
8
+ def self.run(*args)
9
+ self.new.run(*args)
10
+ end
11
+
12
+ def initialize
13
+ @cli = CLIRB.new
14
+ end
15
+
16
+ def run(*args)
17
+ begin
18
+ @cli.parse!(args)
19
+ Dassets::Runner.new(@cli.args, @cli.opts).run
20
+ rescue CLIRB::HelpExit
21
+ puts help
22
+ rescue CLIRB::VersionExit
23
+ puts Dassets::VERSION
24
+ rescue Dassets::Runner::UnknownCmdError => err
25
+ $stderr.puts "#{err.message}\n\n"
26
+ $stderr.puts help
27
+ exit(1)
28
+ rescue Dassets::Runner::CmdError => err
29
+ $stderr.puts "#{err.message}"
30
+ exit(1)
31
+ rescue Dassets::Runner::CmdFail => err
32
+ exit(1)
33
+ rescue CLIRB::Error => exception
34
+ $stderr.puts "#{exception.message}\n\n"
35
+ $stderr.puts help
36
+ exit(1)
37
+ rescue Exception => exception
38
+ $stderr.puts "#{exception.class}: #{exception.message}"
39
+ $stderr.puts exception.backtrace.join("\n")
40
+ exit(1)
41
+ end
42
+ exit(0)
43
+ end
44
+
45
+ def help
46
+ "Usage: dassets [options] COMMAND\n"\
47
+ "\n"\
48
+ "Options:"\
49
+ "#{@cli}"
50
+ end
51
+
52
+ end
53
+
54
+ class CLIRB # Version 1.0.0, https://github.com/redding/cli.rb
55
+ Error = Class.new(RuntimeError);
56
+ HelpExit = Class.new(RuntimeError); VersionExit = Class.new(RuntimeError)
57
+ attr_reader :argv, :args, :opts, :data
58
+
59
+ def initialize(&block)
60
+ @options = []; instance_eval(&block) if block
61
+ require 'optparse'
62
+ @data, @args, @opts = [], [], {}; @parser = OptionParser.new do |p|
63
+ p.banner = ''; @options.each do |o|
64
+ @opts[o.name] = o.value; p.on(*o.parser_args){ |v| @opts[o.name] = v }
65
+ end
66
+ p.on_tail('--version', ''){ |v| raise VersionExit, v.to_s }
67
+ p.on_tail('--help', ''){ |v| raise HelpExit, v.to_s }
68
+ end
69
+ end
70
+
71
+ def option(*args); @options << Option.new(*args); end
72
+ def parse!(argv)
73
+ @args = (argv || []).dup.tap do |args_list|
74
+ begin; @parser.parse!(args_list)
75
+ rescue OptionParser::ParseError => err; raise Error, err.message; end
76
+ end; @data = @args + [@opts]
77
+ end
78
+ def to_s; @parser.to_s; end
79
+ def inspect
80
+ "#<#{self.class}:#{'0x0%x' % (object_id << 1)} @data=#{@data.inspect}>"
81
+ end
82
+
83
+ class Option
84
+ attr_reader :name, :opt_name, :desc, :abbrev, :value, :klass, :parser_args
85
+
86
+ def initialize(name, *args)
87
+ settings, @desc = args.last.kind_of?(::Hash) ? args.pop : {}, args.pop || ''
88
+ @name, @opt_name, @abbrev = parse_name_values(name, settings[:abbrev])
89
+ @value, @klass = gvalinfo(settings[:value])
90
+ @parser_args = if [TrueClass, FalseClass, NilClass].include?(@klass)
91
+ ["-#{@abbrev}", "--[no-]#{@opt_name}", @desc]
92
+ else
93
+ ["-#{@abbrev}", "--#{@opt_name} #{@opt_name.upcase}", @klass, @desc]
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def parse_name_values(name, custom_abbrev)
100
+ [ (processed_name = name.to_s.strip.downcase), processed_name.gsub('_', '-'),
101
+ custom_abbrev || processed_name.gsub(/[^a-z]/, '').chars.first || 'a'
102
+ ]
103
+ end
104
+ def gvalinfo(v); v.kind_of?(Class) ? [nil,gklass(v)] : [v,gklass(v.class)]; end
105
+ def gklass(k); k == Fixnum ? Integer : k; end
106
+ end
107
+ end
108
+
109
+ end
@@ -0,0 +1,70 @@
1
+ require 'dassets/asset_file'
2
+
3
+ module Dassets
4
+
5
+ class DigestsFile
6
+
7
+ attr_reader :path
8
+
9
+ def initialize(file_path)
10
+ @path, @hash = file_path, decode(file_path)
11
+ end
12
+
13
+ def [](*args); @hash.send('[]', *args); end
14
+ def []=(*args); @hash.send('[]=', *args); end
15
+ def delete(*args); @hash.delete(*args); end
16
+
17
+ def each(*args, &block); @hash.each(*args, &block); end
18
+
19
+ def keys; @hash.keys; end
20
+ def values; @hash.values; end
21
+ def empty?; @hash.empty?; end
22
+
23
+ def asset_files
24
+ @hash.map{ |path, md5| Dassets::AssetFile.new(path, md5) }
25
+ end
26
+
27
+ def asset_file(path)
28
+ Dassets::AssetFile.new(path, @hash[path] || '')
29
+ end
30
+
31
+ def to_hash
32
+ Hash.new.tap do |to_hash|
33
+ @hash.each{ |k, v| to_hash[k] = v }
34
+ end
35
+ end
36
+
37
+ def save!
38
+ encode(@hash, @path)
39
+ end
40
+
41
+ private
42
+
43
+ def decode(file_path)
44
+ Hash.new.tap do |h|
45
+ if File.exists?(file_path)
46
+ File.open(file_path, 'r').each_line do |l|
47
+ path, md5 = l.split(','); path ||= ''; path.strip!; md5 ||= ''; md5.strip!
48
+ h[path] = md5 if !path.empty?
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def encode(hash, file_path)
55
+ File.open(file_path, 'w') do |f|
56
+ hash.keys.sort.each{ |path| f.write("#{path.strip},#{hash[path].strip}\n") }
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ module NullDigestsFile
63
+
64
+ def self.new
65
+ DigestsFile.new('/dev/null')
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,12 @@
1
+ # This takes a path string relative to the configured root path and tranforms
2
+ # to the full qualifed root path. The goal here is to specify path options
3
+ # with root-relative path strings.
4
+
5
+ module Dassets; end
6
+ class Dassets::RootPath < String
7
+
8
+ def initialize(path_string)
9
+ super(Dassets.config.root_path.join(path_string).to_s)
10
+ end
11
+
12
+ end
@@ -0,0 +1,46 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+ require 'dassets/asset_file'
4
+ require 'dassets/digests_file'
5
+
6
+ module Dassets; end
7
+ class Dassets::Runner; end
8
+ class Dassets::Runner::CacheCommand
9
+
10
+ attr_reader :files_root_path, :cache_root_path, :digests_file, :asset_files
11
+
12
+ def initialize(cache_root_path)
13
+ unless cache_root_path && File.directory?(cache_root_path)
14
+ raise Dassets::Runner::CmdError, "specify an existing cache directory"
15
+ end
16
+
17
+ @files_root_path = Pathname.new(Dassets.config.files_path)
18
+ @cache_root_path = Pathname.new(cache_root_path)
19
+ @digests_file = Dassets::DigestsFile.new(Dassets.config.digests_file_path)
20
+ @asset_files = @digests_file.asset_files
21
+ end
22
+
23
+ def run(write_files=true)
24
+ begin
25
+ @asset_files.each do |file|
26
+ files_path = @files_root_path.join(file.path).to_s
27
+ cache_path = @cache_root_path.join(file.cache_path).to_s
28
+
29
+ if write_files
30
+ FileUtils.mkdir_p File.dirname(cache_path)
31
+ FileUtils.cp(files_path, cache_path)
32
+ end
33
+ end
34
+ unless ENV['DASSETS_TEST_MODE']
35
+ $stdout.puts "#{@asset_files.size} files written to #{@cache_root_path}"
36
+ end
37
+ return write_files
38
+ rescue Exception => e
39
+ unless ENV['DASSETS_TEST_MODE']
40
+ $stderr.puts e, *e.backtrace; $stderr.puts ""
41
+ end
42
+ raise Dassets::Runner::CmdFail
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,65 @@
1
+ require 'set'
2
+ require 'dassets/asset_file'
3
+ require 'dassets/digests_file'
4
+
5
+ module Dassets; end
6
+ class Dassets::Runner; end
7
+ class Dassets::Runner::DigestCommand
8
+
9
+ attr_reader :asset_files, :digests_file
10
+
11
+ def initialize(file_paths)
12
+ @pwd = ENV['PWD']
13
+ @asset_files = if (file_paths || []).empty?
14
+ get_asset_files([*Dassets.config.files_path])
15
+ else
16
+ get_asset_files(file_paths)
17
+ end
18
+ @digests_file = Dassets::DigestsFile.new(Dassets.config.digests_file_path)
19
+ end
20
+
21
+ def run(save=true)
22
+ begin
23
+ digest_paths = @digests_file.keys
24
+ asset_paths = @asset_files.map{ |f| f.path }
25
+
26
+ (digest_paths - asset_paths).each{ |file| @digests_file.delete(file) }
27
+ @asset_files.each{ |f| @digests_file[f.path] = f.md5 }
28
+
29
+ @digests_file.save! if save
30
+ unless ENV['DASSETS_TEST_MODE']
31
+ $stdout.puts "digested #{@asset_files.size} assets, saved to #{@digests_file.path}"
32
+ end
33
+ return save
34
+ rescue Exception => e
35
+ unless ENV['DASSETS_TEST_MODE']
36
+ $stderr.puts e, *e.backtrace; $stderr.puts ""
37
+ end
38
+ raise Dassets::Runner::CmdFail
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ # Get all file paths fuzzy-matching the given paths. Each path must be a
45
+ # file that exists and is in the `config.files_path` tree. Return them
46
+ # as sorted AssetFile objects.
47
+ def get_asset_files(paths)
48
+ fuzzy_paths(paths).
49
+ select{ |p| is_asset_file?(p) }.
50
+ sort.
51
+ map{ |p| Dassets::AssetFile.from_abs_path(p) }
52
+ end
53
+
54
+ def fuzzy_paths(paths)
55
+ paths.inject(Set.new) do |paths, path|
56
+ p = File.expand_path(path, @pwd)
57
+ paths += Dir.glob("#{p}*") + Dir.glob("#{p}*/**/*")
58
+ end
59
+ end
60
+
61
+ def is_asset_file?(path)
62
+ File.file?(path) && path.include?("#{Dassets.config.files_path}/")
63
+ end
64
+
65
+ end
@@ -0,0 +1,42 @@
1
+ require 'dassets'
2
+
3
+ ENV['DASSETS_CONFIG_FILE'] ||= 'config/assets'
4
+
5
+ module Dassets; end
6
+ class Dassets::Runner
7
+ UnknownCmdError = Class.new(ArgumentError)
8
+ CmdError = Class.new(RuntimeError)
9
+ CmdFail = Class.new(RuntimeError)
10
+
11
+ attr_reader :cmd_name, :cmd_args, :opts
12
+
13
+ def initialize(args, opts)
14
+ @opts = opts
15
+ @cmd_name = args.shift || ""
16
+ @cmd_args = args
17
+ end
18
+
19
+ def run
20
+ require ENV['DASSETS_CONFIG_FILE']
21
+
22
+ case @cmd_name
23
+ when 'digest'
24
+ require 'dassets/runner/digest_command'
25
+ DigestCommand.new(@cmd_args).run
26
+ when 'cache'
27
+ require 'dassets/runner/cache_command'
28
+ CacheCommand.new(@cmd_args.first).run
29
+ when 'null'
30
+ NullCommand.new.run
31
+ else
32
+ raise UnknownCmdError, "unknown command `#{@cmd_name}`"
33
+ end
34
+ end
35
+
36
+ class NullCommand
37
+ def run
38
+ # if this was a real command it would do something here
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,48 @@
1
+ require 'rack/request'
2
+
3
+ class Dassets::Server
4
+
5
+ class Request < Rack::Request
6
+
7
+ # The HTTP request method. This is the standard implementation of this
8
+ # method but is respecified here due to libraries that attempt to modify
9
+ # the behavior to respect POST tunnel method specifiers. We always want
10
+ # the real request method.
11
+ def request_method; @env['REQUEST_METHOD']; end
12
+ def path_info; @env['PATH_INFO']; end
13
+
14
+ # Determine if the request is for an asset file
15
+ # This will be called on every request so speed is an issue
16
+ # - first check if the request is a GET or HEAD (fast)
17
+ # - then check if for a digest resource (kinda fast)
18
+ # - then check if on a path in the digests (slower)
19
+ def for_asset_file?
20
+ !!((get? || head?) && for_digest_file? && Dassets.digests[asset_path])
21
+ end
22
+
23
+ def asset_path
24
+ @asset_path ||= path_digest_match.captures.select{ |m| !m.empty? }.join
25
+ end
26
+
27
+ def asset_file
28
+ @asset_file ||= Dassets[asset_path]
29
+ end
30
+
31
+ private
32
+
33
+ def for_digest_file?
34
+ !path_digest_match.nil?
35
+ end
36
+
37
+ def path_digest_match
38
+ @path_digest_match ||= begin
39
+ path_info.match(/\/(.+)-[a-f0-9]{32}(\..+|)$/i) || NullDigestMatch.new
40
+ end
41
+ end
42
+
43
+ class NullDigestMatch
44
+ def captures; []; end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ require 'rack/response'
2
+ require 'rack/utils'
3
+ require 'rack/mime'
4
+
5
+ class Dassets::Server
6
+
7
+ class Response
8
+ attr_reader :asset_file, :status, :headers, :body
9
+
10
+ def initialize(env, asset_file)
11
+ @asset_file = asset_file
12
+
13
+ mtime = @asset_file.mtime.to_s
14
+ @status, @headers, @body = if env['HTTP_IF_MODIFIED_SINCE'] == mtime
15
+ [ 304, Rack::Utils::HeaderHash.new, [] ]
16
+ elsif !@asset_file.exists?
17
+ [ 404, Rack::Utils::HeaderHash.new, [] ]
18
+ else
19
+ [ 200,
20
+ Rack::Utils::HeaderHash.new.tap do |h|
21
+ h["Content-Type"] = @asset_file.mime_type.to_s
22
+ h["Content-Length"] = @asset_file.size.to_s
23
+ h["Last-Modified"] = mtime
24
+ end,
25
+ env["REQUEST_METHOD"] == "HEAD" ? [] : [ @asset_file.content ]
26
+ ]
27
+ end
28
+ end
29
+
30
+ def to_rack
31
+ [@status, @headers.to_hash, @body]
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,37 @@
1
+ require 'dassets/server/request'
2
+ require 'dassets/server/response'
3
+
4
+ # Rack middleware for serving Dassets asset files
5
+
6
+ module Dassets
7
+ class Server
8
+
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ # The Rack call interface. The receiver acts as a prototype and runs
14
+ # each request in a clone object unless the +rack.run_once+ variable is
15
+ # set in the environment. Ripped from:
16
+ # http://github.com/rtomayko/rack-cache/blob/master/lib/rack/cache/context.rb
17
+ def call(env)
18
+ if env['rack.run_once']
19
+ call! env
20
+ else
21
+ clone.call! env
22
+ end
23
+ end
24
+
25
+ # The real Rack call interface.
26
+ # if an asset file is being requested, this is an endpoint - otherwise, call
27
+ # on up to the app as normal
28
+ def call!(env)
29
+ if (request = Request.new(env)).for_asset_file?
30
+ Response.new(env, request.asset_file).to_rack
31
+ else
32
+ @app.call(env)
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module Dassets
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/dassets.rb CHANGED
@@ -1,5 +1,35 @@
1
- require "dassets/version"
1
+ require 'pathname'
2
+ require 'ns-options'
3
+
4
+ require 'dassets/version'
5
+ require 'dassets/root_path'
6
+ require 'dassets/digests_file'
2
7
 
3
8
  module Dassets
4
- # TODO: your code goes here...
9
+
10
+ def self.config; Config; end
11
+ def self.configure(&block); Config.define(&block); end
12
+
13
+ def self.init
14
+ @digests_file = DigestsFile.new(self.config.digests_file_path)
15
+ end
16
+
17
+ def self.reset
18
+ @digests_file = nil
19
+ end
20
+
21
+ def self.digests; @digests_file || NullDigestsFile.new; end
22
+ def self.[](asset_path)
23
+ self.digests.asset_file(asset_path)
24
+ end
25
+
26
+ class Config
27
+ include NsOptions::Proxy
28
+
29
+ option :root_path, Pathname, :required => true
30
+ option :files_path, RootPath, :default => proc{ "app/assets/public" }
31
+ option :digests_file_path, RootPath, :default => proc{ "app/assets/.digests" }
32
+
33
+ end
34
+
5
35
  end
data/test/helper.rb CHANGED
@@ -7,4 +7,6 @@ $LOAD_PATH.unshift(File.expand_path("../..", __FILE__))
7
7
  # require pry for debugging (`binding.pry`)
8
8
  require 'pry'
9
9
 
10
- # TODO: put test helpers here...
10
+ ENV['DASSETS_TEST_MODE'] = 'yes'
11
+ ENV['DASSETS_CONFIG_FILE'] = 'test/support/config/assets'
12
+ require ENV['DASSETS_CONFIG_FILE']