lash 0.1.1 → 1.0.1
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/.autotest +10 -0
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/Gemfile +2 -1
- data/LICENSE.md +48 -0
- data/README.md +117 -5
- data/Rakefile +1 -0
- data/lib/lash.rb +17 -4
- data/lib/lash/assets_host.rb +74 -0
- data/lib/lash/bundle_helper.rb +6 -24
- data/lib/lash/capistrano.rb +2 -0
- data/lib/lash/closure_minifier.rb +20 -0
- data/lib/lash/files.rb +56 -0
- data/lib/lash/java_script_bundler.rb +117 -0
- data/lib/lash/java_script_minifier.rb +67 -0
- data/lib/lash/railtie.rb +4 -33
- data/lib/lash/sprite_bundler.rb +132 -0
- data/lib/lash/tasks/lash.rake +50 -181
- data/lib/lash/version.rb +1 -1
- data/spec/lib/assets_host_spec.rb +56 -0
- data/spec/lib/bundle_helper_spec.rb +99 -0
- data/spec/lib/closure_minifier_spec.rb +67 -0
- data/spec/lib/java_script_bundler_spec.rb +46 -0
- data/spec/lib/lash_files_spec.rb +68 -0
- data/spec/spec_helper.rb +15 -25
- data/spec/support/rails_fake.rb +31 -0
- data/spec/test_app/.gitignore +4 -0
- data/spec/test_app/Rakefile +8 -0
- data/spec/test_app/public/images/rails.png +0 -0
- data/spec/test_app/public/images/ui-sprite.png +0 -0
- data/spec/test_app/public/images/ui-sprite.png.sprite +3 -0
- data/spec/test_app/public/javascripts/application/README +0 -0
- data/spec/test_app/public/javascripts/application/application.bundleVersion.js +1 -0
- data/spec/test_app/public/javascripts/application/application.js +3 -0
- data/spec/test_app/public/javascripts/application/application2.js +3 -0
- data/spec/test_app/public/javascripts/application/tools/validate.js +3 -0
- data/spec/test_app/public/javascripts/bundle_application.js +1 -0
- data/spec/test_app/public/javascripts/bundle_application.js.gz +0 -0
- data/spec/test_app/public/javascripts/cdn/jquery.js +1 -0
- data/spec/test_app/public/javascripts/cdn/jquery.js.gz +0 -0
- data/spec/test_app/public/javascripts/cdn/jquery.min.js +1 -0
- data/spec/test_app/public/javascripts/cdn/jquery.min.js.gz +0 -0
- data/spec/test_app/public/javascripts/demand/huge.js +0 -0
- data/spec/test_app/public/javascripts/demand/huge.js.gz +0 -0
- data/spec/test_app/public/javascripts/demand/huge.min.js +1 -0
- data/spec/test_app/public/javascripts/demand/huge.min.js.gz +0 -0
- data/spec/test_app/public/sprites/ui/accept.png +0 -0
- data/spec/test_app/public/sprites/ui/add.png +0 -0
- data/spec/test_app/public/stylesheets/.gitkeep +0 -0
- data/spec/test_app/public/stylesheets/sass/_ui-sprite.scss +20 -0
- data/spec/test_app/public/stylesheets/sass/_version.scss +1 -0
- metadata +81 -10
data/.autotest
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
Autotest.add_hook(:initialize) do |at|
|
2
|
+
at.clear_mappings
|
3
|
+
at.add_mapping(%r%^lib/(.*)\.rb$%) { |_, m|
|
4
|
+
["spec/lib/#{m[1]}_spec.rb"]
|
5
|
+
}
|
6
|
+
at.add_mapping(%r%^spec/(models|controllers|routing|views|helpers|mailers|requests|lib)/.*rb$%) { |filename, _|
|
7
|
+
filename
|
8
|
+
}
|
9
|
+
nil
|
10
|
+
end
|
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --debug
|
data/Gemfile
CHANGED
data/LICENSE.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# License Notices
|
2
|
+
|
3
|
+
## Lash
|
4
|
+
|
5
|
+
Copyright (C) 2011 Apps In Your Pants.
|
6
|
+
|
7
|
+
Dual licensed under MIT and GPLv3
|
8
|
+
|
9
|
+
|
10
|
+
## Google Closure Compiler
|
11
|
+
|
12
|
+
Copyright 2009 The Closure Compiler Authors.
|
13
|
+
|
14
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
15
|
+
you may not use this file except in compliance with the License.
|
16
|
+
You may obtain a copy of the License at
|
17
|
+
|
18
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
19
|
+
|
20
|
+
Unless required by applicable law or agreed to in writing, software
|
21
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
22
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
23
|
+
See the License for the specific language governing permissions and
|
24
|
+
limitations under the License.
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
## Optipng
|
29
|
+
|
30
|
+
Copyright (C) 2001-2011 Cosmin Truta.
|
31
|
+
|
32
|
+
This software is provided 'as-is', without any express or implied
|
33
|
+
warranty. In no event will the author(s) be held liable for any damages
|
34
|
+
arising from the use of this software.
|
35
|
+
|
36
|
+
Permission is granted to anyone to use this software for any purpose,
|
37
|
+
including commercial applications, and to alter it and redistribute it
|
38
|
+
freely, subject to the following restrictions:
|
39
|
+
|
40
|
+
1. The origin of this software must not be misrepresented; you must not
|
41
|
+
claim that you wrote the original software. If you use this software
|
42
|
+
in a product, an acknowledgment in the product documentation would be
|
43
|
+
appreciated but is not required.
|
44
|
+
|
45
|
+
2. Altered source versions must be plainly marked as such, and must not
|
46
|
+
be misrepresented as being the original software.
|
47
|
+
|
48
|
+
3. This notice may not be removed or altered from any source distribution.
|
data/README.md
CHANGED
@@ -15,10 +15,10 @@ Based on ["Optimizing asset bundling and serving with Rails"](https://github.com
|
|
15
15
|
|
16
16
|
# Gemfile
|
17
17
|
gem 'lash'
|
18
|
-
|
19
|
-
or as a plugin
|
20
18
|
|
21
|
-
|
19
|
+
|
20
|
+
|
21
|
+
|
22
22
|
|
23
23
|
## Bundling Assets
|
24
24
|
|
@@ -29,6 +29,10 @@ Lash includes several rake tasks to bundle loose, development versions of your s
|
|
29
29
|
|
30
30
|
Individual assets can be bundled on demand using their respective `lash:asset_type` tasks.
|
31
31
|
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
|
32
36
|
## JavaScript
|
33
37
|
|
34
38
|
Lash expects to find JavaScript assets as subfolders of the public/javascripts folder like so
|
@@ -60,6 +64,19 @@ __public/javascripts/demand__
|
|
60
64
|
|
61
65
|
### CDN JavaScripts and Developer Mode
|
62
66
|
|
67
|
+
Shared CDN hosts are great for optimizing your site's user experience but there are two issues that developers regularly have to deal with - developing offline and accounting for CDN unavailability. Lash handles both of these cases with ease.
|
68
|
+
|
69
|
+
During development, Lash will use the copies of the libraries found in your public/javascripts/cdn directory. This makes it easy to work offline.
|
70
|
+
|
71
|
+
In production, Lash will use the CDN versions of the scripts and if a runtime test is provided, fallback to the local version if the CDN is unavailable.
|
72
|
+
|
73
|
+
<%= javascript_cdn 'jquery', '//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js', 'jQuery' -%>
|
74
|
+
|
75
|
+
# => <script src="//ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" type="text/javascript"></script>
|
76
|
+
# => <script type="text/javascript">
|
77
|
+
# => if(typeof jQuery === "undefined" )
|
78
|
+
# => document.write( unescape("<script src=\"/javascripts/cdn/jquery.min.js?1297459967\" type=\"text/javascript\"><\/script>") );
|
79
|
+
# => </script>
|
63
80
|
|
64
81
|
### Referencing JavaScript Bundles in Layouts
|
65
82
|
|
@@ -72,9 +89,15 @@ When {Lash::BundleHelper#bundle_files?} is true (on by default in production), l
|
|
72
89
|
|
73
90
|
See {Lash::BundleHelper#javascript_bundle} for details.
|
74
91
|
|
92
|
+
### application.bundleVersion.js
|
75
93
|
|
94
|
+
When referring to assets in your javascripts you loose the convenience of the rails cache busting asset tags. During bundling, lash will generate an `public/javascripts/application/application.bundleVersion.js` which gets bundled into the application script. This script simply declares a global variable `bundleVersion` which you can append to asset urls in your scripts.
|
76
95
|
|
77
|
-
####
|
96
|
+
#### Example
|
97
|
+
|
98
|
+
$('#waiting-div').append( $('<img src="/images/wainting.gif?' + bundleVersion + '" />' ) )
|
99
|
+
|
100
|
+
### JavaScript bundling tasks
|
78
101
|
|
79
102
|
rake lash:js # Bundles and minifies javascripts
|
80
103
|
rake lash:js_bundle # Bundles javascripts folders into single minified files
|
@@ -84,8 +107,97 @@ See {Lash::BundleHelper#javascript_bundle} for details.
|
|
84
107
|
|
85
108
|
|
86
109
|
|
110
|
+
|
111
|
+
|
87
112
|
## CSS Sprites
|
88
113
|
|
114
|
+
CSS sprites is are an effective optimization technique, but can be very time consuming to produce. Lash makes it easy. Just drop the desired images into a folder, run a rake task and you've got a highly optimized sprite image and accompanying CSS file.
|
115
|
+
|
116
|
+
Lash looks for sprite images in `public/sprites`. Each sub folder will be bundled into a single image and css file. Given the following folder structure:
|
117
|
+
|
118
|
+
* public/sprites/ui
|
119
|
+
* accept.png
|
120
|
+
* add.png
|
121
|
+
|
122
|
+
Lash will generate a sprite image `public/images/ui-sprite.png` and corresponding css file `public/stylesheets/sass/_ui-sprite.scss`.
|
123
|
+
|
124
|
+
#### To generate your apps sprites
|
125
|
+
|
126
|
+
rake lash:sprites
|
127
|
+
|
128
|
+
|
129
|
+
### _version.scss
|
130
|
+
|
131
|
+
When referring to assets in your css scripts you loose the convenience of the rails cache busting asset tags. During bundling, lash will generate an `public/stylesheets/sass/_version.scss`. You can include this into any of your SASS scripts when you reference static assets like images.
|
132
|
+
|
133
|
+
#### Example
|
134
|
+
|
135
|
+
@import 'version';
|
136
|
+
.smiley { background-image: url(/images/smiley.png?#{$bundle-version})}
|
137
|
+
|
138
|
+
### CSS bundling tasks
|
139
|
+
|
140
|
+
rake lash:css # Process CSS scripts
|
141
|
+
rake lash:css_gzip # Compresses stylesheets for use with nginx gzip_static
|
142
|
+
rake lash:sass # Pre-generate sass scripts
|
143
|
+
rake lash:sprites # Generate CSS sprites from the public/sprites folders
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
## Optimizing PNG Images
|
150
|
+
|
151
|
+
Most image editors will compress PNG files using a very basic compression algorithm. However the PNG format
|
152
|
+
allows for much more aggressive optimization at the cost of speed. Since image resources are some of the
|
153
|
+
heaviest assets downloaded from your site, optimizing them is often worth the effort.
|
154
|
+
|
155
|
+
See [A guide to PNG optimization](http://optipng.sourceforge.net/pngtech/optipng.html) for a more detailed discussion.
|
156
|
+
|
157
|
+
#### To optimize your pngs
|
158
|
+
|
159
|
+
rake lash:png
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
|
89
165
|
## Integrating With Capistrano
|
90
166
|
|
91
|
-
|
167
|
+
Lash was designed to work with capistrano to easily run static asset bundling during the deployment process. This makes
|
168
|
+
sure that all assets are primed for static serving from your website without interfering with any existing requests
|
169
|
+
that are currently being served.
|
170
|
+
|
171
|
+
#### To run bundling tasks during deployment
|
172
|
+
|
173
|
+
# in config/deploy.rb
|
174
|
+
require 'lash/capistrano'
|
175
|
+
|
176
|
+
If you use capistrano to publish your app (and really who isn't?) you'll want to add some additional filters to your .gitignore file
|
177
|
+
|
178
|
+
# lash asset helpers
|
179
|
+
public/javascripts/common/application.bundleVersion.js
|
180
|
+
public/stylesheets/sass/_version.scss
|
181
|
+
|
182
|
+
# lash generated filed
|
183
|
+
public/javascripts/bundle_*.js
|
184
|
+
public/stylesheets/*.css
|
185
|
+
public/stylesheets/sass/_*-sprite.scss
|
186
|
+
public/javascripts/**/*.min.js
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
|
193
|
+
|
194
|
+
|
195
|
+
## License
|
196
|
+
|
197
|
+
### Lash
|
198
|
+
|
199
|
+
Copyright (C) 2011 Apps In Your Pants.
|
200
|
+
|
201
|
+
Dual licensed under MIT and GPLv3
|
202
|
+
|
203
|
+
[Other License Notices](LICENSE.md)
|
data/Rakefile
CHANGED
data/lib/lash.rb
CHANGED
@@ -3,17 +3,30 @@ module Lash
|
|
3
3
|
# from the Lash::ViewHelpers.lash_options hash. You can write
|
4
4
|
# to this hash to override default options on the global level:
|
5
5
|
#
|
6
|
-
#
|
6
|
+
# Lash.lash_options[:unicorns] = 'run free'
|
7
7
|
#
|
8
8
|
def self.lash_options() @lash_options; end
|
9
|
+
|
9
10
|
# Overrides the default {lash_options}
|
10
|
-
|
11
|
+
#
|
12
|
+
# @option options [String] :use_asset_servers Use asset server subdomain hack to allow multiple simultaenous connections. See {Lash::AssetsHost.resolve_static_asset_server_for_source}.
|
13
|
+
# @option options [String] :use_asset_servers_in_ssl Use asset server subdomain hack even over SSL. Requires a wildcard certificate.
|
14
|
+
# @option options [String] :use_sass Support SASS
|
15
|
+
# @option options [String] :use_git_asset_id Use GIT repro id for cachebusting asset id
|
16
|
+
# @option options [String] :closure_compiler Path to custom version of google's closure compiler.
|
17
|
+
def self.lash_options=(options) @lash_options = options; end
|
11
18
|
|
12
19
|
self.lash_options = {
|
13
20
|
:use_asset_servers => true,
|
14
21
|
:use_asset_servers_in_ssl => true,
|
15
|
-
:use_sass => Gem.available?('sass')
|
22
|
+
:use_sass => Gem.available?('sass'),
|
23
|
+
:use_git_asset_id => true,
|
24
|
+
:closure_compiler => File.expand_path( "../../bin/closure-compiler/compiler.jar", __FILE__ )
|
16
25
|
}
|
17
26
|
end
|
18
27
|
|
19
|
-
require 'lash/railtie' if defined?(::Rails::Railtie)
|
28
|
+
require 'lash/railtie' if defined?(Rails) && Rails === Class and defined?(::Rails::Railtie)
|
29
|
+
require 'lash/java_script_bundler'
|
30
|
+
require 'lash/sprite_bundler'
|
31
|
+
require 'lash/files'
|
32
|
+
require 'lash/assets_host'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'grit'
|
2
|
+
require 'lash'
|
3
|
+
|
4
|
+
module Lash
|
5
|
+
module AssetsHost
|
6
|
+
|
7
|
+
# Configures Rails to use the most recent GIT repo id for public/(javascripts|stylesheets|images) for the
|
8
|
+
# asset id used for cache busting.
|
9
|
+
def self.use_git_asset_id
|
10
|
+
return unless Lash.lash_options[:use_git_asset_id]
|
11
|
+
|
12
|
+
repo = Grit::Repo.new( Rails.root.to_s )
|
13
|
+
|
14
|
+
ENV['RAILS_ASSET_ID'] = \
|
15
|
+
[:javascripts, :stylesheets, :images] \
|
16
|
+
.map { |folder| repo.log( 'master', "spec/test_app/public/#{folder}", :max_count => 1 ).first } \
|
17
|
+
.max_by { |log| log && log.committed_date }
|
18
|
+
.id
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generates an asset id for the given file used for cache-busting
|
22
|
+
def self.asset_id( file )
|
23
|
+
if Lash.lash_options[:use_git_asset_id]
|
24
|
+
repo = Grit::Repo.new( Rails.root.to_s )
|
25
|
+
[:javascripts, :stylesheets, :images] \
|
26
|
+
.map { |folder| repo.log( 'master', "spec/test_app/public/#{folder}", :max_count => 1 ).first } \
|
27
|
+
.max_by { |log| log && log.committed_date }
|
28
|
+
.id
|
29
|
+
elsif ::File.exist?( file )
|
30
|
+
File.mtime( file ).to_i.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Method used to map an asset to a static asset server. This method simply generates a semi-random domain
|
35
|
+
# prefix based on the filename of the source. The asset server should resolve to the same server as
|
36
|
+
# the rails app. This is a basic browser hack to allow more than 4 connections to the server so that the
|
37
|
+
# browser can download multiple assets simultaneously.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# request.host # => lvh.me
|
41
|
+
# resolve_static_asset_server_for_source "smiles", request
|
42
|
+
# # => assets1.lvh.me
|
43
|
+
#
|
44
|
+
# # from terminal
|
45
|
+
# nslookup lvh.me # => 127.0.0.1
|
46
|
+
# nslookup assets1.lvh.me # => 127.0.0.1
|
47
|
+
|
48
|
+
def self.resolve_static_asset_server_for_source( source, request )
|
49
|
+
if /\/\// =~ source
|
50
|
+
nil
|
51
|
+
elsif request.ssl? and ! Lash.lash_options[:use_asset_servers_in_ssl]
|
52
|
+
nil
|
53
|
+
elsif !Lash.lash_options[:use_asset_servers]
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
|
57
|
+
# Change the host name to include a randomized asset name at the same domain
|
58
|
+
# level. This is required so that HTTPS requests can use a wildcard domain
|
59
|
+
# without using subject alt name.
|
60
|
+
|
61
|
+
host = request.host_with_port
|
62
|
+
parts = host.split( /\./ )
|
63
|
+
if parts.length > 2
|
64
|
+
parts[0] = "#{parts[0]}-assets#{source.hash % 4}"
|
65
|
+
else
|
66
|
+
parts.unshift "assets#{source.hash % 4}"
|
67
|
+
end
|
68
|
+
|
69
|
+
"http#{'s' if request.ssl?}://#{parts.join('.')}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
data/lib/lash/bundle_helper.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
|
-
require '
|
1
|
+
require 'lash/files'
|
2
2
|
|
3
3
|
module Lash
|
4
4
|
module BundleHelper
|
5
5
|
|
6
|
-
unloadable if Rails.env.development?
|
7
|
-
|
8
6
|
# Root folder where application javascript files can be found
|
9
7
|
def javascript_root
|
10
8
|
File.join( ::Rails.root, 'public', 'javascripts', '' )
|
11
9
|
end
|
12
10
|
|
13
11
|
# Determines if the processed bundle files should be used, or the loose files used to build those bundles.
|
14
|
-
# By default bundled
|
12
|
+
# By default bundled files are used in production or if the request includes a `:bundle` param or cookie.
|
13
|
+
# @return [Boolean] true if bundled assets should be used.
|
15
14
|
def bundle_files?
|
16
15
|
if params.has_key? :bundle
|
17
|
-
return
|
16
|
+
return /^t(rue)?|y(es)?|1$/i.match( params[:bundle] ) != nil
|
18
17
|
end
|
19
18
|
Rails.env.production? || cookies[:bundle] == "yes"
|
20
19
|
end
|
@@ -71,28 +70,11 @@ module Lash
|
|
71
70
|
end
|
72
71
|
|
73
72
|
private
|
74
|
-
# Collects an array of all files in `{basedir}` with the given `{ext}`.
|
75
|
-
def recursive_file_list( basedir, ext )
|
76
|
-
files = []
|
77
|
-
return files unless File.exist? basedir
|
78
|
-
Find.find( basedir ) do |path|
|
79
|
-
if FileTest.directory?( path )
|
80
|
-
if File.basename( path )[0] == ?. # Skip dot directories
|
81
|
-
Find.prune
|
82
|
-
else
|
83
|
-
next
|
84
|
-
end
|
85
|
-
end
|
86
|
-
files << path if File.extname( path ) == ext
|
87
|
-
end
|
88
|
-
files.sort
|
89
|
-
end
|
90
|
-
|
91
73
|
# Generates javscript include tags for the actual bundled javascript for each named bundle
|
92
74
|
def javascript_include_bundles( bundles )
|
93
75
|
output = ""
|
94
76
|
bundles.each do |bundle|
|
95
|
-
output << javascript_src_tag( "bundle_#{bundle}", {} )
|
77
|
+
output << javascript_src_tag( "bundle_#{bundle}.js", {} )
|
96
78
|
end
|
97
79
|
output.html_safe
|
98
80
|
end
|
@@ -101,7 +83,7 @@ module Lash
|
|
101
83
|
def javascript_include_files( bundles )
|
102
84
|
output = "\n"
|
103
85
|
bundles.each do |bundle|
|
104
|
-
files = recursive_file_list( File.join( javascript_root, bundle ), '.js' )
|
86
|
+
files = Lash::Files.recursive_file_list( File.join( javascript_root, bundle ), '.js' )
|
105
87
|
files.each do |file|
|
106
88
|
file = file.gsub( javascript_root, '' )
|
107
89
|
output << javascript_src_tag( file, {} ) + "\n"
|
data/lib/lash/capistrano.rb
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'lash/java_script_minifier'
|
2
|
+
|
3
|
+
module Lash
|
4
|
+
# Minifies a collection of scripts using Google's closure compiler.
|
5
|
+
class ClosureMinifier < JavaScriptMinifier
|
6
|
+
|
7
|
+
private
|
8
|
+
def minify_scripts( files, target )
|
9
|
+
tmp = "#{target}.tmp"
|
10
|
+
`java -jar \"#{Lash.lash_options[:closure_compiler]}\" #{command_options} --warning_level QUIET --js \"#{files.join("\" --js \"")}\" --js_output_file \"#{tmp}\"`
|
11
|
+
if $?.exitstatus == 0
|
12
|
+
File.delete target if File.exist? target
|
13
|
+
File.rename tmp, target
|
14
|
+
end
|
15
|
+
ensure
|
16
|
+
File.delete tmp if File.exist? tmp
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/lash/files.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'find'
|
2
|
+
|
3
|
+
module Lash
|
4
|
+
module Files
|
5
|
+
|
6
|
+
# Collects an array of all files in {basedir} with the given ext.
|
7
|
+
#
|
8
|
+
# @param [String] basedir the root directory to search
|
9
|
+
# @param [String] ext extension to filter results by
|
10
|
+
# @return [Array] an array of expanded paths for each file in {basedir} and any of it's sub directories
|
11
|
+
def self.recursive_file_list( basedir, ext )
|
12
|
+
unless ext.is_a? Regexp
|
13
|
+
ext = ".#{ext}" if ext && ext[0] != ?.
|
14
|
+
ext ||= ""
|
15
|
+
end
|
16
|
+
|
17
|
+
files = []
|
18
|
+
return files unless File.exist? basedir
|
19
|
+
Find.find( basedir ) do |path|
|
20
|
+
if FileTest.directory?( path )
|
21
|
+
if File.basename( path )[0] == ?. # Skip dot directories
|
22
|
+
Find.prune
|
23
|
+
else
|
24
|
+
next
|
25
|
+
end
|
26
|
+
end
|
27
|
+
if ext.is_a? Regexp
|
28
|
+
files << path if ext.match( path )
|
29
|
+
else
|
30
|
+
files << path if File.extname( path ) == ext
|
31
|
+
end
|
32
|
+
end
|
33
|
+
files.sort
|
34
|
+
end
|
35
|
+
|
36
|
+
# Gets all the top level directories in the given base_path
|
37
|
+
#
|
38
|
+
# @param [String] basedir the root directory to search
|
39
|
+
# @return [Array] an array of expanded paths for all directories found
|
40
|
+
def self.get_top_level_directories( basedir )
|
41
|
+
Dir.entries( basedir ).collect do |path|
|
42
|
+
path = File.join( basedir, path )
|
43
|
+
File.basename( path )[0] == ?. || !File.directory?( path ) ? nil : path # not dot directories or files
|
44
|
+
end - [nil]
|
45
|
+
end
|
46
|
+
|
47
|
+
# Gets the relative path from root to path of path is a subdirectory of root, otherwise returns path
|
48
|
+
def self.relative_to( path, root )
|
49
|
+
path = File.expand_path( path )
|
50
|
+
root = File.expand_path( root )
|
51
|
+
return path unless path.start_with? root
|
52
|
+
path[ root.length + 1, path.length ]
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|