pannier 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b499e4d8880444acab12e7c82236e70102645d60
4
+ data.tar.gz: 9fc7791e4d6f6d2de4a7aad64bae4b2249932241
5
+ SHA512:
6
+ metadata.gz: 46f440d5c44c3dfb30489c42532840afd4b397e0a6bdad58f35f8614a3dd3e39204b34b5b2007ce899465fe268b9e20e08db404116aeac197f89a44787f8a585
7
+ data.tar.gz: 9e208521ae8caa253f9a52899a7c4b0ead6d9618b7e166848a42e8d1bb87b965af0ad26483dbaf38fa583859b9cc2cb0a10c436e6d613d072d3f582d1f4b8ef9
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2013 Joe Corcoran
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,81 @@
1
+ # Pannier
2
+
3
+ [![Build Status](https://travis-ci.org/joecorcoran/pannier.png?branch=master)](https://travis-ci.org/joecorcoran/pannier) [![Code Climate](https://codeclimate.com/github/joecorcoran/pannier.png)](https://codeclimate.com/github/joecorcoran/pannier)
4
+
5
+ Pannier is a general-purpose Ruby asset processing tool. Its goal is to
6
+ work the same way in any Rack environment. No Rails glue, no mandatory
7
+ JavaScript or CSS libraries, preprocessors or gems.
8
+
9
+ ## Why?
10
+
11
+ The Rails asset pipeline essentially consists of [Sprockets][sprockets]
12
+ and a bunch of inscrutable Rails coupling. You generate a new Rails app
13
+ and everything is setup for you. We call this "convention over configuration".
14
+ It's a fine idea, but as soon as you need to ditch one of more of those
15
+ conventions you'll be frustrated.
16
+
17
+ I have found the
18
+ [principle of least astonishment][pola] to be far more valuable than
19
+ an *automagic* beginners' experience in the long run. This is especially
20
+ true where asset processing in Rails is concerned. I want explicit control
21
+ over my assets and I don't mind spending a small amount of time on
22
+ configuration.
23
+
24
+ ## What does it do?
25
+
26
+ The configuration DSL was inspired by — but is ultimately quite
27
+ different from — [rake-pipeline][rp]. The config describes a Rack
28
+ application that handles asset processing (modification of file contents
29
+ and file names, file concatenation). No decisions about
30
+ uglifiers/optimisers/preprocessors have been made; that part is up to you.
31
+ The interface to plug any of these libraries into Pannier is very simple
32
+ and inspired by Rack. The lack of a plugin ecosystem that binds you to any
33
+ particular preprocessor is considered a feature.
34
+
35
+ In some cases the above is all you'll need, but the application
36
+ can also be mounted (mapped to a path) within another Rack application, so
37
+ it can serve your assets too. In that case, there are helper methods for
38
+ including your assets in the view layer. Adding your own view helpers is
39
+ easy too.
40
+
41
+ ## Getting started
42
+
43
+ Create a config file in the root of your project named `.assets.rb`. The
44
+ example below will simply take all of your stylesheets from one directory
45
+ and concatenate them in another.
46
+
47
+
48
+ ```ruby
49
+ input 'assets' # Where your unprocessed assets live.
50
+ output 'public' # Where your processed assets will live.
51
+
52
+ package :styles do
53
+ input 'stylesheets' # Relative to `assets`.
54
+ assets '**/*.css' # Glob, relative to `assets/stylesheets`.
55
+
56
+ modify do |content, basename| # Do something to the file content here.
57
+ [quuxify(content), basename] # Return an array of content and basename.
58
+ end # This block is called once for each file.
59
+
60
+ concat 'main.min.css' # Concat into `public`.
61
+ end
62
+ ```
63
+
64
+ To understand further, you can [browse the current features on relish][relish].
65
+
66
+ ## Contributing
67
+
68
+ Yes, please contribute! Fork this repo, then send a pull request with a
69
+ note that clearly explains your changes. If you're unsure or you're
70
+ asking for a big change, just open an issue and we can chat about it first.
71
+
72
+ ## License
73
+
74
+ [MIT][license].
75
+
76
+ [sprockets]: https://github.com/sstephenson/sprockets
77
+ [pola]: http://en.wikipedia.org/wiki/Principle_of_least_astonishment
78
+ [rp]: https://github.com/livingsocial/rake-pipeline
79
+ [relish]: https://www.relishapp.com/joecorcoran/pannier/docs
80
+ [todo]: https://github.com/joecorcoran/pannier/wiki/Todo
81
+ [license]: https://github.com/joecorcoran/pannier/blob/master/LICENSE.txt
data/bin/pannier ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
4
+ require 'pannier/cli'
5
+
6
+ Pannier::CLI.new(ARGV.dup).run!
data/lib/pannier.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'multi_json'
2
+
3
+ require 'pannier/app'
4
+ require 'pannier/version'
5
+
6
+ module Pannier
7
+ def self.build(env_name = 'development', &block)
8
+ App.build(env_name, &block)
9
+ end
10
+
11
+ def self.load(path, env_name)
12
+ config = File.read(path)
13
+ block = eval("proc { #{config} }", TOPLEVEL_BINDING, path, 0)
14
+ App.build(env_name, &block)
15
+ end
16
+
17
+ def self.prime(path, env_name = 'development')
18
+ app = load(path, env_name)
19
+ if manifest_exists?(app, env_name)
20
+ manifest = load_manifest(app, env_name)
21
+ app.prime!(manifest)
22
+ end
23
+ app
24
+ end
25
+
26
+ def self.rackup!(ru, path = './.assets.rb')
27
+ app = prime(path, ENV['RACK_ENV'])
28
+ ru.map(app.root) { run(app) }
29
+ app
30
+ end
31
+
32
+ private
33
+
34
+ def self.load_manifest(app, env_name)
35
+ path = File.join(app.input_path, ".assets.#{env_name}.json")
36
+ json = File.read(path)
37
+ MultiJson.load(json, :symbolize_keys => true)
38
+ end
39
+
40
+ def self.manifest_exists?(app, env_name)
41
+ path = File.join(app.input_path, ".assets.#{env_name}.json")
42
+ File.exists?(path)
43
+ end
44
+ end
@@ -0,0 +1,89 @@
1
+ require 'ostruct'
2
+
3
+ require 'pannier/dsl'
4
+ require 'pannier/environment'
5
+ require 'pannier/manifest_writer'
6
+ require 'pannier/package'
7
+
8
+ module Pannier
9
+ class App
10
+ extend DSL
11
+
12
+ attr_reader :env, :root, :input_path, :output_path,
13
+ :behaviors, :packages
14
+
15
+ def initialize(env_name = 'development')
16
+ @env = Environment.new(env_name)
17
+ @behaviors, @packages, @root = {}, [], '/'
18
+ end
19
+
20
+ def set_root(path)
21
+ @root = path
22
+ end
23
+
24
+ def set_input(path)
25
+ @input_path = File.expand_path(path)
26
+ end
27
+
28
+ def set_output(path)
29
+ @output_path = File.expand_path(path)
30
+ end
31
+
32
+ def add_package(package)
33
+ @packages << package
34
+ end
35
+
36
+ def [](package_name)
37
+ @packages.find { |pkg| pkg.name == package_name }
38
+ end
39
+
40
+ def manifest_writer
41
+ @manifest_writer ||= ManifestWriter.new(self, @env)
42
+ end
43
+
44
+ def process!
45
+ @packages.each(&:process!)
46
+ manifest_writer.write!(@input_path)
47
+ end
48
+
49
+ def process_owners!(*paths)
50
+ pkgs = @packages.select { |pkg| pkg.owns_any?(*paths) }
51
+ pkgs.each(&:process!)
52
+ end
53
+
54
+ dsl do
55
+
56
+ def _
57
+ @locals ||= OpenStruct.new(:env => env.name)
58
+ end
59
+
60
+ def root(path)
61
+ set_root(path)
62
+ end
63
+
64
+ def input(path)
65
+ set_input(path)
66
+ end
67
+
68
+ def output(path)
69
+ set_output(path)
70
+ end
71
+
72
+ def behavior(name, &block)
73
+ add_behavior(name, &block)
74
+ end
75
+
76
+ def package(name, &block)
77
+ add_package(Package.build(name, __getobj__, &block))
78
+ end
79
+
80
+ private
81
+
82
+ def add_behavior(name, &block)
83
+ self.behaviors[name] = block
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,55 @@
1
+ require 'fileutils'
2
+ require 'pathname'
3
+
4
+ module Pannier
5
+ class Asset
6
+ include Comparable
7
+
8
+ attr_accessor :basename, :dirname, :content
9
+
10
+ def initialize(basename, dirname, package)
11
+ @basename, @dirname, @package = basename, dirname, package
12
+ @content = original_content
13
+ end
14
+
15
+ def path
16
+ File.join(@dirname, @basename)
17
+ end
18
+
19
+ def original_content
20
+ return unless File.exists?(path)
21
+ @original_content ||= File.read(path)
22
+ end
23
+
24
+ def eql?(other)
25
+ path == other.path
26
+ end
27
+
28
+ def hash
29
+ path.hash
30
+ end
31
+
32
+ def <=>(other)
33
+ path <=> other.path
34
+ end
35
+
36
+ def copy_to(to_dirname)
37
+ copy = self.dup
38
+ copy.content = content.dup if content
39
+ copy.dirname = to_dirname
40
+ copy
41
+ end
42
+
43
+ def modify!(modifier)
44
+ modified = modifier.call(content, basename)
45
+ self.content, self.basename = modified
46
+ end
47
+
48
+ def write_file!
49
+ FileUtils.mkdir_p(@dirname)
50
+ File.open(path, 'w+') do |file|
51
+ file << content
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,85 @@
1
+ require 'slop'
2
+
3
+ require 'pannier'
4
+
5
+ module Pannier
6
+ class CLI
7
+ def initialize(args, stdin = $stdin, stdout = $stdout, stderr = $stderr)
8
+ @args, @stdin, @stdout, @stderr = args, stdin, stdout, stderr
9
+ end
10
+
11
+ def run!
12
+ command, opts = (@args.shift || 'usage').to_sym, @args
13
+ public_send(command, *opts)
14
+ end
15
+
16
+ def process(*opts)
17
+ opts = Slop.parse(opts, :help => true, :ignore_case => true) do
18
+ banner 'Usage: pannier process [options]'
19
+ on :c, :config, 'Config file', :argument => :optional, :default => '.assets.rb'
20
+ on :e, :env, 'Host environment', :argument => :optional, :default => 'development'
21
+ on :a, :assets, 'Asset paths', :argument => :optional, :as => Array
22
+ end
23
+
24
+ config_path = File.expand_path(opts[:config])
25
+ err(no_config_msg(config_path)) && abort unless File.exists?(config_path)
26
+
27
+ app = Pannier.load(config_path, opts[:env])
28
+
29
+ if opts.assets?
30
+ paths = opts[:assets].map { |path| File.expand_path(path) }
31
+ app.process_owners!(*paths)
32
+ else
33
+ app.process!
34
+ end
35
+ exit
36
+ end
37
+
38
+ def usage
39
+ out(usage_msg)
40
+ exit
41
+ end
42
+
43
+ def method_missing(command, *args)
44
+ err(<<-txt)
45
+ You ran `pannier #{command}#{(' ' + args.join(' ')) unless args.empty?}`.
46
+ Pannier has no command named "#{command}".
47
+ txt
48
+ exit(127)
49
+ end
50
+
51
+ private
52
+
53
+ def no_config_msg(path)
54
+ <<-txt
55
+ Pannier config file not found at #{path}.
56
+ txt
57
+ end
58
+
59
+ def usage_msg
60
+ <<-txt
61
+ Available commands (run any command with --help for details):
62
+ pannier process Process assets
63
+ pannier usage Show this list of commands
64
+ txt
65
+ end
66
+
67
+ def out(*msgs)
68
+ msg = msgs.map { |m| format_output(m) }.join
69
+ @stdout.puts(msg)
70
+ end
71
+
72
+ def err(*msgs)
73
+ msg = msgs.map { |m| format_output(m) }.join
74
+ msg += format_output(usage_msg)
75
+ @stderr.puts(msg)
76
+ end
77
+
78
+ def format_output(txt)
79
+ spaces = '^[ \t]'
80
+ indent = txt.scan(/#{spaces}*(?=\S)/).min
81
+ indent_size = indent ? indent.size : 0
82
+ txt.gsub(/#{spaces}{#{indent_size}}/, '')
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,9 @@
1
+ module Pannier
2
+ class Concatenator
3
+
4
+ def call(content_array)
5
+ content_array.join("\n")
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ require 'delegate'
2
+ require 'pannier/errors'
3
+
4
+ module Pannier
5
+ module DSL
6
+
7
+ def build(*args, &block)
8
+ base = self.new(*args)
9
+ delegator_klass = self.const_get('DSLDelegator')
10
+ delegator = delegator_klass.new(base)
11
+ delegator.instance_eval(&block)
12
+ base
13
+ end
14
+
15
+ def dsl(&block)
16
+ begin
17
+ delegator_klass = self.const_get('DSLDelegator')
18
+ delegator_klass.class_eval(&block)
19
+ rescue NameError
20
+ delegator_klass = Class.new(SimpleDelegator, &block)
21
+ self.const_set('DSLDelegator', delegator_klass)
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ module Pannier
2
+ class Environment
3
+
4
+ attr_reader :name
5
+
6
+ def initialize(name)
7
+ @name = name
8
+ end
9
+
10
+ def is?(expression)
11
+ expression = Regexp.new(expression)
12
+ @name =~ expression
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Pannier
2
+ class MissingBehavior < ArgumentError
3
+ def initialize(msg)
4
+ @msg = msg
5
+ end
6
+
7
+ def message
8
+ "No behavior #{@msg.inspect} was found. Maybe you need to add one?"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Pannier
2
+ class FileHandler < Rack::File
3
+
4
+ def initialize(allowed, *args)
5
+ @allowed = allowed
6
+ super(*args)
7
+ end
8
+
9
+ def call(env)
10
+ return fail(404, 'File not a member of this package') unless handle?(env)
11
+ super(env)
12
+ end
13
+
14
+ def handle?(env)
15
+ path = Rack::Utils.unescape(env['PATH_INFO'])
16
+ @allowed.include?(File.join(@root, path))
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,32 @@
1
+ require 'fileutils'
2
+ require 'multi_json'
3
+
4
+ require 'pannier/report'
5
+
6
+ module Pannier
7
+ class ManifestWriter
8
+
9
+ def initialize(app, env)
10
+ @app, @env = app, env
11
+ @report = Report.new(@app)
12
+ end
13
+
14
+ def basename
15
+ ".assets.#{@env.name}.json"
16
+ end
17
+
18
+ def content
19
+ @report.build!
20
+ MultiJson.dump(@report.tree)
21
+ end
22
+
23
+ def write!(dir_path)
24
+ @report.build!
25
+ FileUtils.mkdir_p(dir_path)
26
+ File.open(File.join(dir_path, basename), 'w+') do |f|
27
+ f << content
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ require 'rack'
2
+
3
+ require 'pannier'
4
+ require 'pannier/mounted/app'
5
+ require 'pannier/mounted/asset'
6
+ require 'pannier/mounted/package'
7
+ require 'pannier/mounted/tags'
@@ -0,0 +1,32 @@
1
+ require 'pannier/app'
2
+
3
+ module Pannier
4
+ class App
5
+
6
+ def prime!(manifest)
7
+ manifest.each do |name, paths|
8
+ if (pkg = self[name])
9
+ assets = pkg.build_assets_from_paths(paths)
10
+ pkg.add_output_assets(assets)
11
+ end
12
+ end
13
+ end
14
+
15
+ def handler_map
16
+ @packages.reduce({}) do |hash, pkg|
17
+ hash[pkg.handler_path] ||= Rack::Cascade.new([])
18
+ hash[pkg.handler_path].add(pkg.handler)
19
+ hash
20
+ end
21
+ end
22
+
23
+ def handler
24
+ Rack::URLMap.new(handler_map)
25
+ end
26
+
27
+ def call(env)
28
+ handler.call(env)
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ require 'pannier/asset'
2
+
3
+ module Pannier
4
+ class Asset
5
+
6
+ def serve_from(app)
7
+ asset_path, app_output_path = Pathname.new(path), Pathname.new(app.output_path)
8
+ relative_path = asset_path.relative_path_from(app_output_path)
9
+ File.join(app.root, relative_path.to_s)
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ require 'pannier/file_handler'
2
+ require 'pannier/package'
3
+
4
+ module Pannier
5
+ class Package
6
+
7
+ def add_middleware(middleware, *args, &block)
8
+ @middlewares << proc { |app| middleware.new(app, *args, &block) }
9
+ end
10
+
11
+ def handler
12
+ handler_with_middlewares(output_assets.map(&:path), full_output_path)
13
+ end
14
+
15
+ def handler_path
16
+ build_handler_path(output_path)
17
+ end
18
+
19
+ def build_handler_path(handler_path)
20
+ hp = handler_path || '/'
21
+ hp.insert(0, '/') unless hp[0] == '/'
22
+ hp
23
+ end
24
+
25
+ def handler_with_middlewares(paths, full_path)
26
+ handler = FileHandler.new(paths, full_path)
27
+ return handler if @middlewares.empty?
28
+ @middlewares.reverse.reduce(handler) { |app, proc| proc.call(app) }
29
+ end
30
+
31
+ dsl do
32
+
33
+ def use(*args, &block)
34
+ add_middleware(*args, &block)
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,75 @@
1
+ require 'erb'
2
+
3
+ module Pannier
4
+ class Tags
5
+
6
+ attr_reader :app
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ def write(package_name, attrs = {})
13
+ template_klass = attrs.delete(:as)
14
+ template = template_klass.new
15
+ to_write = @app[package_name].output_assets.map do |asset|
16
+ template.call(asset.serve_from(@app), attrs)
17
+ end
18
+ to_write.join("\n")
19
+ end
20
+
21
+ module Helpers
22
+ def attrs_to_s(hash)
23
+ pairs = hash.reduce([]) do |arr, pair|
24
+ key, value = escape(pair[0].to_s), escape(pair[1].to_s)
25
+ arr << "#{key}=\"#{value}\""
26
+ end
27
+ pairs.sort.join(' ')
28
+ end
29
+
30
+ def escape(string)
31
+ ERB::Util.html_escape(string)
32
+ end
33
+ end
34
+
35
+ class JavaScript
36
+ include Helpers
37
+
38
+ def call(path, attrs)
39
+ attrs = attrs_to_s({
40
+ :type => 'text/javascript',
41
+ :src => path
42
+ }.merge(attrs))
43
+
44
+ template.result(binding)
45
+ end
46
+
47
+ def template
48
+ ERB.new(<<-erb.strip)
49
+ <script <%= attrs %>></script>
50
+ erb
51
+ end
52
+ end
53
+
54
+ class CSS
55
+ include Helpers
56
+
57
+ def call(path, attrs)
58
+ attrs = attrs_to_s({
59
+ :rel => 'stylesheet',
60
+ :type => 'text/css',
61
+ :href => path
62
+ }.merge(attrs))
63
+
64
+ template.result(binding)
65
+ end
66
+
67
+ def template
68
+ ERB.new(<<-erb.strip)
69
+ <link <%= attrs %> />
70
+ erb
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,144 @@
1
+ require 'delegate'
2
+ require 'ostruct'
3
+ require 'pathname'
4
+ require 'set'
5
+
6
+ require 'pannier/asset'
7
+ require 'pannier/concatenator'
8
+ require 'pannier/dsl'
9
+ require 'pannier/errors'
10
+
11
+ module Pannier
12
+ class Package
13
+ extend DSL
14
+
15
+ attr_reader :name, :app, :input_assets, :output_assets, :input_path,
16
+ :output_path, :middlewares, :processors
17
+
18
+ def initialize(name, app)
19
+ @name, @app = name, app
20
+ @input_assets, @output_assets = SortedSet.new, SortedSet.new
21
+ @middlewares, @processors = [], []
22
+ end
23
+
24
+ def set_input(path)
25
+ @input_path = path
26
+ end
27
+
28
+ def set_output(path)
29
+ @output_path = path
30
+ end
31
+
32
+ def full_input_path
33
+ File.expand_path(File.join(*[@app.input_path, @input_path].compact))
34
+ end
35
+
36
+ def full_output_path
37
+ File.expand_path(File.join(*[@app.output_path, @output_path].compact))
38
+ end
39
+
40
+ def build_assets_from_paths(paths)
41
+ paths.map do |path|
42
+ pathname = Pathname.new(path)
43
+ Asset.new(pathname.basename.to_s, pathname.dirname.to_s, self)
44
+ end
45
+ end
46
+
47
+ def add_input_assets(assets)
48
+ @input_assets.merge(assets)
49
+ end
50
+
51
+ def add_output_assets(assets)
52
+ @output_assets.merge(assets)
53
+ end
54
+
55
+ def add_modifiers(modifiers)
56
+ @processors += modifiers.map { |m| [:modify!, m] }
57
+ end
58
+
59
+ def add_concatenator(concat_name, concatenator = Concatenator.new)
60
+ @processors << [:concat!, concat_name, concatenator]
61
+ end
62
+
63
+ def owns_any?(*paths)
64
+ @input_assets.any? { |a| paths.include?(a.path) }
65
+ end
66
+
67
+ def process!
68
+ copy!
69
+ !@processors.empty? && @processors.each do |instructions|
70
+ send(*instructions)
71
+ end
72
+ write_files!
73
+ end
74
+
75
+ def modify!(modifier)
76
+ @output_assets.each do |asset|
77
+ asset.modify!(modifier)
78
+ end
79
+ end
80
+
81
+ def concat!(concat_name, concatenator)
82
+ asset = Asset.new(concat_name, full_output_path, self)
83
+ asset.content = concatenator.call(@output_assets.map(&:content))
84
+ @output_assets.replace([asset])
85
+ end
86
+
87
+ def copy!
88
+ assets = @input_assets.map do |asset|
89
+ asset.copy_to(full_output_path)
90
+ end
91
+ @output_assets.replace(assets)
92
+ end
93
+
94
+ def write_files!
95
+ @output_assets.each(&:write_file!)
96
+ end
97
+
98
+ dsl do
99
+
100
+ def _
101
+ @locals ||= OpenStruct.new(:env => app.env.name)
102
+ end
103
+
104
+ def input(path)
105
+ set_input(path)
106
+ end
107
+
108
+ def output(path)
109
+ set_output(path)
110
+ end
111
+
112
+ def behave(*names)
113
+ names.each do |name|
114
+ behavior = self.app.behaviors[name]
115
+ raise MissingBehavior.new(name) if behavior.nil?
116
+ self.instance_eval(&behavior)
117
+ end
118
+ end
119
+
120
+ def assets(*patterns)
121
+ patterns.each do |pattern|
122
+ paths = Dir[File.join(full_input_path, pattern)]
123
+ assets = build_assets_from_paths(paths)
124
+ add_input_assets(assets)
125
+ end
126
+ end
127
+
128
+ def modify(*modifiers, &block)
129
+ modifiers << block if block_given?
130
+ add_modifiers(modifiers)
131
+ end
132
+
133
+ def concat(*args)
134
+ add_concatenator(*args)
135
+ end
136
+
137
+ def env(expression, &block)
138
+ self.instance_eval(&block) if self.app.env.is?(expression)
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+ end
@@ -0,0 +1,26 @@
1
+ module Pannier
2
+ class Report
3
+
4
+ attr_reader :tree
5
+
6
+ def initialize(app, base_url = '')
7
+ @app, @base_url, @tree = app, base_url, {}
8
+ build!
9
+ end
10
+
11
+ def build!
12
+ @app.packages.each do |package|
13
+ @tree[package.name] ||= []
14
+ next if package.output_assets.empty?
15
+ @tree[package.name] = package.output_assets.map(&:path)
16
+ end
17
+ self
18
+ end
19
+
20
+ def lookup(package_name)
21
+ return @tree if package_name.nil?
22
+ @tree[package_name.to_sym]
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Pannier
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,250 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pannier
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joe Corcoran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.5.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.5'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.5.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: multi_json
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.7'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.7.9
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.7'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.7.9
53
+ - !ruby/object:Gem::Dependency
54
+ name: slop
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '3.4'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.4.6
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '3.4'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 3.4.6
73
+ - !ruby/object:Gem::Dependency
74
+ name: rake
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '10.1'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 10.1.0
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.1'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 10.1.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: rspec
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '2.14'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 2.14.1
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '2.14'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 2.14.1
113
+ - !ruby/object:Gem::Dependency
114
+ name: mocha
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '0.14'
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 0.14.0
123
+ type: :development
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '0.14'
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 0.14.0
133
+ - !ruby/object:Gem::Dependency
134
+ name: cucumber
135
+ requirement: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - "~>"
138
+ - !ruby/object:Gem::Version
139
+ version: '1.3'
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 1.3.6
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - "~>"
148
+ - !ruby/object:Gem::Version
149
+ version: '1.3'
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: 1.3.6
153
+ - !ruby/object:Gem::Dependency
154
+ name: aruba
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.5'
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: 0.5.3
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '0.5'
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 0.5.3
173
+ - !ruby/object:Gem::Dependency
174
+ name: rack-test
175
+ requirement: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '0.6'
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: 0.6.2
183
+ type: :development
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - "~>"
188
+ - !ruby/object:Gem::Version
189
+ version: '0.6'
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: 0.6.2
193
+ description: |
194
+ Pannier is a Ruby tool for the processing of web assets like CSS
195
+ and JavaScript files, both programatically and from the command line. It can
196
+ be used as a standalone asset organizer or mounted within any Rack-compatible
197
+ application.
198
+ email:
199
+ - joecorcoran@gmail.com
200
+ executables:
201
+ - pannier
202
+ extensions: []
203
+ extra_rdoc_files: []
204
+ files:
205
+ - LICENSE.txt
206
+ - README.md
207
+ - bin/pannier
208
+ - lib/pannier.rb
209
+ - lib/pannier/app.rb
210
+ - lib/pannier/asset.rb
211
+ - lib/pannier/cli.rb
212
+ - lib/pannier/concatenator.rb
213
+ - lib/pannier/dsl.rb
214
+ - lib/pannier/environment.rb
215
+ - lib/pannier/errors.rb
216
+ - lib/pannier/file_handler.rb
217
+ - lib/pannier/manifest_writer.rb
218
+ - lib/pannier/mounted.rb
219
+ - lib/pannier/mounted/app.rb
220
+ - lib/pannier/mounted/asset.rb
221
+ - lib/pannier/mounted/package.rb
222
+ - lib/pannier/mounted/tags.rb
223
+ - lib/pannier/package.rb
224
+ - lib/pannier/report.rb
225
+ - lib/pannier/version.rb
226
+ homepage: http://github.com/joecorcoran/pannier
227
+ licenses:
228
+ - MIT
229
+ metadata: {}
230
+ post_install_message:
231
+ rdoc_options: []
232
+ require_paths:
233
+ - lib
234
+ required_ruby_version: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - ">="
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
239
+ required_rubygems_version: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ requirements: []
245
+ rubyforge_project:
246
+ rubygems_version: 2.2.1
247
+ signing_key:
248
+ specification_version: 4
249
+ summary: A simple, portable asset processing tool for Ruby web apps
250
+ test_files: []