fewer 0.0.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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /coverage
2
+ /pkg
3
+ /rdoc
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Ben Pickles
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # fewer
2
+
3
+ Rack middleware to bundle assets and help you make fewer HTTP requests.
4
+
5
+ ## How to use in Rails (3)
6
+
7
+ config.middleware.insert 0, Fewer::App, {
8
+ :engine => Fewer::Engines::Css,
9
+ :mount => '/stylesheets',
10
+ :root => Rails.root.join('public', 'stylesheets')
11
+ }
12
+
13
+ ## Copyright
14
+
15
+ Copyright (c) 2010 Ben Pickles. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "fewer"
8
+ gem.summary = %q{Rack middleware to bundle assets and help you make fewer HTTP requests.}
9
+ gem.description = %q{Rack middleware to bundle assets and help you make fewer HTTP requests.}
10
+ gem.email = 'spideryoung@gmail.com'
11
+ gem.homepage = 'http://github.com/benpickles/fewer'
12
+ gem.authors = ['Ben Pickles']
13
+ gem.add_dependency('rack')
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort 'RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov'
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "fewer #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/lib/fewer.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'fewer/app'
2
+ require 'fewer/engines'
3
+ require 'fewer/errors'
4
+ require 'fewer/serializer'
data/lib/fewer/app.rb ADDED
@@ -0,0 +1,36 @@
1
+ module Fewer
2
+ class App
3
+ attr_reader :app, :engine_klass, :cache, :mount, :root
4
+
5
+ def initialize(app, options = {})
6
+ @app = app
7
+ @engine_klass = options[:engine]
8
+ @mount = options[:mount]
9
+ @root = options[:root]
10
+ @cache = options[:cache] || 3600 * 24 * 365
11
+ end
12
+
13
+ def call(env)
14
+ return app.call(env) unless env['PATH_INFO'] =~ /^#{mount}/
15
+
16
+ names = names_from_path(env['PATH_INFO'])
17
+ engine = engine_klass.new(root, names)
18
+ headers = {
19
+ 'Content-Type' => engine.content_type,
20
+ 'Cache-Control' => "public, max-age=#{cache}"
21
+ }
22
+
23
+ [200, headers, [engine.read]]
24
+ rescue Fewer::MissingSourceFileError => e
25
+ [404, { 'Content-Type' => 'text/plain' }, [e.message]]
26
+ rescue => e
27
+ [500, { 'Content-Type' => 'text/plain' }, ["#{e.class}: #{e.message}"]]
28
+ end
29
+
30
+ private
31
+ def names_from_path(path)
32
+ encoded = File.basename(path.sub(/^#{mount}\/?/, ''), '.*')
33
+ Serializer.decode(encoded)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ module Fewer
2
+ module Engines
3
+ autoload :Abstract, 'fewer/engines/abstract'
4
+ autoload :Css, 'fewer/engines/css'
5
+ autoload :Less, 'fewer/engines/less'
6
+ end
7
+ end
@@ -0,0 +1,46 @@
1
+ module Fewer
2
+ module Engines
3
+ class Abstract
4
+ attr_reader :names, :root
5
+
6
+ def initialize(root, names)
7
+ @root = root
8
+ @names = names
9
+ check_paths!
10
+ end
11
+
12
+ def check_paths!
13
+ if (missing = paths.reject { |path| File.exist?(path) }).any?
14
+ files = missing.map { |path| path.to_s }.join("\n")
15
+ raise Fewer::MissingSourceFileError.new("Missing source file#{'s' if missing.size > 1}:\n#{files}")
16
+ end
17
+ end
18
+
19
+ def content_type
20
+ 'text/plain'
21
+ end
22
+
23
+ def extension
24
+ end
25
+
26
+ def mtime
27
+ paths.map { |path|
28
+ File.mtime(path).to_i
29
+ }.sort.last
30
+ end
31
+
32
+ def paths
33
+ names.map { |name|
34
+ # TODO: Fix rather large security hole...
35
+ File.join(root, "#{name}#{extension}")
36
+ }
37
+ end
38
+
39
+ def read
40
+ paths.map { |path|
41
+ File.read(path)
42
+ }.join("\n")
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ module Fewer
2
+ module Engines
3
+ class Css < Abstract
4
+ def content_type
5
+ 'text/css'
6
+ end
7
+
8
+ def extension
9
+ '.css'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ autoload :Less, 'less'
2
+
3
+ module Fewer
4
+ module Engines
5
+ class Less < Abstract
6
+ def content_type
7
+ 'text/css'
8
+ end
9
+
10
+ def extension
11
+ '.less'
12
+ end
13
+
14
+ def read
15
+ ::Less::Engine.new(super).to_css
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Fewer
2
+ class MissingSourceFileError < StandardError; end
3
+ end
@@ -0,0 +1,30 @@
1
+ require 'base64'
2
+
3
+ module Fewer
4
+ module Serializer
5
+ class BadString < RuntimeError; end
6
+
7
+ class << self
8
+ def b64_decode(string)
9
+ padding_length = string.length % 4
10
+ Base64.decode64(string + '=' * padding_length)
11
+ end
12
+
13
+ def b64_encode(string)
14
+ Base64.encode64(string).tr("\n=",'')
15
+ end
16
+
17
+ def marshal_decode(string)
18
+ Marshal.load(b64_decode(string))
19
+ rescue TypeError, ArgumentError => e
20
+ raise BadString, "couldn't decode #{string} - got #{e}"
21
+ end
22
+ alias_method :decode, :marshal_decode
23
+
24
+ def marshal_encode(object)
25
+ b64_encode(Marshal.dump(object))
26
+ end
27
+ alias_method :encode, :marshal_encode
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ # require 'test_helper'
2
+ #
3
+ # class AbstractBundlerTest < Test::Unit::TestCase
4
+ # def setup
5
+ # @bundler = Fewer::Engines::Abstract.new('.', ['a.txt', 'b.txt'])
6
+ # end
7
+ #
8
+ # def test_root_to_a_pathname
9
+ # assert_kind_of Pathname, @bundler.root
10
+ # end
11
+ # end
data/test/app_test.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+
3
+ class AppTest < Test::Unit::TestCase
4
+ def setup
5
+ @app = stub
6
+ @engine = stub(
7
+ :check_request_extension => true,
8
+ :content_type => 'text/css',
9
+ :read => 'content'
10
+ )
11
+ @engine_klass = stub(:new => @engine)
12
+ @fewer_app = Fewer::App.new(@app, {
13
+ :engine => @engine_klass,
14
+ :mount => '/path',
15
+ :root => 'root'
16
+ })
17
+ @browser = Rack::Test::Session.new(Rack::MockSession.new(@fewer_app))
18
+ end
19
+
20
+ def test_initialises_a_new_engine_with_a_single_file
21
+ file = 'file'
22
+ @engine_klass.expects(:new).with('root', file).returns(@engine)
23
+ @browser.get "/path/#{encode(file)}.css"
24
+ end
25
+
26
+ def test_initialises_a_new_engine_with_multiple_files
27
+ files = ['file1', 'file2']
28
+ @engine_klass.expects(:new).with('root', files).returns(@engine)
29
+ @browser.get "/path/#{encode(files)}.css"
30
+ end
31
+
32
+ def test_responds_with_engine_content_type
33
+ @browser.get "/path/#{encode('file')}.css"
34
+ assert_equal 'text/css', @browser.last_response.content_type
35
+ end
36
+
37
+ def test_responds_with_cache_control
38
+ @browser.get "/path/#{encode('file')}.css"
39
+ assert_equal 'public, max-age=31536000', @browser.last_response.headers['Cache-Control']
40
+ end
41
+
42
+ def test_responds_with_bundled_content
43
+ @engine.expects(:read).returns('content')
44
+ @browser.get "/path/#{encode('file')}.css"
45
+ assert_equal 'content', @browser.last_response.body
46
+ end
47
+
48
+ def test_responds_with_200
49
+ @browser.get "/path/#{encode('file')}.css"
50
+ assert_equal 200, @browser.last_response.status
51
+ end
52
+
53
+ def test_responds_with_404_for_missing_source
54
+ @engine.stubs(:read).raises(Fewer::MissingSourceFileError)
55
+ @browser.get "/path/#{encode('file')}.css"
56
+ assert_equal 404, @browser.last_response.status
57
+ end
58
+
59
+ def test_responds_with_500_for_other_errors
60
+ @engine.stubs(:read).raises(RuntimeError)
61
+ @browser.get "/path/#{encode('file')}.css"
62
+ assert_equal 500, @browser.last_response.status
63
+ end
64
+
65
+ private
66
+ def decode(string)
67
+ Fewer::Serializer.decode(string)
68
+ end
69
+
70
+ def encode(obj)
71
+ Fewer::Serializer.encode(obj)
72
+ end
73
+ end
@@ -0,0 +1,7 @@
1
+ # require 'test_helper'
2
+ #
3
+ # class LessBundlerTest < Test::Unit::TestCase
4
+ # def test_a
5
+ #
6
+ # end
7
+ # end
@@ -0,0 +1,12 @@
1
+ require 'test/unit'
2
+
3
+ require 'rubygems'
4
+ require 'mocha'
5
+ require 'rack/test'
6
+
7
+ begin
8
+ require 'redgreen'
9
+ rescue LoadError
10
+ end
11
+
12
+ require 'fewer'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fewer
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ben Pickles
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-23 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rack
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: Rack middleware to bundle assets and help you make fewer HTTP requests.
36
+ email: spideryoung@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.md
44
+ files:
45
+ - .gitignore
46
+ - LICENSE
47
+ - README.md
48
+ - Rakefile
49
+ - VERSION
50
+ - lib/fewer.rb
51
+ - lib/fewer/app.rb
52
+ - lib/fewer/engines.rb
53
+ - lib/fewer/engines/abstract.rb
54
+ - lib/fewer/engines/css.rb
55
+ - lib/fewer/engines/less.rb
56
+ - lib/fewer/errors.rb
57
+ - lib/fewer/serializer.rb
58
+ - test/app_test.rb
59
+ - test/test_helper.rb
60
+ - test/abstract_bundler_test.rb
61
+ - test/less_bundler_test.rb
62
+ has_rdoc: true
63
+ homepage: http://github.com/benpickles/fewer
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --charset=UTF-8
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.3.7
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Rack middleware to bundle assets and help you make fewer HTTP requests.
96
+ test_files:
97
+ - test/abstract_bundler_test.rb
98
+ - test/app_test.rb
99
+ - test/less_bundler_test.rb
100
+ - test/test_helper.rb