asset_library 0.4.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/README.rdoc +172 -0
- data/lib/asset_library/asset.rb +35 -0
- data/lib/asset_library/asset_module.rb +84 -0
- data/lib/asset_library/helpers.rb +96 -0
- data/lib/asset_library/rake_tasks.rb +25 -0
- data/lib/asset_library/util.rb +11 -0
- data/lib/asset_library.rb +71 -0
- data/rails/init.rb +3 -0
- data/spec/asset_library/asset_module_spec.rb +216 -0
- data/spec/asset_library/asset_spec.rb +49 -0
- data/spec/asset_library/helpers_spec.rb +210 -0
- data/spec/asset_library_spec.rb +97 -0
- data/spec/spec_helper.rb +4 -0
- metadata +76 -0
data/README.rdoc
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
= asset_library
|
2
|
+
|
3
|
+
Bundles your JavaScript and CSS, so your development environment is simple to
|
4
|
+
code and your production environment is as fast as possible.
|
5
|
+
|
6
|
+
== Installation
|
7
|
+
|
8
|
+
First, install the gem:
|
9
|
+
|
10
|
+
sudo gem install alegscogs-asset_library --source http://gems.github.com
|
11
|
+
|
12
|
+
Next, add the gem into your Rails project's config/environment.rb:
|
13
|
+
|
14
|
+
config.gem 'alegscogs-asset_library', :lib => 'asset_library', :source => 'http://gems.github.com'
|
15
|
+
|
16
|
+
Finally, include the Rake tasks in your project:
|
17
|
+
|
18
|
+
echo "begin; require 'asset_library/rake_tasks'; rescue LoadError; end" > lib/tasks/asset_library.rake
|
19
|
+
|
20
|
+
== Usage
|
21
|
+
|
22
|
+
In your Rails project, edit <tt>config/asset_library.yml</tt> as described
|
23
|
+
in the following section.
|
24
|
+
|
25
|
+
Once configured, asset_library provides two helper methods for your views:
|
26
|
+
|
27
|
+
<%# outputs library.js (production) or its files (development) %>
|
28
|
+
<%= asset_library_javascript_tags(:javascripts) %>
|
29
|
+
|
30
|
+
<%# outputs library.css (production) or its files (development) %>
|
31
|
+
<%= asset_library_stylesheet_tags(:stylesheets) %>
|
32
|
+
|
33
|
+
<%# outputs library.ie6.css (production) or its files (development) %>
|
34
|
+
<!--[if lte IE 6]>
|
35
|
+
<%= asset_library_stylesheet_tags(:stylesheets, :ie6) %>
|
36
|
+
<![endif]-->
|
37
|
+
|
38
|
+
Both helpers behave differently depending on whether
|
39
|
+
<tt>ActionController::Base.perform_caching</tt> is true (that is, whether you
|
40
|
+
are in <tt>development</tt> environment or not). When caching is disabled, each
|
41
|
+
file in the module will be included. (Internet Explorer only allows 30
|
42
|
+
<tt>style</tt> and <tt>link</tt> stylesheet tags; in development mode,
|
43
|
+
<tt>import</tt> rules are used to work around the bug.) When caching is
|
44
|
+
enabled, a single tag is output.
|
45
|
+
|
46
|
+
When caching is enabled, the modules to include must be generated using:
|
47
|
+
|
48
|
+
rake asset_library:write
|
49
|
+
|
50
|
+
These moduels can be removed using:
|
51
|
+
|
52
|
+
rake asset_library:clean
|
53
|
+
|
54
|
+
A cached module is simply the concatenation of its constituent files.
|
55
|
+
|
56
|
+
== Configuration
|
57
|
+
|
58
|
+
A typical configuration (Yaml) file might look like this. In Rails, this
|
59
|
+
should be in <tt>config/asset_library.yml</tt>.
|
60
|
+
|
61
|
+
javascripts:
|
62
|
+
cache: library
|
63
|
+
optional_suffix: compressed
|
64
|
+
base: javascripts
|
65
|
+
suffix: js
|
66
|
+
files:
|
67
|
+
- vendor/jquery
|
68
|
+
|
69
|
+
# jQuery UI parts we need
|
70
|
+
- vendor/jquery-ui/ui.core
|
71
|
+
- vendor/jquery-ui/ui.draggable
|
72
|
+
- vendor/jquery-ui/ui.droppable
|
73
|
+
- vendor/jquery-ui/ui.sortable
|
74
|
+
- vendor/jquery-ui/effects.core
|
75
|
+
|
76
|
+
- lib
|
77
|
+
- plugins/*
|
78
|
+
- application
|
79
|
+
- initializers/*
|
80
|
+
|
81
|
+
tiny_mce_javascripts:
|
82
|
+
# TinyMCE doesn't give us a choice on cache name
|
83
|
+
cache: vendor/tiny_mce/tiny_mce_gzip
|
84
|
+
optional_suffix: compressed
|
85
|
+
base: javascripts
|
86
|
+
suffix: js
|
87
|
+
files:
|
88
|
+
- vendor/tiny_mce/tiny_mce
|
89
|
+
# ... it's possible to bundle all of TinyMCE together with a bit of magic
|
90
|
+
|
91
|
+
stylesheets:
|
92
|
+
cache: library
|
93
|
+
base: stylesheets
|
94
|
+
suffix: css
|
95
|
+
formats:
|
96
|
+
- ie6: [null, ie8, ie7, ie6]
|
97
|
+
- ie7: [null, ie8, ie7]
|
98
|
+
- ie8: [null, ie8]
|
99
|
+
files:
|
100
|
+
- reset
|
101
|
+
- application
|
102
|
+
- layout
|
103
|
+
- views/**/*
|
104
|
+
|
105
|
+
# in general...
|
106
|
+
#module_name:
|
107
|
+
# cache: cache_file
|
108
|
+
# base: base_path_of_assets_in_web_root
|
109
|
+
# suffix: suffix ("css" or "js")
|
110
|
+
# formats:
|
111
|
+
# format1: ["extra_suffix_1", "extra_suffix_2"]
|
112
|
+
# format2: [null, "extra_suffix3"]
|
113
|
+
# optional_suffix: optional_suffix
|
114
|
+
# files:
|
115
|
+
# - file1_relative_to_base
|
116
|
+
# - file2_relative_to_base
|
117
|
+
# - ...
|
118
|
+
|
119
|
+
Here are what the various configuration elements mean:
|
120
|
+
|
121
|
+
<tt>module_name</tt> is the name of the module. It is passed as the first
|
122
|
+
parameter to <tt>asset_library_javascript_tags</tt> or
|
123
|
+
<tt>asset_library_stylesheet_tags</tt>.
|
124
|
+
|
125
|
+
<tt>cache</tt> is a filename, without suffix, relative to <tt>base</tt>, where
|
126
|
+
the module will be bundled when caching is enabled. (Ensure that <tt>files</tt>
|
127
|
+
does not include <tt>cache_file</tt>, even with globbing.)
|
128
|
+
|
129
|
+
<tt>base</tt> is the base path of the assets in URLs. For instance, in Rails,
|
130
|
+
where stylesheets are usually served under <tt>/stylesheets</tt>, <tt>base</tt>
|
131
|
+
should be <tt>stylesheets</tt>.
|
132
|
+
|
133
|
+
<tt>suffix</tt> is either "js" or "css", depending on whether you are including
|
134
|
+
JavaScript or CSS files.
|
135
|
+
|
136
|
+
<tt>formats</tt> allows construction of parallel modules. By default, for a
|
137
|
+
module named <tt>styles</tt> will use <tt>*.css</tt> (where <tt>*</tt> is
|
138
|
+
specified by the <tt>files</tt> option) to generate <tt>styles.css</tt>; but
|
139
|
+
filenames of the format <tt>*.suffix.css</tt> will be ignored in that search.
|
140
|
+
If a <tt>format</tt> called <tt>format1</tt> is specified as
|
141
|
+
<tt>[suffix1, suffix2]</tt>, then <tt>*.suffix1.css</tt> and
|
142
|
+
<tt>*.suffix2.css</tt> will be included, but not <tt>*.css</tt>. (To specify
|
143
|
+
<tt>*.css</tt>, put <tt>null</tt> in the format list.)
|
144
|
+
|
145
|
+
<tt>optional_suffix</tt> will cause asset_library to check for the existence of
|
146
|
+
files with <tt>optional_suffix</tt> suffixes, falling back to files without
|
147
|
+
<tt>optional_suffix</tt> if necessary. For instance, if you run all your
|
148
|
+
JavaScript files through
|
149
|
+
{YUI Compressor}[http://developer.yahoo.com/yui/compressor/] and output the
|
150
|
+
compressed version of <tt>file1.js</tt> as <tt>file1.compressed.js</tt>, then
|
151
|
+
set <tt>optional_suffix</tt> to <tt>compressed</tt>. In your development
|
152
|
+
environment, where <tt>compressed</tt> javascripts are missing,
|
153
|
+
<tt>file1.js</tt> will be included and you can debug your JavaScript. In your
|
154
|
+
production environment, create the <tt>compressed</tt> JavaScripts in the same
|
155
|
+
directory, and they will be included instead, for optimal download speed.
|
156
|
+
|
157
|
+
<tt>files</tt> is a list of files, relative to <tt>base</tt>. File globbing is
|
158
|
+
allowed; <tt>**</tt> expands to "any nesting of directories". Files which do
|
159
|
+
not exist will be excluded; globbing will include/exclude files with
|
160
|
+
<tt>formats</tt> as appropriate; and files without <tt>optional_suffix</tt>
|
161
|
+
will not be output alongside files with the same name endowed with the suffix.
|
162
|
+
|
163
|
+
== Copyright
|
164
|
+
|
165
|
+
I believe in software freedom, not any abomination thereof. This project is
|
166
|
+
released under the Public Domain, meaning I relinquish my copyright (to any
|
167
|
+
extend the law allows) and grant you all rights to use, modify, sell, or
|
168
|
+
otherwise take advantage of my software.
|
169
|
+
|
170
|
+
However, I do kindly request that, as a favor, you refrain from using my
|
171
|
+
software as part of an evil plan involving velociraptors and mind-controlling
|
172
|
+
robots (even though I would not be legally entitled to sue you for doing so).
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class AssetLibrary
|
2
|
+
class Asset
|
3
|
+
attr_reader(:absolute_path)
|
4
|
+
|
5
|
+
def initialize(absolute_path)
|
6
|
+
@absolute_path = absolute_path
|
7
|
+
end
|
8
|
+
|
9
|
+
def eql?(other)
|
10
|
+
self.class === other && absolute_path == other.absolute_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash
|
14
|
+
self.absolute_path.hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def relative_path
|
18
|
+
if AssetLibrary.root == '/'
|
19
|
+
absolute_path
|
20
|
+
else
|
21
|
+
absolute_path[AssetLibrary.root.length..-1]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def timestamp
|
26
|
+
File.mtime(absolute_path)
|
27
|
+
rescue Errno::ENOENT
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def relative_url
|
32
|
+
"#{relative_path}?#{timestamp.to_i.to_s}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/asset'
|
2
|
+
|
3
|
+
class AssetLibrary
|
4
|
+
class AssetModule
|
5
|
+
attr_reader(:config)
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
# Returns an Array of Assets to include.
|
12
|
+
#
|
13
|
+
# Arguments:
|
14
|
+
# extra_suffix: if set, finds files with the given extra suffix
|
15
|
+
def assets(format = nil)
|
16
|
+
if format
|
17
|
+
assets_with_format(format)
|
18
|
+
else
|
19
|
+
assets_with_extra_suffix(nil)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an Array of Assets to include.
|
24
|
+
#
|
25
|
+
# Arguments:
|
26
|
+
# extra_suffix: if set, finds files with the given extra suffix
|
27
|
+
def assets_with_extra_suffix(extra_suffix)
|
28
|
+
return nil unless config
|
29
|
+
|
30
|
+
GlobFu.find(
|
31
|
+
config[:files],
|
32
|
+
:suffix => config[:suffix],
|
33
|
+
:extra_suffix => extra_suffix,
|
34
|
+
:root => File.join(*([AssetLibrary.root, config[:base]].compact)),
|
35
|
+
:optional_suffix => config[:optional_suffix]
|
36
|
+
).collect { |f| Asset.new(f) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns an Array of Assets to include.
|
40
|
+
#
|
41
|
+
# Calls assets_with_extra_suffix for each suffix in the given format
|
42
|
+
#
|
43
|
+
# Arguments:
|
44
|
+
# format: format specified in the config
|
45
|
+
def assets_with_format(format)
|
46
|
+
return nil unless config
|
47
|
+
|
48
|
+
extra_suffixes = config[:formats][format.to_sym]
|
49
|
+
extra_suffixes.inject([]) { |r, s| r.concat(assets_with_extra_suffix(s)) }
|
50
|
+
end
|
51
|
+
|
52
|
+
def contents(format = nil)
|
53
|
+
s = StringIO.new
|
54
|
+
|
55
|
+
assets(format).each do |asset|
|
56
|
+
File.open(asset.absolute_path, 'r') do |infile|
|
57
|
+
s.write(infile.read)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
s.rewind
|
61
|
+
|
62
|
+
s
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns an Asset representing the cache file
|
66
|
+
def cache_asset(format = nil)
|
67
|
+
extra = format ? ".#{format}" : ''
|
68
|
+
Asset.new(File.join(AssetLibrary.root, config[:base], "#{config[:cache]}#{extra}.#{config[:suffix]}"))
|
69
|
+
end
|
70
|
+
|
71
|
+
def write_cache(format = nil)
|
72
|
+
File.open(cache_asset(format).absolute_path, 'w') do |outfile|
|
73
|
+
outfile.write(contents(format).read)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def write_all_caches
|
78
|
+
write_cache
|
79
|
+
(config[:formats] || {}).keys.each do |format|
|
80
|
+
write_cache(format)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class AssetLibrary
|
2
|
+
module Helpers
|
3
|
+
def asset_library_javascript_tags(module_key, format = nil)
|
4
|
+
|
5
|
+
m = AssetLibrary.asset_module(module_key)
|
6
|
+
if AssetLibrary.cache
|
7
|
+
AssetLibrary.cache_vars[:javascript_tags] ||= {}
|
8
|
+
AssetLibrary.cache_vars[:javascript_tags][module_key] ||= asset_library_priv.script_tag(m.cache_asset)
|
9
|
+
else
|
10
|
+
m.assets(format).collect{|a| asset_library_priv.script_tag(a)}.join("\n")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def asset_library_stylesheet_tags(module_key, *args)
|
15
|
+
html_options = args.last.is_a?(Hash) ? args.pop : {}
|
16
|
+
format = args[0]
|
17
|
+
|
18
|
+
m = AssetLibrary.asset_module(module_key)
|
19
|
+
if AssetLibrary.cache
|
20
|
+
AssetLibrary.cache_vars[:stylesheet_tags] ||= {}
|
21
|
+
AssetLibrary.cache_vars[:stylesheet_tags][[module_key, format, request.protocol, request.host_with_port]] ||= asset_library_priv.style_tag(m.cache_asset(format), html_options)
|
22
|
+
else
|
23
|
+
asset_library_priv.import_styles_tag(m.assets(format), html_options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def asset_library_priv
|
30
|
+
@asset_library_priv ||= Priv.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
class Priv
|
34
|
+
# Don't pollute helper's class's namespace with all our methods; put
|
35
|
+
# them here instead
|
36
|
+
|
37
|
+
attr_accessor :helper
|
38
|
+
|
39
|
+
def initialize(helper)
|
40
|
+
@helper = helper
|
41
|
+
end
|
42
|
+
|
43
|
+
def url(asset)
|
44
|
+
absolute_url(asset.relative_url)
|
45
|
+
end
|
46
|
+
|
47
|
+
def absolute_url(relative_url)
|
48
|
+
host = helper.__send__(:compute_asset_host, relative_url) if helper.respond_to?(:compute_asset_host, true)
|
49
|
+
|
50
|
+
host = nil if host == '' # Rails sets '' by default
|
51
|
+
|
52
|
+
if host && !(host =~ %r{^[-a-z]+://})
|
53
|
+
controller = helper.instance_variable_get(:@controller)
|
54
|
+
request = controller && controller.respond_to?(:request) && controller.request
|
55
|
+
host = request && "#{request.protocol}#{host}"
|
56
|
+
end
|
57
|
+
|
58
|
+
if host
|
59
|
+
"#{host}#{relative_url}"
|
60
|
+
else
|
61
|
+
relative_url
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def script_tag(asset)
|
66
|
+
content_tag(:script, "", {:type => "text/javascript", :src => url(asset)})
|
67
|
+
end
|
68
|
+
|
69
|
+
def style_tag(asset, html_options = {})
|
70
|
+
"<link rel=\"stylesheet\" type=\"text/css\" href=\"#{url(asset)}\" #{attributes_from_hash(html_options)}/>"
|
71
|
+
end
|
72
|
+
|
73
|
+
def import_styles_tag(assets, html_options = {})
|
74
|
+
a = []
|
75
|
+
assets.each_slice(30) do |subset|
|
76
|
+
a << import_style_tag(subset, html_options)
|
77
|
+
end
|
78
|
+
a.join("\n")
|
79
|
+
end
|
80
|
+
|
81
|
+
def import_style_tag(assets, html_options = {})
|
82
|
+
imports = assets.collect{ |a| "@import \"#{url(a)}\";" }
|
83
|
+
content_tag(:style, "\n#{imports.join("\n")}\n", html_options.merge(:type => "text/css"))
|
84
|
+
end
|
85
|
+
|
86
|
+
def content_tag(name, content, options = {})
|
87
|
+
"<#{name} #{attributes_from_hash(options)}>#{content}</#{name}>"
|
88
|
+
end
|
89
|
+
|
90
|
+
def attributes_from_hash(options = {})
|
91
|
+
options.to_a.collect{|k, v| "#{k}=\"#{v}\""}.join(" ")
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
def init_asset_library
|
2
|
+
require 'asset_library'
|
3
|
+
|
4
|
+
# TODO: Find a way to not-hard-code these paths?
|
5
|
+
AssetLibrary.config_path = File.join(RAILS_ROOT, 'config', 'asset_library.yml')
|
6
|
+
AssetLibrary.root = File.join(RAILS_ROOT, 'public')
|
7
|
+
end
|
8
|
+
|
9
|
+
namespace(:asset_library) do
|
10
|
+
desc "Writes all asset caches specified in config/asset.yml by concatenating the constituent files."
|
11
|
+
task(:write) do
|
12
|
+
init_asset_library
|
13
|
+
AssetLibrary.write_all_caches
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Deletes all asset caches specified in config/asset.yml"
|
17
|
+
task(:clean) do
|
18
|
+
init_asset_library
|
19
|
+
keys = AssetLibrary.config.keys
|
20
|
+
asset_modules = keys.collect{|k| AssetLibrary.asset_module(k)}
|
21
|
+
asset_modules.each do |m|
|
22
|
+
FileUtils.rm_f(m.cache_asset.absolute_path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AssetLibrary
|
2
|
+
module Util
|
3
|
+
def self.symbolize_hash_keys(hash)
|
4
|
+
return hash unless Hash === hash # because we recurse
|
5
|
+
hash.inject({}) do |ret, (key, value)|
|
6
|
+
ret[(key.to_sym rescue key) || key] = symbolize_hash_keys(value)
|
7
|
+
ret
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
begin
|
2
|
+
require 'glob_fu'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'glob_fu'
|
6
|
+
end
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + '/asset_library/asset_module'
|
9
|
+
require File.dirname(__FILE__) + '/asset_library/util'
|
10
|
+
|
11
|
+
class AssetLibrary
|
12
|
+
class << self
|
13
|
+
def config_path
|
14
|
+
@config_path
|
15
|
+
end
|
16
|
+
|
17
|
+
def config_path=(config_path)
|
18
|
+
@config_path = config_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def root
|
22
|
+
@root
|
23
|
+
end
|
24
|
+
|
25
|
+
def root=(root)
|
26
|
+
@root = root
|
27
|
+
end
|
28
|
+
|
29
|
+
def cache
|
30
|
+
return true if @cache.nil?
|
31
|
+
@cache
|
32
|
+
end
|
33
|
+
|
34
|
+
def cache=(cache)
|
35
|
+
@config = nil
|
36
|
+
@cache_vars = nil
|
37
|
+
@cache = cache
|
38
|
+
end
|
39
|
+
|
40
|
+
def cache_vars
|
41
|
+
# We store cache_vars even if not caching--this is our "globals"
|
42
|
+
@cache_vars ||= {}
|
43
|
+
end
|
44
|
+
|
45
|
+
def config
|
46
|
+
return @config if cache && @config
|
47
|
+
ret = if File.exist?(config_path)
|
48
|
+
yaml = YAML.load_file(config_path) || {}
|
49
|
+
Util::symbolize_hash_keys(yaml)
|
50
|
+
else
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
@config = cache ? ret : nil
|
54
|
+
ret
|
55
|
+
end
|
56
|
+
|
57
|
+
def asset_module(key)
|
58
|
+
module_config = config[key.to_sym]
|
59
|
+
if module_config
|
60
|
+
AssetModule.new(module_config)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def write_all_caches
|
65
|
+
config.keys.each do |key|
|
66
|
+
m = asset_module(key)
|
67
|
+
m.write_all_caches
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
describe(AssetLibrary::AssetModule) do
|
6
|
+
before(:each) do
|
7
|
+
AssetLibrary.stub!(:root).and_return(prefix)
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
wipe_fs
|
12
|
+
end
|
13
|
+
|
14
|
+
describe('#assets') do
|
15
|
+
it('should include file1 and file2') do
|
16
|
+
files = [ '/c/file1.css', '/c/file2.css' ]
|
17
|
+
stub_fs(files)
|
18
|
+
m(css_config(:files => ['file1', 'file2'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css", "#{prefix}/c/file2.css"]
|
19
|
+
end
|
20
|
+
|
21
|
+
it('should not include file2 if that does not exist') do
|
22
|
+
files = [ '/c/file1.css' ]
|
23
|
+
stub_fs(files)
|
24
|
+
m(css_config(:files => ['file1', 'file2'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.css" ]
|
25
|
+
end
|
26
|
+
|
27
|
+
it('should not include other files') do
|
28
|
+
files = [ '/c/file1.css', '/c/file2.css' ]
|
29
|
+
stub_fs(files)
|
30
|
+
m(css_config(:files => ['file1'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.css" ]
|
31
|
+
end
|
32
|
+
|
33
|
+
it('should glob filenames') do
|
34
|
+
files = [ '/c/file1.css', '/c/file2.css', '/c/other_file.css' ]
|
35
|
+
stub_fs(files)
|
36
|
+
m(css_config(:files => ['file*'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css", "#{prefix}/c/file2.css"]
|
37
|
+
end
|
38
|
+
|
39
|
+
it('should glob directories') do
|
40
|
+
files = [ '/c/file1.css', '/c/a/file2.css', '/c/b/a/file3.css' ]
|
41
|
+
stub_fs(files)
|
42
|
+
m(css_config(:files => ['**/file*'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/a/file2.css", "#{prefix}/c/b/a/file3.css", "#{prefix}/c/file1.css"]
|
43
|
+
end
|
44
|
+
|
45
|
+
it('should use :optional_suffix when appropriate') do
|
46
|
+
files = [ '/c/file1.css', '/c/file1.css.o' ]
|
47
|
+
stub_fs(files)
|
48
|
+
m(css_config(:optional_suffix => 'o', :files => ['file1'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css.o"]
|
49
|
+
end
|
50
|
+
|
51
|
+
it('should show :optional_suffix file even if original is absent') do
|
52
|
+
files = [ '/c/file1.css.o' ]
|
53
|
+
stub_fs(files)
|
54
|
+
m(css_config(:optional_suffix => 'o', :files => ['file1'])).assets.collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css.o"]
|
55
|
+
end
|
56
|
+
|
57
|
+
it('should ignore :optional_suffix when suffixed file is not present') do
|
58
|
+
stub_fs([ '/c/file1.css' ])
|
59
|
+
m(css_config(:optional_suffix => 'o', :files => ['file1'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.css" ]
|
60
|
+
end
|
61
|
+
|
62
|
+
it('should pick files with :extra_suffix') do
|
63
|
+
stub_fs([ '/c/file1.e.css' ])
|
64
|
+
m(css_config(:files => ['file1'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e.css" ]
|
65
|
+
end
|
66
|
+
|
67
|
+
it('should ignore non-suffixed files when :extra_suffix is set') do
|
68
|
+
stub_fs([ '/c/file1.css' ])
|
69
|
+
m(css_config(:files => ['file1'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == []
|
70
|
+
end
|
71
|
+
|
72
|
+
it('should use extra suffixes with format') do
|
73
|
+
stub_fs([ '/c/file1.e1.css', '/c/file1.e2.css' ])
|
74
|
+
m(css_config(:files => ['file1'], :formats => { :f1 => [ 'e1', 'e2' ] })).assets_with_format(:f1).collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e1.css", "#{prefix}/c/file1.e2.css" ]
|
75
|
+
end
|
76
|
+
|
77
|
+
it('should ignore extra suffixes unspecified in format') do
|
78
|
+
stub_fs([ '/c/file1.e1.css', '/c/file1.e2.css' ])
|
79
|
+
m(css_config(:files => ['file1'], :formats => { :f1 => [ 'e1' ] })).assets_with_format(:f1).collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e1.css" ]
|
80
|
+
end
|
81
|
+
|
82
|
+
it('should allow nil suffixes in format') do
|
83
|
+
stub_fs([ '/c/file1.css', '/c/file1.e1.css' ])
|
84
|
+
m(css_config(:files => ['file1'], :formats => { :f1 => [nil, 'e1'] })).assets_with_format(:f1).collect{|a| a.absolute_path}.should == ["#{prefix}/c/file1.css", "#{prefix}/c/file1.e1.css" ]
|
85
|
+
end
|
86
|
+
|
87
|
+
it('should combine :extra_suffix with :optional_suffix') do
|
88
|
+
stub_fs([ '/c/file1.e.css', '/c/file1.e.css.o' ])
|
89
|
+
m(css_config(:files => ['file1'], :optional_suffix => 'o')).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e.css.o" ]
|
90
|
+
end
|
91
|
+
|
92
|
+
it('should ignore too many dots when globbing') do
|
93
|
+
stub_fs([ '/c/file1.x.css' ])
|
94
|
+
m(css_config(:files => ['file1*'])).assets.collect{|a| a.absolute_path}.should == []
|
95
|
+
end
|
96
|
+
|
97
|
+
it('should pick files with :extra_suffix when globbing') do
|
98
|
+
stub_fs([ '/c/file1.e.css', '/c/file2.css' ])
|
99
|
+
m(css_config(:files => ['file*'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file1.e.css" ]
|
100
|
+
end
|
101
|
+
|
102
|
+
it('should pick files with :optional_suffix when globbing') do
|
103
|
+
stub_fs([ '/c/file.css', '/c/file.css.o' ])
|
104
|
+
m(css_config(:optional_suffix => 'o', :files => ['file*'])).assets.collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file.css.o" ]
|
105
|
+
end
|
106
|
+
|
107
|
+
it('should pick files with both :extra_suffix and :optional_suffix when globbing') do
|
108
|
+
stub_fs([ '/c/file.css', '/c/file.e.css', '/c/file.e.css.o' ])
|
109
|
+
m(css_config(:optional_suffix => 'o', :files => ['file*'])).assets_with_extra_suffix('e').collect{|a| a.absolute_path}.should == [ "#{prefix}/c/file.e.css.o" ]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe('#contents') do
|
114
|
+
it('should return an IO object') do
|
115
|
+
stub_fs([ '/c/file1.css', '/c/file2.css' ])
|
116
|
+
m(css_config(:files => ['file*'])).contents.should(respond_to(:read))
|
117
|
+
end
|
118
|
+
|
119
|
+
it('should concatenate individual file contents') do
|
120
|
+
stub_fs([ '/c/file1.css', '/c/file2.css' ])
|
121
|
+
m(css_config(:files => ['file*'])).contents.read.should == "/c/file1.css\n/c/file2.css\n"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe('#cache_asset') do
|
126
|
+
it('should use options[:cache]') do
|
127
|
+
m(css_config).cache_asset.absolute_path.should == "#{prefix}/c/cache.css"
|
128
|
+
end
|
129
|
+
|
130
|
+
it('should use :format if set') do
|
131
|
+
m(css_config).cache_asset(:e).absolute_path.should == "#{prefix}/c/cache.e.css"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe('#write_cache') do
|
136
|
+
it('should write to cache.css') do
|
137
|
+
File.should_receive(:open).with("#{prefix}/c/cache.css", 'w')
|
138
|
+
m(css_config).write_cache
|
139
|
+
end
|
140
|
+
|
141
|
+
it('should write cache contents to cache') do
|
142
|
+
stub_fs([ '/c/file1.css', '/c/file2.css' ])
|
143
|
+
m(css_config(:files => ['file*'])).write_cache
|
144
|
+
File.open("#{prefix}/c/cache.css") { |f| f.read.should == "/c/file1.css\n/c/file2.css\n" }
|
145
|
+
end
|
146
|
+
|
147
|
+
it('should use :format to determine CSS output file') do
|
148
|
+
File.should_receive(:open).with("#{prefix}/c/cache.e.css", 'w')
|
149
|
+
m(css_config).write_cache(:e)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe('#write_all_caches') do
|
154
|
+
it('should write cache.css (no :format)') do
|
155
|
+
File.should_receive(:open).with("#{prefix}/c/cache.css", 'w')
|
156
|
+
m(css_config).write_all_caches
|
157
|
+
end
|
158
|
+
|
159
|
+
it('should write no-format and all format files') do
|
160
|
+
formats = { :e1 => [], :e2 => [] }
|
161
|
+
File.should_receive(:open).with("#{prefix}/c/cache.css", 'w')
|
162
|
+
formats.keys.each do |format|
|
163
|
+
File.should_receive(:open).with("#{prefix}/c/cache.#{format}.css", 'w')
|
164
|
+
end
|
165
|
+
m(css_config(:formats => formats)).write_all_caches
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def m(config)
|
172
|
+
AssetLibrary::AssetModule.new(config)
|
173
|
+
end
|
174
|
+
|
175
|
+
def js_config(options = {})
|
176
|
+
{
|
177
|
+
:cache => 'cache',
|
178
|
+
:base => 'j',
|
179
|
+
:suffix => 'js',
|
180
|
+
:files => [ 'file1', 'file2' ]
|
181
|
+
}.merge(options)
|
182
|
+
end
|
183
|
+
|
184
|
+
def css_config(options = {})
|
185
|
+
{
|
186
|
+
:cache => 'cache',
|
187
|
+
:base => 'c',
|
188
|
+
:suffix => 'css',
|
189
|
+
:files => [ 'file1', 'file2' ]
|
190
|
+
}.merge(options)
|
191
|
+
end
|
192
|
+
|
193
|
+
def prefix
|
194
|
+
@prefix ||= File.dirname(__FILE__) + '/deleteme'
|
195
|
+
end
|
196
|
+
|
197
|
+
def stub_fs(filenames)
|
198
|
+
wipe_fs
|
199
|
+
FileUtils.mkdir(prefix)
|
200
|
+
|
201
|
+
filenames.each do |file|
|
202
|
+
path = File.join(prefix, file)
|
203
|
+
dir = File.dirname(path)
|
204
|
+
unless File.exist?(dir)
|
205
|
+
FileUtils.mkdir_p(dir)
|
206
|
+
end
|
207
|
+
File.open(path, 'w') { |f| f.write("#{file}\n") }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def wipe_fs
|
212
|
+
if File.exist?(prefix)
|
213
|
+
FileUtils.rm_r(prefix)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../../lib/asset_library/asset'
|
4
|
+
|
5
|
+
describe(AssetLibrary::Asset) do
|
6
|
+
it('should have eql? work for identical assets') do
|
7
|
+
a('/a/b.css').should eql(a('/a/b.css'))
|
8
|
+
end
|
9
|
+
|
10
|
+
it('should use absolute_path as its hash') do
|
11
|
+
a('/a/b.css').hash.should == '/a/b.css'.hash
|
12
|
+
end
|
13
|
+
|
14
|
+
context('#relative_path') do
|
15
|
+
it('should strip AssetLibrary.root') do
|
16
|
+
AssetLibrary.stub!(:root).and_return('/r')
|
17
|
+
a('/r/a/b.css').relative_path.should == '/a/b.css'
|
18
|
+
end
|
19
|
+
|
20
|
+
it('should strip nothing if root is "/"') do
|
21
|
+
AssetLibrary.stub!(:root).and_return('/')
|
22
|
+
a('/r/a/b.css').relative_path.should == '/r/a/b.css'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context('#timestamp') do
|
27
|
+
it('should return the file mtime') do
|
28
|
+
File.stub!(:mtime).with('/r/a/b.css').and_return(Time.at(123))
|
29
|
+
a('/r/a/b.css').timestamp.should == Time.at(123)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context('#relative_url') do
|
34
|
+
before(:each) do
|
35
|
+
AssetLibrary.stub!(:root).and_return('/r')
|
36
|
+
end
|
37
|
+
|
38
|
+
it('should use relative path and mtime') do
|
39
|
+
File.stub!(:mtime).with('/r/a/b.css').and_return(Time.at(123))
|
40
|
+
a('/r/a/b.css').relative_url.should == '/a/b.css?123'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def a(*args)
|
47
|
+
AssetLibrary::Asset.new(*args)
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../../lib/asset_library/helpers'
|
4
|
+
|
5
|
+
describe(AssetLibrary::Helpers) do
|
6
|
+
before(:each) do
|
7
|
+
@h = nil
|
8
|
+
AssetLibrary.stub!(:root).and_return('/')
|
9
|
+
end
|
10
|
+
before(:each) do
|
11
|
+
@old_cache = AssetLibrary.cache # Empty globals
|
12
|
+
end
|
13
|
+
|
14
|
+
after(:each) do
|
15
|
+
@h = nil
|
16
|
+
end
|
17
|
+
after(:each) do
|
18
|
+
AssetLibrary.cache = @old_cache
|
19
|
+
@old_cache = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
describe('#asset_library_javascript_tags') do
|
23
|
+
describe('when not caching') do
|
24
|
+
before(:each) do
|
25
|
+
AssetLibrary.stub!(:cache).and_return(false)
|
26
|
+
end
|
27
|
+
|
28
|
+
it('should fetch using asset_module') do
|
29
|
+
m = mock(:assets => [])
|
30
|
+
AssetLibrary.should_receive(:asset_module).with(:m).and_return(m)
|
31
|
+
h.asset_library_javascript_tags(:m)
|
32
|
+
end
|
33
|
+
|
34
|
+
it('should output nothing when a module is empty') do
|
35
|
+
m = mock(:assets => [])
|
36
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
37
|
+
h.asset_library_javascript_tags(:m).should == ''
|
38
|
+
end
|
39
|
+
|
40
|
+
it('should output a <script> tag for a file') do
|
41
|
+
m = mock(:assets => [a('/f.js')])
|
42
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
43
|
+
h.asset_library_javascript_tags(:m).should == '<script type="text/javascript" src="/f.js?123"></script>'
|
44
|
+
end
|
45
|
+
|
46
|
+
it('should join <script> tags with newlines') do
|
47
|
+
m = mock(:assets => [a('/f.js'), a('/f2.js')])
|
48
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
49
|
+
h.asset_library_javascript_tags(:m).should == '<script type="text/javascript" src="/f.js?123"></script>' + "\n" + '<script type="text/javascript" src="/f2.js?123"></script>'
|
50
|
+
end
|
51
|
+
|
52
|
+
it('should use compute_asset_host if available') do
|
53
|
+
m = mock(:assets => [a('/f.js')])
|
54
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
55
|
+
h.should_receive(:compute_asset_host).with('/f.js?123').and_return('http://assets.test')
|
56
|
+
h.asset_library_javascript_tags(:m).should =~ %r{"http://assets.test/f.js\?123"}
|
57
|
+
end
|
58
|
+
|
59
|
+
it('should not use compute_asset_host if it returns nil') do
|
60
|
+
m = mock(:assets => [a('/f.js')])
|
61
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
62
|
+
h.should_receive(:compute_asset_host).and_return(nil)
|
63
|
+
h.asset_library_javascript_tags(:m).should =~ %r{"/f.js\?123"}
|
64
|
+
end
|
65
|
+
|
66
|
+
it('should not use compute_asset_host if it returns ""') do
|
67
|
+
m = mock(:assets => [a('/f.js')])
|
68
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
69
|
+
h.should_receive(:compute_asset_host).and_return("")
|
70
|
+
h.asset_library_javascript_tags(:m).should =~ %r{"/f.js\?123"}
|
71
|
+
end
|
72
|
+
|
73
|
+
it('should add request protocol to compute_asset_host output if applicable') do
|
74
|
+
m = mock(:assets => [a('/f.js')])
|
75
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
76
|
+
h.stub!(:compute_asset_host).and_return('assets.test')
|
77
|
+
h.instance_variable_set(:@controller, mock(:request => mock(:protocol => 'http://')))
|
78
|
+
h.asset_library_javascript_tags(:m).should =~ %r{"http://assets.test/f.js\?123"}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe('when caching') do
|
83
|
+
before(:each) do
|
84
|
+
AssetLibrary.cache = true
|
85
|
+
end
|
86
|
+
|
87
|
+
it('should output a single <script> tag with the cache filename') do
|
88
|
+
m = mock(:cache_asset => a('/cache.js'))
|
89
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
90
|
+
h.asset_library_javascript_tags(:m).should == '<script type="text/javascript" src="/cache.js?123"></script>'
|
91
|
+
end
|
92
|
+
|
93
|
+
it('should use compute_asset_host if available') do
|
94
|
+
m = mock(:cache_asset => a('/cache.js'))
|
95
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
96
|
+
h.should_receive(:compute_asset_host).with('/cache.js?123').and_return('http://assets.test')
|
97
|
+
h.asset_library_javascript_tags(:m)
|
98
|
+
h.asset_library_javascript_tags(:m).should =~ %r{"http://assets.test/cache.js\?123"}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe('#asset_library_stylesheet_tags') do
|
104
|
+
describe('when not caching') do
|
105
|
+
before(:each) do
|
106
|
+
AssetLibrary.stub!(:cache).and_return(false)
|
107
|
+
end
|
108
|
+
|
109
|
+
it('should fetch using asset_module') do
|
110
|
+
m = mock(:assets => [])
|
111
|
+
AssetLibrary.should_receive(:asset_module).with(:m).and_return(m)
|
112
|
+
h.asset_library_stylesheet_tags(:m)
|
113
|
+
end
|
114
|
+
|
115
|
+
it('should output nothing when a module is empty') do
|
116
|
+
m = mock(:assets => [])
|
117
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
118
|
+
h.asset_library_stylesheet_tags(:m).should == ''
|
119
|
+
end
|
120
|
+
|
121
|
+
it('should output a single <script> with a single @import when there is one file') do
|
122
|
+
m = mock(:assets => [a('/f.css')])
|
123
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
124
|
+
h.asset_library_stylesheet_tags(:m).should == "<style type=\"text/css\">\n@import \"\/f.css?123\";\n</style>"
|
125
|
+
end
|
126
|
+
|
127
|
+
it('should use formats to find cache filename') do
|
128
|
+
m = mock
|
129
|
+
m.should_receive(:assets).with(:e).and_return([a('f.e.css')])
|
130
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
131
|
+
h.asset_library_stylesheet_tags(:m, :e).should == "<style type=\"text/css\">\n@import \"f.e.css?123\";\n</style>"
|
132
|
+
end
|
133
|
+
|
134
|
+
it('should output a single <script> tag with 30 @import') do
|
135
|
+
m = mock(:assets => (1..30).collect{|i| a("/f#{i}.css") })
|
136
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
137
|
+
h.asset_library_stylesheet_tags(:m).should =~ /\<style type=\"text\/css\"\>(\n@import \"\/f\d+.css\?123\";){30}\n\<\/style\>/
|
138
|
+
end
|
139
|
+
|
140
|
+
it('should output two <script> tags with 31 @imports') do
|
141
|
+
m = mock(:assets => (1..31).collect{|i| a("/f#{i}.css") })
|
142
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
143
|
+
h.asset_library_stylesheet_tags(:m).should =~ /\<style type="text\/css"\>(\n@import "\/f\d+.css\?123";){30}\n\<\/style\>\n<style type="text\/css"\>\n@import "\/f31.css\?123";\n\<\/style\>/
|
144
|
+
end
|
145
|
+
|
146
|
+
it('should output a final hash in the parameters as html attributes') do
|
147
|
+
m = mock(:assets => [a('/f.css')])
|
148
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
149
|
+
optional_hash = {:key1 => "val1", :key2 => "val2", :key3 => "val3"}
|
150
|
+
attributes_to_hash( h.asset_library_stylesheet_tags(:m, optional_hash), [:type] ).should == optional_hash
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe('when caching') do
|
155
|
+
before(:each) do
|
156
|
+
AssetLibrary.stub!(:cache).and_return(true)
|
157
|
+
end
|
158
|
+
|
159
|
+
it('should output a single <style> tag with the cache filename') do
|
160
|
+
m = mock(:cache_asset => a('/cache.css'))
|
161
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
162
|
+
h.asset_library_stylesheet_tags(:m).should == '<link rel="stylesheet" type="text/css" href="/cache.css?123" />'
|
163
|
+
end
|
164
|
+
|
165
|
+
it('should use format for the cache filename') do
|
166
|
+
m = mock
|
167
|
+
m.should_receive(:cache_asset).with(:e).and_return(a('/cache.e.css'))
|
168
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
169
|
+
h.asset_library_stylesheet_tags(:m, :e).should == '<link rel="stylesheet" type="text/css" href="/cache.e.css?123" />'
|
170
|
+
end
|
171
|
+
|
172
|
+
it('should output a final hash in the parameters as html attributes') do
|
173
|
+
m = mock(:cache_asset => a('/cache.css'))
|
174
|
+
AssetLibrary.stub!(:asset_module).and_return(m)
|
175
|
+
optional_hash = {:key1 => "val1", :key2 => "val2", :key3 => "val3"}
|
176
|
+
attributes_to_hash(h.asset_library_stylesheet_tags(:m, optional_hash), [:type, :rel, :href]).should == optional_hash
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def a(path)
|
184
|
+
File.stub!(:mtime).and_return(Time.at(123))
|
185
|
+
AssetLibrary::Asset.new(path)
|
186
|
+
end
|
187
|
+
|
188
|
+
def h
|
189
|
+
return @h if @h
|
190
|
+
c = Class.new do
|
191
|
+
include AssetLibrary::Helpers
|
192
|
+
end
|
193
|
+
@h = c.new
|
194
|
+
@h.stub!(:request).and_return(mock(:protocol => 'http://', :host_with_port => 'example.com'))
|
195
|
+
@h
|
196
|
+
end
|
197
|
+
|
198
|
+
def attributes_to_hash(string, without = [])
|
199
|
+
hash_from_tag_attributes = {}
|
200
|
+
|
201
|
+
string.scan(/\s([^\s=]+="[^"]*)"/).each do |attr|
|
202
|
+
a = attr[0].split("=\"")
|
203
|
+
hash_from_tag_attributes.merge!( a[0].to_sym => a[1] )
|
204
|
+
end
|
205
|
+
|
206
|
+
without.each{|k| hash_from_tag_attributes.delete k}
|
207
|
+
|
208
|
+
hash_from_tag_attributes
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe(AssetLibrary) do
|
4
|
+
before(:each) do
|
5
|
+
@old_root = AssetLibrary.root
|
6
|
+
@old_config_path = AssetLibrary.config_path
|
7
|
+
@old_cache = AssetLibrary.cache
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:each) do
|
11
|
+
AssetLibrary.root = @old_root
|
12
|
+
AssetLibrary.config_path = @old_config_path
|
13
|
+
AssetLibrary.cache = @old_cache
|
14
|
+
end
|
15
|
+
|
16
|
+
describe('#config') do
|
17
|
+
it('should YAML.load_file the config from config_path') do
|
18
|
+
AssetLibrary.config_path = '/config.yml'
|
19
|
+
File.stub!(:exist?).with('/config.yml').and_return(true)
|
20
|
+
YAML.should_receive(:load_file).with('/config.yml')
|
21
|
+
AssetLibrary.config
|
22
|
+
end
|
23
|
+
|
24
|
+
it('should return {} if config_path does not exist') do
|
25
|
+
AssetLibrary.config_path = '/config.yml'
|
26
|
+
File.stub!(:exist?).with('/config.yml').and_return(false)
|
27
|
+
AssetLibrary.config.should == {}
|
28
|
+
end
|
29
|
+
|
30
|
+
it('should cache config if cache is set') do
|
31
|
+
AssetLibrary.cache = true
|
32
|
+
AssetLibrary.config_path = '/config.yml'
|
33
|
+
|
34
|
+
File.stub!(:exist?).with('/config.yml').and_return(true)
|
35
|
+
YAML.should_receive(:load_file).with('/config.yml').once
|
36
|
+
|
37
|
+
AssetLibrary.config
|
38
|
+
AssetLibrary.config
|
39
|
+
end
|
40
|
+
|
41
|
+
it('should not cache config if cache is not set') do
|
42
|
+
AssetLibrary.cache = false
|
43
|
+
AssetLibrary.config_path = '/config.yml'
|
44
|
+
|
45
|
+
File.stub!(:exist?).with('/config.yml').and_return(true)
|
46
|
+
YAML.should_receive(:load_file).with('/config.yml').twice
|
47
|
+
|
48
|
+
AssetLibrary.config
|
49
|
+
AssetLibrary.config
|
50
|
+
end
|
51
|
+
|
52
|
+
it('should symbolize config hash keys') do
|
53
|
+
AssetLibrary.cache = false
|
54
|
+
AssetLibrary.config_path = '/config.yml'
|
55
|
+
|
56
|
+
File.stub!(:exist?).with('/config.yml').and_return(true)
|
57
|
+
YAML.should_receive(:load_file).with('/config.yml').and_return(
|
58
|
+
{ 'a' => { 'b' => 'c' } }
|
59
|
+
)
|
60
|
+
|
61
|
+
AssetLibrary.config.should == { :a => { :b => 'c' } }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe('#asset_module') do
|
66
|
+
before(:each) do
|
67
|
+
@config = {}
|
68
|
+
AssetLibrary.stub!(:config).and_return(@config)
|
69
|
+
end
|
70
|
+
|
71
|
+
it('should return nil when given an invalid key') do
|
72
|
+
AssetLibrary.asset_module(:foo).should == nil
|
73
|
+
end
|
74
|
+
|
75
|
+
it('should return an AssetModule when given a valid key') do
|
76
|
+
@config[:foo] = {}
|
77
|
+
AssetLibrary.asset_module(:foo).should(be_a(AssetLibrary::AssetModule))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe('#write_all_caches') do
|
82
|
+
it('should call write_all_caches on all asset_modules') do
|
83
|
+
mock1 = mock
|
84
|
+
mock2 = mock
|
85
|
+
|
86
|
+
mock1.should_receive(:write_all_caches)
|
87
|
+
mock2.should_receive(:write_all_caches)
|
88
|
+
|
89
|
+
AssetLibrary.stub!(:asset_module).with(:mock1).and_return(mock1)
|
90
|
+
AssetLibrary.stub!(:asset_module).with(:mock2).and_return(mock2)
|
91
|
+
|
92
|
+
AssetLibrary.stub!(:config).and_return({ :mock1 => {}, :mock2 => {} })
|
93
|
+
|
94
|
+
AssetLibrary.write_all_caches
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: asset_library
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- adamh
|
8
|
+
- alegscogs
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-02-09 00:00:00 -05:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: adamh-glob_fu
|
18
|
+
type: :runtime
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.4
|
25
|
+
version:
|
26
|
+
description:
|
27
|
+
email: adam@adamhooper.com
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- README.rdoc
|
34
|
+
files:
|
35
|
+
- lib/asset_library.rb
|
36
|
+
- lib/asset_library/asset.rb
|
37
|
+
- lib/asset_library/asset_module.rb
|
38
|
+
- lib/asset_library/helpers.rb
|
39
|
+
- lib/asset_library/rake_tasks.rb
|
40
|
+
- lib/asset_library/util.rb
|
41
|
+
- rails/init.rb
|
42
|
+
- README.rdoc
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/adamh/asset_library
|
45
|
+
licenses: []
|
46
|
+
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --charset=UTF-8
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.5
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Manage and bundle CSS and JavaScript files
|
71
|
+
test_files:
|
72
|
+
- spec/asset_library_spec.rb
|
73
|
+
- spec/asset_library/asset_module_spec.rb
|
74
|
+
- spec/asset_library/asset_spec.rb
|
75
|
+
- spec/asset_library/helpers_spec.rb
|
76
|
+
- spec/spec_helper.rb
|