rack-pipeline 0.0.6

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.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzE2ZjZiOTc5YWYxZGM0MjQ2ZjNmNDJkNjkxOWY2YzdmNTcxZDRhMQ==
5
+ data.tar.gz: !binary |-
6
+ NzFkNTRhZWUyYWFhYzIwNmVkMTgyMzhjYzRiY2Y0MjZjZGJmMDJjMA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NmIxY2NmOWY4MGIzMzgzMTI4M2IzMzM4MzFkZjE3ZDQ0ZGVjMzY4ZTJkZWQ4
10
+ MTE5NWYyOWQ0NjE3ZTI3MTFlYjQ3NTViYjQwMjg1YzFhMTdiNzBlNThjOGRk
11
+ OTU3MjJiYTNhOTYzZDFkNzM2NDk4ZWEyMmQ3ZjM5ZTE0Njc0YTU=
12
+ data.tar.gz: !binary |-
13
+ NWFmMzdjNTQzNzhhOTU4NTIwN2VlM2Q2OTk3ZjAzMzQyODY4OTQ2NzFlNmQ2
14
+ ZGFjZjM3ZjVjMWVjMDcxOGEzOGI4N2I3N2RjNTM2MTViYTlmY2VhNzQ3YjMw
15
+ YzA2NmYyN2E4ZjUyM2IwZDE0NjFmYmQ5YjFlYTQ4ZjE1Zjg5NmQ=
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rbx
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Igor Bochkariov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ [![Build Status](https://travis-ci.org/ujifgc/rack-pipeline.png)](https://travis-ci.org/ujifgc/rack-pipeline)
2
+ [![Code Climate](https://codeclimate.com/github/ujifgc/rack-pipeline.png)](https://codeclimate.com/github/ujifgc/rack-pipeline)
3
+
4
+ # RackPipeline
5
+
6
+ A rack middleware to serve javascript and stylesheet assets for ruby web applications
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'rack-pipeline'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install rack-pipeline
21
+
22
+ ## Usage
23
+
24
+ TODO: Write usage instructions here
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,3 @@
1
+ require 'rack-pipeline/base'
2
+ require 'rack-pipeline/compiling'
3
+ require 'rack-pipeline/compressing'
@@ -0,0 +1,139 @@
1
+ require 'time'
2
+
3
+ require 'rack-pipeline/version'
4
+ require 'rack-pipeline/caching'
5
+ require 'rack-pipeline/processing'
6
+
7
+ module RackPipeline
8
+ class MustRepopulate < Exception; end
9
+ class Base
10
+ include Caching
11
+ include Processing
12
+
13
+ attr_accessor :assets, :settings
14
+
15
+ STATIC_TYPES = { '.js' => :js, '.css' => :css }.freeze
16
+
17
+ def assets_for( pipes, type, opts = {} )
18
+ Array(pipes).inject([]) do |all,pipe|
19
+ all += Array( combine? ? "#{pipe}.#{type}" : assets[type][pipe].keys )
20
+ end.compact.uniq
21
+ end
22
+
23
+ def initialize(app, *args)
24
+ @generations = 0
25
+ @assets = {}
26
+ @settings = {
27
+ :temp => nil,
28
+ :compress => false,
29
+ :combine => false,
30
+ :content_type => {
31
+ '.css' => 'text/css',
32
+ '.js' => 'application/javascript',
33
+ },
34
+ :css => {
35
+ :app => 'assets/**/*.css',
36
+ },
37
+ :js => {
38
+ :app => 'assets/**/*.js',
39
+ },
40
+ }
41
+ @settings.merge!(args.pop) if args.last.kind_of?(Hash)
42
+ create_temp_directory
43
+ populate_pipelines
44
+ @app = app
45
+ end
46
+
47
+ def inspect
48
+ { :settings => settings, :assets => assets }
49
+ end
50
+
51
+ def call(env)
52
+ env['rack-pipeline'] = self
53
+ if file_path = prepare_pipe(env['PATH_INFO'])
54
+ serve_file( file_path, env['HTTP_IF_MODIFIED_SINCE'] )
55
+ else
56
+ @app.call(env)
57
+ end
58
+ rescue MustRepopulate
59
+ populate_pipelines
60
+ retry
61
+ end
62
+
63
+ private
64
+
65
+ def serve_file( file, mtime )
66
+ headers = { 'Last-Modified' => File.mtime(file).httpdate }
67
+ if mtime == headers['Last-Modified']
68
+ [304, headers, []]
69
+ else
70
+ body = File.read file
71
+ headers['Content-Type'] = "#{settings[:content_type][File.extname(file)] || 'text'}; charset=#{body.encoding.to_s}"
72
+ headers['Content-Length'] = File.size(file).to_s
73
+ [200, headers, [body]]
74
+ end
75
+ rescue Errno::ENOENT
76
+ raise MustRepopulate
77
+ end
78
+
79
+ def static_type( file )
80
+ if file.kind_of? String
81
+ STATIC_TYPES[file] || STATIC_TYPES[File.extname(file)]
82
+ else
83
+ STATIC_TYPES.values.include?(file) && file
84
+ end
85
+ end
86
+
87
+ def prepare_pipe( path_info )
88
+ file = path_info.sub( /^\/(.*)\??.*$/, '\1' )
89
+ type = static_type(file) or return nil
90
+ unless ready_file = get_or_compile(file, type)
91
+ pipename = File.basename(file, '.*').to_sym
92
+ if assets[type] && assets[type][pipename]
93
+ ready_file = combine( assets[type][pipename], File.basename(file) )
94
+ end
95
+ end
96
+ compress ready_file
97
+ rescue Errno::ENOENT
98
+ raise MustRepopulate
99
+ end
100
+
101
+ def get_or_compile( source, type )
102
+ result = nil
103
+ assets[type].each do |pipe,files|
104
+ result = case files[source]
105
+ when :raw
106
+ source
107
+ when :source
108
+ compile( source, File.basename(source, '.*') + ".#{type}" )
109
+ end
110
+ break if result
111
+ end
112
+ result
113
+ end
114
+
115
+ def file_kind( file )
116
+ static_type(file) ? :raw : :source
117
+ end
118
+
119
+ def extract_files( globs )
120
+ Array(globs).each_with_object({}) do |glob,all|
121
+ Dir.glob(glob).sort.each do |file|
122
+ all[file] = file_kind( file )
123
+ end
124
+ end
125
+ end
126
+
127
+ def populate_pipelines
128
+ fail( SystemStackError, 'too many RackPipeline generations' ) if @generations > 5
129
+ @generations += 1
130
+ STATIC_TYPES.each do |extname,type|
131
+ pipes = settings[type]
132
+ assets[type] = {}
133
+ pipes.each do |pipe, globs|
134
+ assets[type][pipe] = extract_files(globs)
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,35 @@
1
+ require 'digest/md5'
2
+ require 'fileutils'
3
+
4
+ module RackPipeline
5
+ module Caching
6
+ def cache_target( source, target )
7
+ target_path = File.join( settings[:temp], "#{File.basename(target)}.#{calculate_hash(source)}#{File.extname(target)}" )
8
+ if File.file?(target_path)
9
+ target_path
10
+ else
11
+ cleanup_cache(target)
12
+ yield target_path
13
+ end
14
+ end
15
+
16
+ def create_temp_directory
17
+ temp = if settings[:temp]
18
+ settings[:temp]
19
+ else
20
+ require 'tmpdir'
21
+ File.join( Dir.tmpdir, 'RackPipeline' )
22
+ end
23
+ FileUtils.mkdir_p temp
24
+ settings[:temp] = temp
25
+ end
26
+
27
+ def cleanup_cache( target )
28
+ FileUtils.rm Dir.glob( File.join( settings[:temp], File.basename(target) ) + '.*' )
29
+ end
30
+
31
+ def calculate_hash( sources )
32
+ Digest::MD5.hexdigest(Array(sources).inject(''){ |all,(file,_)| all << "#{file}:#{File.mtime(file)}" })
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,7 @@
1
+ module RackPipeline
2
+ module Compiling
3
+ def self.process( source, target )
4
+ nil
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module RackPipeline
2
+ module Compressing
3
+ def self.process( source, target )
4
+ nil
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ module RackPipeline
2
+ module Processing
3
+ def combine?
4
+ settings[:combine]
5
+ end
6
+
7
+ def compress?
8
+ settings[:compress]
9
+ end
10
+
11
+ def combine( sources, target )
12
+ cache_target( sources, target ) do |target_path|
13
+ body = sources.inject('') do |all,(source,kind)|
14
+ all << File.read(get_or_compile(source, static_type(target))).encode('utf-8') + "\n\n"
15
+ end
16
+ File.write( target_path, body )
17
+ target_path
18
+ end
19
+ end
20
+
21
+ def compress( source )
22
+ if compress? && defined?(Compressor)
23
+ Compressor.process source
24
+ else
25
+ source
26
+ end
27
+ end
28
+
29
+ def compile( source, target )
30
+ cache_target( source, target ) do |target_path|
31
+ compiled_file = if defined? Compiler
32
+ Compiler.process source, target_path
33
+ end
34
+ raise LoadError, "compiler for #{source} => #{target}" unless compiled_file
35
+ compiled_file
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ module RackPipeline
2
+ module Sinatra
3
+ def self.registered(app)
4
+ app.use RackPipeline::Base, app.respond_to?(:pipeline) ? app.pipeline : {}
5
+ app.helpers Helpers
6
+ end
7
+ module Helpers
8
+ def pipeline( pipes = [ :app ], types = [ :css, :js ], opts = {} )
9
+ Array(types).map do |type|
10
+ assets = env['rack-pipeline'].assets_for( pipes, type, opts )
11
+ assets.map do |asset|
12
+ pipe_tag( type, asset )
13
+ end.join("\n")
14
+ end.join("\n")
15
+ end
16
+
17
+ def pipe_tag( type, asset )
18
+ case type.to_sym
19
+ when :css
20
+ %(<link href="#{request.script_name}/#{asset}" rel="stylesheet">)
21
+ when :js
22
+ %(<script src="#{request.script_name}/#{asset}"></script>)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module RackPipeline
2
+ VERSION = "0.0.6"
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ $LOAD_PATH << File.expand_path('../lib', __FILE__)
3
+ require 'rack-pipeline/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'rack-pipeline'
7
+ spec.version = RackPipeline::VERSION
8
+ spec.description = 'Asset pipeline for ruby Rack'
9
+ spec.summary = 'A Rack middleware to serve javascript and stylesheet assets for ruby web applications'
10
+
11
+ spec.authors = ['Igor Bochkariov']
12
+ spec.email = ['ujifgc@gmail.com']
13
+ spec.homepage = 'https://github.com/ujifgc/rack-pipeline'
14
+ spec.license = 'MIT'
15
+
16
+ spec.require_paths = ['lib']
17
+ spec.files = `git ls-files`.split($/)
18
+ spec.test_files = spec.files.grep(%r{^test/})
19
+
20
+ spec.add_development_dependency 'bundler', '>= 1.3'
21
+ spec.add_development_dependency 'rake'
22
+ spec.add_development_dependency 'minitest'
23
+ spec.add_development_dependency 'rack-test'
24
+ spec.add_development_dependency 'awesome_print'
25
+ end
@@ -0,0 +1,2 @@
1
+ a = function() {
2
+ };
@@ -0,0 +1,2 @@
1
+ b = function() {
2
+ };
@@ -0,0 +1,3 @@
1
+ a {
2
+ color: white;
3
+ }
@@ -0,0 +1,3 @@
1
+ b {
2
+ color: black;
3
+ }
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+
3
+ require 'minitest/autorun'
4
+ require 'awesome_print'
@@ -0,0 +1,54 @@
1
+ require 'minitest_helper'
2
+ require 'rack/test'
3
+ require 'rack-pipeline/base'
4
+
5
+ describe RackPipeline::Base do
6
+ SETTINGS_1 = {
7
+ }
8
+ SETTINGS_2 = {
9
+ }
10
+
11
+ before do
12
+ Dir.chdir File.dirname(__FILE__)
13
+ @mockapp = MiniTest::Mock.new
14
+ @mockapp.expect(:call, [200, {}, ['UNDERLYING APP RESPONSE']], [Hash])
15
+ @r1 = Rack::MockRequest.new(RackPipeline::Base.new(@mockapp, SETTINGS_1.dup))
16
+ @r2 = Rack::MockRequest.new(RackPipeline::Base.new(@mockapp, SETTINGS_2.dup))
17
+ end
18
+
19
+ it 'should pass to underlying app' do
20
+ response = @r1.get('/')
21
+ @mockapp.verify
22
+ end
23
+
24
+ it 'should pass to underlying app' do
25
+ response = @r1.get('/assets/stylesheets/non-existing.css')
26
+ @mockapp.verify
27
+ end
28
+
29
+ it 'should respond with combined css' do
30
+ response = @r1.get('/app.css')
31
+ response.body.must_include 'color: black'
32
+ response.body.must_include 'color: white'
33
+ end
34
+
35
+ it 'should respond with combined js' do
36
+ response = @r1.get('/app.js')
37
+ response.body.must_include 'a = function'
38
+ response.body.must_include 'b = function'
39
+ end
40
+
41
+ it 'should respond with single css' do
42
+ response = @r1.get('/assets/javascripts/a.js')
43
+ response.body.must_include 'a = function'
44
+ end
45
+
46
+ it 'should respond with single js' do
47
+ response = @r1.get('/assets/stylesheets/a.css')
48
+ response.body.must_include 'color: white'
49
+ end
50
+
51
+ after do
52
+ FileUtils.rm_r( File.join( Dir.tmpdir, 'RackPipeline' ) )
53
+ end
54
+ end
@@ -0,0 +1,8 @@
1
+ require 'minitest_helper'
2
+ require 'rack-pipeline'
3
+
4
+ describe RackPipeline do
5
+ it 'should have a version' do
6
+ RackPipeline::VERSION.must_match /.+/
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-pipeline
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Igor Bochkariov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack-test
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: awesome_print
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Asset pipeline for ruby Rack
84
+ email:
85
+ - ujifgc@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - .travis.yml
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - lib/rack-pipeline.rb
97
+ - lib/rack-pipeline/base.rb
98
+ - lib/rack-pipeline/caching.rb
99
+ - lib/rack-pipeline/compiling.rb
100
+ - lib/rack-pipeline/compressing.rb
101
+ - lib/rack-pipeline/processing.rb
102
+ - lib/rack-pipeline/sinatra.rb
103
+ - lib/rack-pipeline/version.rb
104
+ - rack-pipeline.gemspec
105
+ - test/assets/javascripts/a.js
106
+ - test/assets/javascripts/b.js
107
+ - test/assets/stylesheets/a.css
108
+ - test/assets/stylesheets/b.css
109
+ - test/minitest_helper.rb
110
+ - test/rack-pipeline_base_test.rb
111
+ - test/rack-pipeline_test.rb
112
+ homepage: https://github.com/ujifgc/rack-pipeline
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.0.6
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: A Rack middleware to serve javascript and stylesheet assets for ruby web
136
+ applications
137
+ test_files:
138
+ - test/assets/javascripts/a.js
139
+ - test/assets/javascripts/b.js
140
+ - test/assets/stylesheets/a.css
141
+ - test/assets/stylesheets/b.css
142
+ - test/minitest_helper.rb
143
+ - test/rack-pipeline_base_test.rb
144
+ - test/rack-pipeline_test.rb