paste 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +100 -0
- data/lib/paste.rb +59 -0
- data/lib/paste/capistrano.rb +26 -0
- data/lib/paste/css/base.rb +9 -0
- data/lib/paste/glue.rb +12 -0
- data/lib/paste/js/base.rb +26 -0
- data/lib/paste/js/cache.rb +81 -0
- data/lib/paste/js/chain.rb +47 -0
- data/lib/paste/js/compress.rb +52 -0
- data/lib/paste/js/erb_renderer.rb +55 -0
- data/lib/paste/js/unify.rb +37 -0
- data/lib/paste/parser/sprockets.rb +87 -0
- data/lib/paste/rails.rb +13 -0
- data/lib/paste/rails/helper.rb +53 -0
- data/lib/paste/rails/railtie.rb +36 -0
- data/lib/paste/rails/updater.rb +16 -0
- data/lib/paste/resolver.rb +53 -0
- data/lib/paste/tasks/paste.rake +6 -0
- metadata +134 -0
data/README.rdoc
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
Sprockets for Rails 3
|
2
|
+
=================
|
3
|
+
|
4
|
+
This gem lets you make use of [Sprockets](http://github.com/sstephenson/sprockets).
|
5
|
+
|
6
|
+
## Installing
|
7
|
+
|
8
|
+
In your Gemfile: `gem 'sprockets-packager'`
|
9
|
+
|
10
|
+
Then, `bundle install`
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
Read up on how [Sprockets](http://github.com/sstephenson/sprockets) works to start taking advantage of it.
|
14
|
+
|
15
|
+
Put all of your javascript files in `app/javascripts`. You can even make erb javascript files to interpolate constants and such.
|
16
|
+
|
17
|
+
Now in your views, whenever you need javascript, just call
|
18
|
+
|
19
|
+
<% include_sprocket 'foobar' %>
|
20
|
+
|
21
|
+
or
|
22
|
+
|
23
|
+
<% include_sprockets 'foo/bar', 'foo', 'baz' %>
|
24
|
+
|
25
|
+
And then in your layout file,
|
26
|
+
|
27
|
+
<%= sprockets_include_tag %>
|
28
|
+
|
29
|
+
And that's it! As long as you've got your dependencies in your JS managed all right, the sprockets themselves will be automatically generated for you
|
30
|
+
|
31
|
+
I would recommend you add `public/javascripts/*` to your gitignore. I find it silly to check in generated files anyway.
|
32
|
+
|
33
|
+
### Environments (default behavior)
|
34
|
+
#### Production
|
35
|
+
In production, whenever `sprockets_include_tag` is called, the unique filename is generated, and if the file does not exist, it is generated via `Sprockets::Secretary`.
|
36
|
+
Subsequent calls to `sprockets_include_tag` will never update the generated sprocket, and there is no other way that the sprocket will be generated.
|
37
|
+
|
38
|
+
For deployment, you probably don't want the first request to every page spend time rebuild the sprocket for that page. This gem caches all of the built sprockets to the file `tmp/sprockets-cache/sprockets.yml`. You probably want to symlink the entire directory to your deployment, but you can also just symlink this file.
|
39
|
+
|
40
|
+
You then need to run the `rake sprockets:rebuild` before your deployment goes live to rebuild everything
|
41
|
+
|
42
|
+
#### Development
|
43
|
+
Here, whenever `sprockets_include_tag` is called, the files are examined and the dependency tree is determined. The files are then included in the order which satisfies the dependency tree.
|
44
|
+
Every request will trigger a refresh of the generated files in addition to them being generated when `sprockets_include_tag` is called.
|
45
|
+
|
46
|
+
## Example
|
47
|
+
|
48
|
+
Assume that `app/javascripts/jquery.js` exists and we have these files:
|
49
|
+
|
50
|
+
### app/javascripts/foo.js
|
51
|
+
|
52
|
+
//= require <jquery>
|
53
|
+
//= require <foo/bar>
|
54
|
+
|
55
|
+
$(function() {
|
56
|
+
$('#foo').fadeIn().html($['BAR_VALUE']);
|
57
|
+
});
|
58
|
+
|
59
|
+
### app/javascripts/foo/bar.js.erb
|
60
|
+
|
61
|
+
//= require <jquery>
|
62
|
+
|
63
|
+
$['BAR_VALUE'] = <%= Bar::BAR_VALUE %>;
|
64
|
+
|
65
|
+
### app/views/foo/index.html.erb
|
66
|
+
|
67
|
+
<% include_sprocket 'foo' %>
|
68
|
+
|
69
|
+
<div id='bar'>
|
70
|
+
And the bar value for today is: <div id='foo'></div>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
And as long as your template has `<%= sprockets_include_tag %>`, the right javascript concatenation will be generated for you.
|
74
|
+
|
75
|
+
See configuration below for details, but if `:expand_includes` is true, then `sprockets_include_tag` is equivalent to `javascript_include_tag 'jquery', 'foo/bar', 'foo'`.
|
76
|
+
Otherwise, if `:expand_includes` is false, the three files will be concatenated into a file with a unique name, and it will be the only one served on the page
|
77
|
+
|
78
|
+
## Configuration
|
79
|
+
|
80
|
+
You can configure the packager through `Sprockets::Packager.options`. The following are recognized options:
|
81
|
+
|
82
|
+
* `:load_path` - an array of relative/absolute paths of where to load the sprockets from. Sprockets are interpreted as relative from any one of these paths. Default: `['app/javascripts']`
|
83
|
+
* `:destination` - This is the destination of generated sprockets to go. Default: `'public/javascripts'`
|
84
|
+
* `:root` - This is the root option passed to Sprockets::Secretary. Default: `Rails.root`
|
85
|
+
* `:tmp_path` - This is the absolute/relative place to place any generated files like ERB templates. Default `'tmp/sprockets-cache'`
|
86
|
+
* `:watch_changes` - Values as to whether to watch the file system for changes. If `true`, this will regenerate all necessary sprockets on each request. Default: `Rails.env.development?`
|
87
|
+
* `:expand_includes` - Value as to whether to expand javascript includes or to compact them into one asset. Default: `Rails.env.development?`
|
88
|
+
* `:serve_assets` - Value as to whether a Rack component should be installed to serve all static assets. This is useful for deployments on Heroku where the `public/` directory is not writeable. If this is used, the `:destination` is changed to `tmp/javascripts` and assets are served from there. Default: `false`
|
89
|
+
|
90
|
+
All configuration should be done in `config/application.rb` or `config/environments/*.rb`.
|
91
|
+
|
92
|
+
## License
|
93
|
+
|
94
|
+
Copyright © 2009 Alex Crichton.
|
95
|
+
|
96
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
97
|
+
|
98
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
99
|
+
|
100
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/paste.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/object/blank'
|
3
|
+
require 'active_support/core_ext/module/aliasing'
|
4
|
+
require 'active_support/core_ext/module/attr_accessor_with_default'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
|
7
|
+
module Paste
|
8
|
+
VERSION = '0.0.1'
|
9
|
+
|
10
|
+
autoload :Glue, 'paste/glue'
|
11
|
+
autoload :NeedsUpdate, 'paste/needs_update'
|
12
|
+
autoload :Rails, 'paste/rails'
|
13
|
+
autoload :Resolver, 'paste/resolver'
|
14
|
+
|
15
|
+
module JS
|
16
|
+
autoload :Base, 'paste/js/base'
|
17
|
+
autoload :Cache, 'paste/js/cache'
|
18
|
+
autoload :Chain, 'paste/js/chain'
|
19
|
+
autoload :Compress, 'paste/js/compress'
|
20
|
+
autoload :ERBRenderer, 'paste/js/erb_renderer'
|
21
|
+
autoload :Unify, 'paste/js/unify'
|
22
|
+
|
23
|
+
class << self
|
24
|
+
delegate :configure, :config, :to => Base
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module CSS
|
29
|
+
autoload :Base, 'paste/css/base'
|
30
|
+
|
31
|
+
class << self
|
32
|
+
delegate :configure, :config, :to => Base
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Parser
|
37
|
+
autoload :Sprockets, 'paste/parser/sprockets'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Paste::Glue.configure do |config|
|
42
|
+
config.root = Dir.pwd
|
43
|
+
config.tmp_path = 'tmp/paste-cache'
|
44
|
+
end
|
45
|
+
|
46
|
+
Paste::JS.configure do |config|
|
47
|
+
config.destination = 'public/javascripts'
|
48
|
+
config.load_path = ['app/javascripts']
|
49
|
+
config.erb_path = 'tmp/paste-cache/erb'
|
50
|
+
config.cache_file = 'sprockets.yml'
|
51
|
+
config.parser = Paste::Parser::Sprockets
|
52
|
+
end
|
53
|
+
|
54
|
+
Paste::CSS.configure do |config|
|
55
|
+
config.destination = 'public/stylesheets'
|
56
|
+
config.load_path = ['app/stylesheets']
|
57
|
+
end
|
58
|
+
|
59
|
+
require 'paste/rails/railtie' if defined?(Rails)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Capistrano::Configuration.instance(true).load do
|
2
|
+
after 'deploy:setup', 'paste:create_cache'
|
3
|
+
after 'deploy:update_code', 'paste:link_cache'
|
4
|
+
before 'deploy:symlink', 'paste:rebuild'
|
5
|
+
|
6
|
+
namespace :paste do
|
7
|
+
desc "Rebuild javascripts and such"
|
8
|
+
task :rebuild do
|
9
|
+
# Don't fail if paste isn't installed or something like that. Force this
|
10
|
+
# command to succeed
|
11
|
+
env = exists?(:rails_env) ? rails_env : 'production'
|
12
|
+
run "cd #{latest_release} && " +
|
13
|
+
"rake RAILS_ENV=#{env} paste:rebuild; echo ''"
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Create a directory for caching javascript and such"
|
17
|
+
task :create_cache do
|
18
|
+
run "mkdir -p #{shared_path}/paste-cache"
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Link the cache directory into place"
|
22
|
+
task :link_cache do
|
23
|
+
run "ln -nsf #{shared_path}/paste-cache #{latest_release}/tmp"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/paste/glue.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Paste
|
2
|
+
module JS
|
3
|
+
class Base < Glue
|
4
|
+
|
5
|
+
include Cache
|
6
|
+
include Compress
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
config.load_path << erb_path
|
10
|
+
end
|
11
|
+
|
12
|
+
def paste *sources
|
13
|
+
raise 'Implement me!'
|
14
|
+
end
|
15
|
+
|
16
|
+
def result_name sources
|
17
|
+
raise 'Implement me!'
|
18
|
+
end
|
19
|
+
|
20
|
+
def write_result result
|
21
|
+
raise 'Implement me!'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Paste
|
4
|
+
module JS
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
def rebuild
|
8
|
+
rebuild_if do |result, sources|
|
9
|
+
needs_update? result
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def rebuild!
|
14
|
+
rebuild_if { |r, s| true }
|
15
|
+
end
|
16
|
+
|
17
|
+
def rebuild_if &blk
|
18
|
+
render_all_erb
|
19
|
+
results.each_pair do |result, sources|
|
20
|
+
begin
|
21
|
+
write_result result if blk.call(result, sources[:sources])
|
22
|
+
rescue ResolveError
|
23
|
+
results.delete result
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def register sources
|
29
|
+
if results[result_name(sources)][:sources] != sources
|
30
|
+
results[result_name(sources)] = {
|
31
|
+
:sources => sources,
|
32
|
+
:parser => config.parser.new(self, sources)
|
33
|
+
}
|
34
|
+
|
35
|
+
write_cache_to_disk
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def registered? sources
|
40
|
+
results.key? result_name(sources)
|
41
|
+
end
|
42
|
+
|
43
|
+
def needs_update? result
|
44
|
+
path = destination result
|
45
|
+
return true unless File.exists?(path)
|
46
|
+
|
47
|
+
results[result][:sources].inject(false) do |prev, source|
|
48
|
+
prev || File.mtime(path) < File.mtime(find(source))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def results
|
53
|
+
return @results if defined?(@results)
|
54
|
+
@results = Hash.new { {} }
|
55
|
+
|
56
|
+
(YAML.load_file tmp_path(config.cache_file) rescue []).each do |sources|
|
57
|
+
register sources
|
58
|
+
end
|
59
|
+
|
60
|
+
@results
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def write_cache_to_disk
|
66
|
+
file = tmp_path config.cache_file
|
67
|
+
FileUtils.mkdir_p File.dirname(file)
|
68
|
+
|
69
|
+
to_write = []
|
70
|
+
results.each do |result, hash|
|
71
|
+
to_write << hash[:sources]
|
72
|
+
end
|
73
|
+
|
74
|
+
File.open(file, 'w') do |f|
|
75
|
+
f << YAML.dump(to_write)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Paste
|
2
|
+
module JS
|
3
|
+
class Chain < Base
|
4
|
+
|
5
|
+
def paste *sources
|
6
|
+
js_dependencies = []
|
7
|
+
css_dependencies = []
|
8
|
+
|
9
|
+
sources.each do |source|
|
10
|
+
register [source] unless registered? [source]
|
11
|
+
source_deps = results[result_name([source])][:parser].js_dependencies
|
12
|
+
js_dependencies = js_dependencies | source_deps
|
13
|
+
end
|
14
|
+
|
15
|
+
js_dependencies = js_dependencies.map do |d|
|
16
|
+
result = result_name [d]
|
17
|
+
register [d] unless registered? [d] # implicit dependencies
|
18
|
+
write_result result if needs_update?(result)
|
19
|
+
|
20
|
+
css_dependencies = css_dependencies |
|
21
|
+
results[result][:parser].css_dependencies
|
22
|
+
|
23
|
+
result
|
24
|
+
end
|
25
|
+
|
26
|
+
{
|
27
|
+
:javascript => js_dependencies,
|
28
|
+
:css => css_dependencies
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_result result
|
33
|
+
file = destination result
|
34
|
+
|
35
|
+
FileUtils.mkdir_p File.dirname(file)
|
36
|
+
FileUtils.cp find(result), file
|
37
|
+
end
|
38
|
+
|
39
|
+
def result_name sources
|
40
|
+
result = sources.first
|
41
|
+
result += '.js' unless result.end_with?('.js')
|
42
|
+
result
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
2
|
+
|
3
|
+
module Paste
|
4
|
+
module JS
|
5
|
+
module Compress
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
alias_method_chain :rebuild!, :compression
|
10
|
+
end
|
11
|
+
|
12
|
+
def rebuild_with_compression! options = {}
|
13
|
+
rebuild_without_compression!
|
14
|
+
|
15
|
+
results.keys.each do |result|
|
16
|
+
compress result, options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def compress result, options = {}
|
21
|
+
case options[:compress]
|
22
|
+
when 'google'
|
23
|
+
google_compress result, options
|
24
|
+
when nil, false
|
25
|
+
# Compression not asked for
|
26
|
+
else
|
27
|
+
raise "Unknown compression technique: #{options[:compress]}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def google_compress result, options = {}
|
34
|
+
file = destination result
|
35
|
+
uri = URI.parse('http://closure-compiler.appspot.com/compile')
|
36
|
+
req = Net::HTTP.post_form(uri,
|
37
|
+
:js_code => File.read(file),
|
38
|
+
:compilation_level => options[:level] || 'SIMPLE_OPTIMIZATIONS',
|
39
|
+
:output_format => 'text',
|
40
|
+
:output_info => 'compiled_code'
|
41
|
+
)
|
42
|
+
|
43
|
+
if req.is_a? Net::HTTPSuccess
|
44
|
+
File.open(file, 'w') { |f| f << req.body.chomp }
|
45
|
+
else
|
46
|
+
raise "Google couldn't compile #{result}!"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Paste
|
4
|
+
module JS
|
5
|
+
module ERBRenderer
|
6
|
+
|
7
|
+
def render_all_erb
|
8
|
+
erb_sources.each { |s| render_erb s }
|
9
|
+
end
|
10
|
+
|
11
|
+
def render_erb source
|
12
|
+
to_generate = erb_path source.sub(/\.erb$/, '')
|
13
|
+
source = find source
|
14
|
+
|
15
|
+
if !File.exists?(to_generate) ||
|
16
|
+
File.mtime(source) > File.mtime(to_generate)
|
17
|
+
|
18
|
+
FileUtils.mkdir_p File.dirname(to_generate)
|
19
|
+
contents = PasteERBHelper.new(File.read(source)).result
|
20
|
+
File.open(to_generate, 'w') { |f| f << contents }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def erb_sources
|
27
|
+
sources = load_path.map do |path|
|
28
|
+
Dir[path + '/**/*.js.erb'].map do |erb|
|
29
|
+
erb.gsub(path + '/', '')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
sources.flatten
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# This needs to be outside of the module because we don't want to
|
40
|
+
# force templates to always do ::Rails
|
41
|
+
class PasteERBHelper < ERB
|
42
|
+
include ActionView::Helpers if defined?(ActionView::Helpers)
|
43
|
+
|
44
|
+
def config
|
45
|
+
Rails.application.config.action_controller if defined?(Rails)
|
46
|
+
end
|
47
|
+
|
48
|
+
def result *args
|
49
|
+
super binding
|
50
|
+
end
|
51
|
+
|
52
|
+
def controller
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module Paste
|
4
|
+
module JS
|
5
|
+
class Unify < Base
|
6
|
+
|
7
|
+
def paste *sources
|
8
|
+
result = result_name sources
|
9
|
+
|
10
|
+
register sources unless registered?(sources)
|
11
|
+
|
12
|
+
if needs_update?(result)
|
13
|
+
write_result result
|
14
|
+
end
|
15
|
+
|
16
|
+
{
|
17
|
+
:javascript => [result],
|
18
|
+
:css => results[result][:parser].css_dependencies
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_result result
|
23
|
+
path = destination result
|
24
|
+
FileUtils.mkdir_p File.dirname(path)
|
25
|
+
|
26
|
+
results[result][:parser].reset!
|
27
|
+
results[result][:parser].concatenation.save_to path
|
28
|
+
end
|
29
|
+
|
30
|
+
def result_name sources
|
31
|
+
to_digest = sources.map{ |s| s.gsub /\.js$/, '' }.sort.join
|
32
|
+
Digest::SHA1.hexdigest(to_digest)[0..12] + '.js'
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'sprockets'
|
2
|
+
|
3
|
+
module Paste
|
4
|
+
module Parser
|
5
|
+
class Sprockets
|
6
|
+
|
7
|
+
attr_reader :glue, :secretary, :sources
|
8
|
+
|
9
|
+
delegate :concatenation, :to => :secretary
|
10
|
+
|
11
|
+
def initialize glue, sources
|
12
|
+
@glue = glue
|
13
|
+
@sources = sources
|
14
|
+
@secretary = ::Sprockets::Secretary.new(
|
15
|
+
:root => glue.root,
|
16
|
+
:expand_paths => false,
|
17
|
+
:load_path => glue.load_path,
|
18
|
+
:source_files => sources.map{ |s| glue.find s }
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def js_dependencies
|
23
|
+
generate_dependencies if @js_dependencies.nil?
|
24
|
+
@js_dependencies
|
25
|
+
end
|
26
|
+
|
27
|
+
def css_dependencies
|
28
|
+
generate_dependencies if @css_dependencies.nil?
|
29
|
+
@css_dependencies
|
30
|
+
end
|
31
|
+
|
32
|
+
def reset!
|
33
|
+
@js_dependencies = @css_dependencies = nil
|
34
|
+
secretary.reset!
|
35
|
+
rescue ::Sprockets::LoadError => e
|
36
|
+
raise ResolveError.new(e.message)
|
37
|
+
end
|
38
|
+
|
39
|
+
protected
|
40
|
+
|
41
|
+
def environment
|
42
|
+
@environment ||= ::Sprockets::Environment.new glue.root,
|
43
|
+
glue.load_path
|
44
|
+
end
|
45
|
+
|
46
|
+
def generate_dependencies
|
47
|
+
@js_dependencies = []
|
48
|
+
@css_dependencies = []
|
49
|
+
sources.each { |source| in_order_traversal source }
|
50
|
+
end
|
51
|
+
|
52
|
+
def in_order_traversal source, current_path = []
|
53
|
+
return if @js_dependencies.include? source
|
54
|
+
raise "Circular dependency at #{source}" if current_path.include? source
|
55
|
+
|
56
|
+
current_path.push source
|
57
|
+
|
58
|
+
source_file = ::Sprockets::SourceFile.new environment,
|
59
|
+
::Sprockets::Pathname.new(environment, glue.find(source))
|
60
|
+
css_deps = []
|
61
|
+
source_file.source_lines.each do |line|
|
62
|
+
if line.require?
|
63
|
+
in_order_traversal line.require[/^.(.*).$/, 1], current_path
|
64
|
+
elsif line.css_require?
|
65
|
+
css_deps << line.css_require
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
@js_dependencies.push current_path.pop
|
70
|
+
@css_dependencies = @css_dependencies | css_deps
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module Sprockets
|
78
|
+
class SourceLine
|
79
|
+
def css_require?
|
80
|
+
!!css_require
|
81
|
+
end
|
82
|
+
|
83
|
+
def css_require
|
84
|
+
@css_require ||= (comment || "")[/^=\s+require_css\s+<(.*?)>\s*$/, 1]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/paste/rails.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Paste
|
2
|
+
module Rails
|
3
|
+
autoload :Helper, 'paste/rails/helper'
|
4
|
+
autoload :Railtie, 'paste/rails/railtie'
|
5
|
+
autoload :Updater, 'paste/rails/updater'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor_with_default :glue do
|
9
|
+
Paste::JS::Unify.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module Paste
|
4
|
+
module Rails
|
5
|
+
module Helper
|
6
|
+
def paste_js_tags *javascripts
|
7
|
+
include_javascripts *javascripts
|
8
|
+
|
9
|
+
return '' if @javascripts.empty?
|
10
|
+
|
11
|
+
results = Paste::Rails.glue.paste *@javascripts
|
12
|
+
|
13
|
+
javascript_include_tag(*results[:javascript])
|
14
|
+
end
|
15
|
+
|
16
|
+
def paste_css_tags *other_css
|
17
|
+
include_css *other_css
|
18
|
+
|
19
|
+
results = Paste::Rails.glue.paste *(@javascripts ||= [])
|
20
|
+
all_css = (results[:css] + other_css + @css).uniq
|
21
|
+
|
22
|
+
cache = Digest::SHA1.hexdigest(all_css.sort.join)[0..12]
|
23
|
+
all_css << {:cache => cache}
|
24
|
+
stylesheet_link_tag *all_css
|
25
|
+
end
|
26
|
+
|
27
|
+
def include_javascripts *javascripts
|
28
|
+
@javascripts ||= []
|
29
|
+
@javascripts += javascripts.flatten
|
30
|
+
@javascripts.uniq!
|
31
|
+
end
|
32
|
+
|
33
|
+
def included_javascripts
|
34
|
+
@javascripts
|
35
|
+
end
|
36
|
+
|
37
|
+
def include_css *css
|
38
|
+
@css ||= []
|
39
|
+
@css += css.flatten
|
40
|
+
@css.uniq!
|
41
|
+
end
|
42
|
+
|
43
|
+
def included_css
|
44
|
+
@css
|
45
|
+
end
|
46
|
+
|
47
|
+
alias :include_javascript :include_javascripts
|
48
|
+
alias :javascript :include_javascripts
|
49
|
+
|
50
|
+
alias :css :include_css
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Paste
|
2
|
+
module Rails
|
3
|
+
class Railtie < ::Rails::Railtie
|
4
|
+
|
5
|
+
initializer 'paste_initializer' do
|
6
|
+
Paste::JS.config.root = ::Rails.root
|
7
|
+
ActionView::Base.send :include, Helper
|
8
|
+
|
9
|
+
if ::Rails.env.development?
|
10
|
+
Paste::Rails.glue = Paste::JS::Chain.new
|
11
|
+
config.app_middleware.use Paste::Rails::Updater
|
12
|
+
else
|
13
|
+
Paste::Rails.glue = Paste::JS::Unify.new
|
14
|
+
config.to_prepare do
|
15
|
+
Paste::Rails.glue.render_all_erb
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if Paste::JS.config.serve_assets
|
20
|
+
Paste::JS.config.destination = 'tmp/javascripts'
|
21
|
+
|
22
|
+
# We want this serving to be at the very front
|
23
|
+
config.app_middleware.insert_before Rack::Runtime,
|
24
|
+
::Rack::Static,
|
25
|
+
:urls => ['/javascripts'],
|
26
|
+
:root => Paste::JS.tmp_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
rake_tasks do
|
31
|
+
load 'paste/tasks/paste.rake'
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Paste
|
2
|
+
|
3
|
+
class ResolveError < StandardError; end
|
4
|
+
|
5
|
+
module Resolver
|
6
|
+
|
7
|
+
def resolve path
|
8
|
+
if File.exists?(path) || path.bytes.first == '/'.bytes.first
|
9
|
+
File.expand_path path
|
10
|
+
else
|
11
|
+
File.join config.root, path
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
[:destination, :root, :erb_path, :tmp_path].each do |attribute|
|
16
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
17
|
+
def #{attribute} relative = ''
|
18
|
+
if relative.blank?
|
19
|
+
resolve config.#{attribute}
|
20
|
+
else
|
21
|
+
File.join resolve(config.#{attribute}), relative
|
22
|
+
end
|
23
|
+
end
|
24
|
+
RUBY
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_path
|
28
|
+
config.load_path.map { |p| resolve p }
|
29
|
+
end
|
30
|
+
|
31
|
+
def find source
|
32
|
+
source += '.js' unless source.end_with?('.js') || source.end_with?('.erb')
|
33
|
+
|
34
|
+
path = (load_path + ['']).detect do |path|
|
35
|
+
File.exists? File.join(path, source)
|
36
|
+
end
|
37
|
+
|
38
|
+
raise ResolveError, "Source #{source} couldn't be found!" if path.nil?
|
39
|
+
|
40
|
+
File.join(path, source)
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
def join path1, path2
|
46
|
+
if path2.blank?
|
47
|
+
path1
|
48
|
+
else
|
49
|
+
File.join path1, path2
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paste
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alex Crichton
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-09 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
none: false
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
hash: -1848230024
|
28
|
+
segments:
|
29
|
+
- 3
|
30
|
+
- 0
|
31
|
+
- 0
|
32
|
+
- beta4
|
33
|
+
version: 3.0.0.beta4
|
34
|
+
requirement: *id001
|
35
|
+
type: :runtime
|
36
|
+
name: activesupport
|
37
|
+
prerelease: false
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
requirement: *id002
|
49
|
+
type: :runtime
|
50
|
+
name: sprockets
|
51
|
+
prerelease: false
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 62196421
|
59
|
+
segments:
|
60
|
+
- 2
|
61
|
+
- 0
|
62
|
+
- 0
|
63
|
+
- beta
|
64
|
+
- 19
|
65
|
+
version: 2.0.0.beta.19
|
66
|
+
requirement: *id003
|
67
|
+
type: :development
|
68
|
+
name: rspec
|
69
|
+
prerelease: false
|
70
|
+
description: Asset Management for Rails
|
71
|
+
email:
|
72
|
+
- alex@alexcrichton.com
|
73
|
+
executables: []
|
74
|
+
|
75
|
+
extensions: []
|
76
|
+
|
77
|
+
extra_rdoc_files:
|
78
|
+
- README.rdoc
|
79
|
+
files:
|
80
|
+
- lib/paste/capistrano.rb
|
81
|
+
- lib/paste/css/base.rb
|
82
|
+
- lib/paste/glue.rb
|
83
|
+
- lib/paste/js/base.rb
|
84
|
+
- lib/paste/js/cache.rb
|
85
|
+
- lib/paste/js/chain.rb
|
86
|
+
- lib/paste/js/compress.rb
|
87
|
+
- lib/paste/js/erb_renderer.rb
|
88
|
+
- lib/paste/js/unify.rb
|
89
|
+
- lib/paste/parser/sprockets.rb
|
90
|
+
- lib/paste/rails/helper.rb
|
91
|
+
- lib/paste/rails/railtie.rb
|
92
|
+
- lib/paste/rails/updater.rb
|
93
|
+
- lib/paste/rails.rb
|
94
|
+
- lib/paste/resolver.rb
|
95
|
+
- lib/paste/tasks/paste.rake
|
96
|
+
- lib/paste.rb
|
97
|
+
- README.rdoc
|
98
|
+
has_rdoc: true
|
99
|
+
homepage: http://github.com/alexcrichton/paste
|
100
|
+
licenses: []
|
101
|
+
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options:
|
104
|
+
- --main
|
105
|
+
- README.rdoc
|
106
|
+
require_paths:
|
107
|
+
- lib
|
108
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 3
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
requirements: []
|
127
|
+
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 1.3.7
|
130
|
+
signing_key:
|
131
|
+
specification_version: 3
|
132
|
+
summary: JS and CSS dependency management
|
133
|
+
test_files: []
|
134
|
+
|