weld 0.0.1.dev.20100402 → 0.0.1.dev.20100404
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/bin/weld +24 -47
- data/lib/weld.rb +26 -77
- data/lib/weld/cache.rb +65 -0
- data/lib/weld/cdn.rb +12 -0
- data/lib/weld/cdn/s3.rb +57 -0
- data/lib/weld/cli.rb +73 -0
- data/lib/weld/component.rb +89 -0
- data/lib/weld/compressor.rb +2 -3
- data/lib/weld/server.rb +6 -22
- data/lib/weld/version.rb +1 -1
- metadata +17 -2
    
        data/bin/weld
    CHANGED
    
    | @@ -1,63 +1,40 @@ | |
| 1 1 | 
             
            #!/usr/bin/env ruby
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'rubygems'
         | 
| 4 | 
            -
            require 'trollop'
         | 
| 5 4 | 
             
            require 'weld'
         | 
| 6 5 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
              version "#{Weld::APP_NAME} #{Weld::APP_VERSION}\n" << Weld::APP_COPYRIGHT
         | 
| 9 | 
            -
              banner <<-EOS
         | 
| 10 | 
            -
            Weld combines and minifies CSS and JavaScript files at runtime and build time.
         | 
| 6 | 
            +
            cli = Weld::CLI.new
         | 
| 11 7 |  | 
| 12 | 
            -
             | 
| 13 | 
            -
               | 
| 8 | 
            +
            begin
         | 
| 9 | 
            +
              cli.components.each do |name|
         | 
| 10 | 
            +
                cli.info "Welding #{name}"
         | 
| 14 11 |  | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 12 | 
            +
                component = cli.weld.component(name)
         | 
| 13 | 
            +
                suffix    = Time.now.strftime('-%Y%m%d%H%M')
         | 
| 17 14 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
              opt :no_minify,  "Don't perform any minification.", :short => :none
         | 
| 20 | 
            -
              opt :output_dir, "Write welded files to the specified directory.", :short => '-o', :default => './'
         | 
| 21 | 
            -
              opt :type,       "Only weld components of the specified type ('css' or 'js').", :short => '-t', :type => :string
         | 
| 22 | 
            -
            end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
            if options[:type_given] && !['css', 'js'].include?(options[:type])
         | 
| 25 | 
            -
              abort "Error: Unsupported component type: #{options[:type]}\n" <<
         | 
| 26 | 
            -
                  "Try --help for help."
         | 
| 27 | 
            -
            end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
            weld = Weld.new(options[:config])
         | 
| 30 | 
            -
             | 
| 31 | 
            -
            components = ARGV.length > 0 ? ARGV : @weld.config['components'] || []
         | 
| 32 | 
            -
            type       = options[:type].to_sym if options[:type_given]
         | 
| 33 | 
            -
             | 
| 34 | 
            -
            components.each do |name|
         | 
| 35 | 
            -
              puts "Welding #{name}"
         | 
| 36 | 
            -
             | 
| 37 | 
            -
              component = weld.component(name)
         | 
| 38 | 
            -
              suffix    = Time.now.strftime('-%Y%m%d%H%M')
         | 
| 39 | 
            -
             | 
| 40 | 
            -
              if type
         | 
| 41 | 
            -
                next if component.method(type).call.empty?
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                filename = "#{File.join(options[:output_dir], name)}#{suffix}.#{type}"
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                File.open(filename, 'w') do |file|
         | 
| 46 | 
            -
                  puts "--> #{filename}"
         | 
| 47 | 
            -
                  file.write(options[:no_minify] ? component.merge(type) : component.compress(type))
         | 
| 48 | 
            -
                end
         | 
| 49 | 
            -
              else
         | 
| 50 | 
            -
                [:css, :js].each do |type|
         | 
| 15 | 
            +
                cli.types.each do |type|
         | 
| 51 16 | 
             
                  next if component.method(type).call.empty?
         | 
| 52 17 |  | 
| 53 | 
            -
                  filename | 
| 18 | 
            +
                  filename  = "#{name}#{suffix}.#{type}"
         | 
| 19 | 
            +
                  content   = cli.options[:no_minify] ? component.merge(type) : component.compress(type)
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  if cli.options[:push]
         | 
| 22 | 
            +
                    cdn_url = cli.cdn_push(filename, type, content)
         | 
| 23 | 
            +
                    cli.info "--> #{cdn_url}"
         | 
| 24 | 
            +
                  else
         | 
| 25 | 
            +
                    full_path = File.join(cli.options[:output_dir], filename)
         | 
| 54 26 |  | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 27 | 
            +
                    File.open(full_path, 'w') do |file|
         | 
| 28 | 
            +
                      cli.info "--> #{full_path}"
         | 
| 29 | 
            +
                      file.write(content)
         | 
| 30 | 
            +
                    end
         | 
| 58 31 | 
             
                  end
         | 
| 59 32 | 
             
                end
         | 
| 60 33 | 
             
              end
         | 
| 61 34 |  | 
| 62 | 
            -
             | 
| 35 | 
            +
            # rescue => ex
         | 
| 36 | 
            +
            #   cli.error ex.message
         | 
| 37 | 
            +
            #   exit(1)
         | 
| 63 38 | 
             
            end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            puts
         | 
    
        data/lib/weld.rb
    CHANGED
    
    | @@ -1,21 +1,36 @@ | |
| 1 | 
            -
            require ' | 
| 2 | 
            -
            require 'pathname'
         | 
| 1 | 
            +
            require 'fileutils'
         | 
| 3 2 |  | 
| 4 | 
            -
             | 
| 3 | 
            +
            class Weld
         | 
| 4 | 
            +
              HOME_DIR = ENV['WELD_HOME'] || File.join(File.expand_path('~/'), '.weld')
         | 
| 5 | 
            +
            end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require 'weld/cache'
         | 
| 8 | 
            +
            require 'weld/component'
         | 
| 5 9 | 
             
            require 'weld/version'
         | 
| 6 10 |  | 
| 7 11 | 
             
            class Weld
         | 
| 8 | 
            -
              autoload : | 
| 12 | 
            +
              autoload :CDN,        'weld/cdn'
         | 
| 13 | 
            +
              autoload :CLI,        'weld/cli'
         | 
| 14 | 
            +
              autoload :Compressor, 'weld/compressor'
         | 
| 15 | 
            +
              autoload :Server,     'weld/server'
         | 
| 9 16 |  | 
| 10 | 
            -
              attr_reader :config, :config_file
         | 
| 17 | 
            +
              attr_reader :cache, :config, :config_file
         | 
| 11 18 |  | 
| 12 19 | 
             
              def initialize(config_file)
         | 
| 13 20 | 
             
                # TODO: handle exceptions
         | 
| 14 21 | 
             
                @config      = YAML::load_file(config_file)
         | 
| 15 22 | 
             
                @config_file = config_file
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # Create home dir if it doesn't exist.
         | 
| 25 | 
            +
                FileUtils.mkdir_p(HOME_DIR)
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                @cache           = Cache.new
         | 
| 28 | 
            +
                @component_cache = {}
         | 
| 16 29 | 
             
              end
         | 
| 17 30 |  | 
| 18 31 | 
             
              def component(name)
         | 
| 32 | 
            +
                return @component_cache[name.to_sym] if @component_cache.has_key?(name.to_sym)
         | 
| 33 | 
            +
             | 
| 19 34 | 
             
                unless definition = @config['components'][name]
         | 
| 20 35 | 
             
                  raise ComponentNotFoundError, "Component not found: #{name}"
         | 
| 21 36 | 
             
                end
         | 
| @@ -36,83 +51,17 @@ class Weld | |
| 36 51 | 
             
                css += definition['css'] || []
         | 
| 37 52 | 
             
                js  += definition['js'] || []
         | 
| 38 53 |  | 
| 39 | 
            -
                Component.new(self, name, | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
                 | 
| 44 | 
            -
                attr_accessor :css, :js
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                def initialize(weld, name, config = {})
         | 
| 47 | 
            -
                  raise ArgumentError, "weld must be a Weld instance" unless weld.is_a?(Weld)
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                  @weld     = weld
         | 
| 50 | 
            -
                  @name     = name
         | 
| 51 | 
            -
                  @css      = config[:css] || []
         | 
| 52 | 
            -
                  @js       = config[:js] || []
         | 
| 53 | 
            -
                  @requires = config[:requires] || []
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                  @file_cache = {}
         | 
| 56 | 
            -
                end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                def compress(type)
         | 
| 59 | 
            -
                  compressors = @weld.config['compressors']
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                  unless compressors && compressors[type.to_s] &&
         | 
| 62 | 
            -
                      compressor_name = compressors[type.to_s]['name'].capitalize.to_sym
         | 
| 63 | 
            -
                    raise CompressorError, "No compressor configured"
         | 
| 64 | 
            -
                  end
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                  options    = compressors[type.to_s]['options'] || {}
         | 
| 67 | 
            -
                  compressor = Compressor.const_get(compressor_name).new(type, options)
         | 
| 68 | 
            -
             | 
| 69 | 
            -
                  compressor.compress(merge(type))
         | 
| 70 | 
            -
                end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                def merge(type)
         | 
| 73 | 
            -
                  raise UnsupportedFileTypeError, "Unsupported file type: #{type}" unless [:css, :js].include?(type.to_sym)
         | 
| 74 | 
            -
             | 
| 75 | 
            -
                  content = ''
         | 
| 76 | 
            -
                  method(type.to_sym).call.each {|f| content << read_file(f) + "\n" }
         | 
| 77 | 
            -
                  content
         | 
| 78 | 
            -
                end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                private
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                def read_file(filename)
         | 
| 83 | 
            -
                  filename = resolve_file(filename)
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                  if filename.is_a?(URI)
         | 
| 86 | 
            -
                    open(filename, 'User-Agent' => "#{APP_NAME}/#{APP_VERSION}").read
         | 
| 87 | 
            -
                  else
         | 
| 88 | 
            -
                    File.read(filename)
         | 
| 89 | 
            -
                  end
         | 
| 90 | 
            -
                end
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                def resolve_file(filename)
         | 
| 93 | 
            -
                  return @file_cache[filename] if @file_cache.has_key?(filename)
         | 
| 94 | 
            -
             | 
| 95 | 
            -
                  if filename =~ /^(?:https?|ftp):\/\//
         | 
| 96 | 
            -
                    @file_cache[filename] = URI.parse(filename)
         | 
| 97 | 
            -
                  else
         | 
| 98 | 
            -
                    (@weld.config['sourcePaths'] || []).each do |source_path|
         | 
| 99 | 
            -
                      full_path = if Pathname.new(source_path).relative?
         | 
| 100 | 
            -
                        File.join(File.dirname(@weld.config_file), source_path, filename)
         | 
| 101 | 
            -
                      else
         | 
| 102 | 
            -
                        File.join(source_path, filename)
         | 
| 103 | 
            -
                      end
         | 
| 104 | 
            -
             | 
| 105 | 
            -
                      return @file_cache[filename] = File.expand_path(full_path) if File.exist?(full_path)
         | 
| 106 | 
            -
                    end
         | 
| 107 | 
            -
             | 
| 108 | 
            -
                    raise FileNotFoundError, "File not found: #{filename}"
         | 
| 109 | 
            -
                  end
         | 
| 110 | 
            -
                end
         | 
| 54 | 
            +
                @component_cache[name.to_sym] = Component.new(self, name,
         | 
| 55 | 
            +
                    :css      => css,
         | 
| 56 | 
            +
                    :js       => js,
         | 
| 57 | 
            +
                    :requires => requires
         | 
| 58 | 
            +
                )
         | 
| 111 59 | 
             
              end
         | 
| 112 60 |  | 
| 113 61 | 
             
              class Error < StandardError; end
         | 
| 114 62 | 
             
              class ComponentNotFoundError < Error; end
         | 
| 115 63 | 
             
              class CompressorError < Error; end
         | 
| 64 | 
            +
              class ConfigError < Error; end
         | 
| 116 65 | 
             
              class FileNotFoundError < Error; end
         | 
| 117 66 | 
             
              class UnsupportedFileTypeError < Error; end
         | 
| 118 67 | 
             
            end
         | 
    
        data/lib/weld/cache.rb
    ADDED
    
    | @@ -0,0 +1,65 @@ | |
| 1 | 
            +
            require 'digest/sha1'
         | 
| 2 | 
            +
            require 'time'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class Weld::Cache
         | 
| 5 | 
            +
              CACHE_DIR = File.join(Weld::HOME_DIR, 'cache')
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              def initialize
         | 
| 8 | 
            +
                # Create the cache directory if it doesn't exist.
         | 
| 9 | 
            +
                FileUtils.mkdir_p(CACHE_DIR)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                purge_expired
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              def fetch(key)
         | 
| 15 | 
            +
                now = Time.now
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                Dir["#{CACHE_DIR}/weld.*.#{key_hash(key)}"].each do |filename|
         | 
| 18 | 
            +
                  next unless meta = parse_filename(filename)
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  if meta[:expires] <= now
         | 
| 21 | 
            +
                    File.delete(filename)
         | 
| 22 | 
            +
                  else
         | 
| 23 | 
            +
                    return File.read(filename)
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                nil
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              alias [] fetch
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def store(key, value, expires = Time.now + 1800)
         | 
| 33 | 
            +
                File.open(File.join(CACHE_DIR, "weld.#{expires.to_i}.#{key_hash(key)}"), 'w') do |f|
         | 
| 34 | 
            +
                  f.write(value)
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                value
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              alias []= store
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              private
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def key_hash(key)
         | 
| 45 | 
            +
                Digest::SHA1.hexdigest(key.to_s)
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def parse_filename(filename)
         | 
| 49 | 
            +
                if File.basename(filename) =~ /^weld\.([0-9]+)\.([0-9a-f]{40})$/
         | 
| 50 | 
            +
                  {:expires => Time.at($1.to_i), :hash => $2}
         | 
| 51 | 
            +
                else
         | 
| 52 | 
            +
                  nil
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              # Deletes expired files from the cache.
         | 
| 57 | 
            +
              def purge_expired
         | 
| 58 | 
            +
                now = Time.now
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                Dir["#{CACHE_DIR}/weld.*"].each do |filename|
         | 
| 61 | 
            +
                  next unless meta = parse_filename(filename)
         | 
| 62 | 
            +
                  File.delete(filename) if meta[:expires] <= now
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end
         | 
    
        data/lib/weld/cdn.rb
    ADDED
    
    
    
        data/lib/weld/cdn/s3.rb
    ADDED
    
    | @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            require 'aws/s3'
         | 
| 2 | 
            +
            require 'stringio'
         | 
| 3 | 
            +
            require 'zlib'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            class Weld::CDN::S3 < Weld::CDN
         | 
| 6 | 
            +
              def initialize(options = {})
         | 
| 7 | 
            +
                super(options)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                raise Weld::ConfigError, "S3 bucket not specified" unless @options['bucket']
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                @options['access_key_id']     ||= ENV['AMAZON_ACCESS_KEY_ID']
         | 
| 12 | 
            +
                @options['secret_access_key'] ||= ENV['AMAZON_SECRET_ACCESS_KEY']
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                AWS::S3::Base.establish_connection!(
         | 
| 15 | 
            +
                  :access_key_id     => @options['access_key_id'],
         | 
| 16 | 
            +
                  :secret_access_key => @options['secret_access_key']
         | 
| 17 | 
            +
                )
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                @bucket = if AWS::S3::Bucket.list.include?(@options['bucket'])
         | 
| 20 | 
            +
                  AWS::S3::Bucket.find(@options['bucket'])
         | 
| 21 | 
            +
                else
         | 
| 22 | 
            +
                  AWS::S3::Bucket.create(@options['bucket'], :access => :public_read)
         | 
| 23 | 
            +
                  AWS::S3::Bucket.find(@options['bucket'])
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def push(filename, type, content)
         | 
| 28 | 
            +
                content_type = type == :css ? 'text/css' : 'application/javascript'
         | 
| 29 | 
            +
                prefix       = (@options['prefix'] || {})[type.to_s] || ''
         | 
| 30 | 
            +
                url_base     = @options['url_base'] || "http://#{@options['bucket']}.s3.amazonaws.com/"
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                object_options = {
         | 
| 33 | 
            +
                  :access            => :public_read,
         | 
| 34 | 
            +
                  'Cache-Control'    => 'public,max-age=315360000',
         | 
| 35 | 
            +
                  'Content-Type'     => "#{content_type};charset=utf-8",
         | 
| 36 | 
            +
                  'Expires'          => (Time.now + 315360000).httpdate # 10 years from now
         | 
| 37 | 
            +
                }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                if @options['gzip']
         | 
| 40 | 
            +
                  content_gzip = StringIO.new
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  gzip = Zlib::GzipWriter.new(content_gzip, 9)
         | 
| 43 | 
            +
                  gzip.write(content)
         | 
| 44 | 
            +
                  gzip.close
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  object_options['Content-Encoding'] = 'gzip'
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                object       = @bucket.new_object
         | 
| 50 | 
            +
                object.key   = "#{prefix}#{filename}"
         | 
| 51 | 
            +
                object.value = @options['gzip'] ? content_gzip.string : content
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                object.store(object_options)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                File.join(url_base, object.key)
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
    
        data/lib/weld/cli.rb
    ADDED
    
    | @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            require 'trollop'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Weld::CLI
         | 
| 4 | 
            +
              attr_reader :components, :options, :types, :weld
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              def initialize
         | 
| 7 | 
            +
                parse_args
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                @weld         = Weld.new(@options[:config])
         | 
| 10 | 
            +
                @components ||= @weld.config['components']
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              def cdn_push(filename, type, content)
         | 
| 14 | 
            +
                init_cdn
         | 
| 15 | 
            +
                @cdn.push(filename, type, content)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def error(message)
         | 
| 19 | 
            +
                STDERR.puts "Error: #{message}"
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def info(message)
         | 
| 23 | 
            +
                puts message
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def warn(message)
         | 
| 27 | 
            +
                STDERR.puts "Warning: #{message}"
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def init_cdn
         | 
| 33 | 
            +
                return if @cdn
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                cdn_config = @weld.config['cdn']
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                unless cdn_config && cdn_type = cdn_config['type']
         | 
| 38 | 
            +
                  raise Weld::ConfigError, "No CDN configured"
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                @cdn = Weld::CDN.const_get(cdn_type.capitalize.to_sym).new(cdn_config['options'] || {})
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def parse_args
         | 
| 45 | 
            +
                @options = Trollop.options do
         | 
| 46 | 
            +
                  version "#{Weld::APP_NAME} #{Weld::APP_VERSION}\n" << Weld::APP_COPYRIGHT
         | 
| 47 | 
            +
                  banner <<-EOS
         | 
| 48 | 
            +
            #{Weld::APP_NAME} combines and minifies CSS and JavaScript files at runtime and build time.
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            Usage:
         | 
| 51 | 
            +
              weld [options] <component> [<component> ...]
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            Options:
         | 
| 54 | 
            +
            EOS
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  opt :config,     "Use the specified config file.", :short => '-c', :default => './weld.yaml'
         | 
| 57 | 
            +
                  opt :no_minify,  "Don't perform any minification.", :short => :none
         | 
| 58 | 
            +
                  opt :output_dir, "Write welded files to the specified directory.", :short => '-o', :default => './'
         | 
| 59 | 
            +
                  opt :push,       "Push welded files to the configured CDN instead of saving them locally.", :short => '-p'
         | 
| 60 | 
            +
                  opt :type,       "Only weld components of the specified type ('css' or 'js').", :short => '-t', :type => :string
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                if @options[:type_given] && !['css', 'js'].include?(@options[:type])
         | 
| 64 | 
            +
                  abort "Error: Unsupported component type: #{@options[:type]}\n" <<
         | 
| 65 | 
            +
                      "Try --help for help."
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                @components = ARGV.length > 0 ? ARGV.dup : nil
         | 
| 69 | 
            +
                @types      = @options[:type_given] ? [@options[:type].to_sym] : [:css, :js]
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                @options
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
| @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            require 'open-uri'
         | 
| 2 | 
            +
            require 'pathname'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class Weld::Component
         | 
| 5 | 
            +
              attr_reader :name, :requires
         | 
| 6 | 
            +
              attr_accessor :css, :js
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def initialize(weld, name, config = {})
         | 
| 9 | 
            +
                raise ArgumentError, "weld must be a Weld instance" unless weld.is_a?(Weld)
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                @weld     = weld
         | 
| 12 | 
            +
                @name     = name
         | 
| 13 | 
            +
                @css      = config[:css] || []
         | 
| 14 | 
            +
                @js       = config[:js] || []
         | 
| 15 | 
            +
                @requires = config[:requires] || []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                @filename_cache = {}
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def compress(type)
         | 
| 21 | 
            +
                compressors = @weld.config['compressors']
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                unless compressors && compressors[type.to_s] &&
         | 
| 24 | 
            +
                    compressor_name = compressors[type.to_s]['name'].capitalize.to_sym
         | 
| 25 | 
            +
                  raise Weld::CompressorError, "No compressor configured"
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                options    = compressors[type.to_s]['options'] || {}
         | 
| 29 | 
            +
                compressor = Weld::Compressor.const_get(compressor_name).new(type, options)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                compressor.compress(merge(type))
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              def merge(type)
         | 
| 35 | 
            +
                raise Weld::UnsupportedFileTypeError, "Unsupported file type: #{type}" unless [:css, :js].include?(type.to_sym)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                content = ''
         | 
| 38 | 
            +
                method(type.to_sym).call.each {|f| content << read_file(f) + "\n" }
         | 
| 39 | 
            +
                content
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              private
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def read_file(filename)
         | 
| 45 | 
            +
                filename = resolve_filename(filename)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                if filename.is_a?(URI)
         | 
| 48 | 
            +
                  if cached = @weld.cache[filename]
         | 
| 49 | 
            +
                    return cached
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  open(filename, 'User-Agent' => "#{Weld::APP_NAME}/#{Weld::APP_VERSION}") do |response|
         | 
| 53 | 
            +
                    unless response.status[0] == '200'
         | 
| 54 | 
            +
                      raise Weld::FileNotFoundError, "URL returned HTTP status code #{response.status[0]}: #{filename}"
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    expires = response.meta['expires'] ? Time.parse(response.meta['expires']) : nil
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    if expires
         | 
| 60 | 
            +
                      @weld.cache.store(filename, response.read, expires)
         | 
| 61 | 
            +
                    else
         | 
| 62 | 
            +
                      response.read
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                else
         | 
| 66 | 
            +
                  File.read(filename)
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
              def resolve_filename(filename)
         | 
| 71 | 
            +
                return @filename_cache[filename] if @filename_cache.has_key?(filename)
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                if filename =~ /^(?:https?|ftp):\/\//
         | 
| 74 | 
            +
                  @filename_cache[filename] = URI.parse(filename)
         | 
| 75 | 
            +
                else
         | 
| 76 | 
            +
                  (@weld.config['sourcePaths'] || []).each do |source_path|
         | 
| 77 | 
            +
                    full_path = if Pathname.new(source_path).relative?
         | 
| 78 | 
            +
                      File.join(File.dirname(@weld.config_file), source_path, filename)
         | 
| 79 | 
            +
                    else
         | 
| 80 | 
            +
                      File.join(source_path, filename)
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    return @filename_cache[filename] = File.expand_path(full_path) if File.exist?(full_path)
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  raise Weld::FileNotFoundError, "File not found: #{filename}"
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         | 
    
        data/lib/weld/compressor.rb
    CHANGED
    
    
    
        data/lib/weld/server.rb
    CHANGED
    
    | @@ -2,12 +2,8 @@ require 'sinatra/base' | |
| 2 2 |  | 
| 3 3 | 
             
            class Weld::Server < Sinatra::Base
         | 
| 4 4 |  | 
| 5 | 
            -
              get %r{^/([ | 
| 6 | 
            -
                 | 
| 7 | 
            -
                  @weld ||= Weld.new(settings.config_file)
         | 
| 8 | 
            -
                rescue => ex
         | 
| 9 | 
            -
                  halt 500, ex.to_s
         | 
| 10 | 
            -
                end
         | 
| 5 | 
            +
              get %r{^/([^/]+)\.(css|js)$} do |name, type|
         | 
| 6 | 
            +
                @weld ||= Weld.new(settings.config_file)
         | 
| 11 7 |  | 
| 12 8 | 
             
                type      = type.to_sym
         | 
| 13 9 | 
             
                no_minify = params.has_key?('no-minify') || params.has_key?('nominify')
         | 
| @@ -15,22 +11,10 @@ class Weld::Server < Sinatra::Base | |
| 15 11 | 
             
                content_type(type == :css ? 'text/css' : 'application/javascript',
         | 
| 16 12 | 
             
                    :charset => 'utf-8')
         | 
| 17 13 |  | 
| 18 | 
            -
                 | 
| 19 | 
            -
                   | 
| 20 | 
            -
             | 
| 21 | 
            -
                   | 
| 22 | 
            -
                    @weld.component(name).compress(type)
         | 
| 23 | 
            -
                  end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                rescue Weld::ComponentNotFoundError,
         | 
| 26 | 
            -
                       Weld::FileNotFoundError => ex
         | 
| 27 | 
            -
                  halt 404, ex.to_s
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                rescue Weld::UnsupportedFileTypeError => ex
         | 
| 30 | 
            -
                  halt 400, ex.to_s
         | 
| 31 | 
            -
             | 
| 32 | 
            -
                rescue => ex
         | 
| 33 | 
            -
                  halt 500, ex.to_s
         | 
| 14 | 
            +
                if no_minify
         | 
| 15 | 
            +
                  @weld.component(name).merge(type)
         | 
| 16 | 
            +
                else
         | 
| 17 | 
            +
                  @weld.component(name).compress(type)
         | 
| 34 18 | 
             
                end
         | 
| 35 19 | 
             
              end
         | 
| 36 20 |  | 
    
        data/lib/weld/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: weld
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              version: 0.0.1.dev. | 
| 4 | 
            +
              version: 0.0.1.dev.20100404
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors: 
         | 
| 7 7 | 
             
            - Ryan Grove
         | 
| @@ -9,9 +9,19 @@ autorequire: | |
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 11 |  | 
| 12 | 
            -
            date: 2010-04- | 
| 12 | 
            +
            date: 2010-04-04 00:00:00 -07:00
         | 
| 13 13 | 
             
            default_executable: 
         | 
| 14 14 | 
             
            dependencies: 
         | 
| 15 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 16 | 
            +
              name: aws-s3
         | 
| 17 | 
            +
              type: :runtime
         | 
| 18 | 
            +
              version_requirement: 
         | 
| 19 | 
            +
              version_requirements: !ruby/object:Gem::Requirement 
         | 
| 20 | 
            +
                requirements: 
         | 
| 21 | 
            +
                - - ~>
         | 
| 22 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 23 | 
            +
                    version: "0.6"
         | 
| 24 | 
            +
                version: 
         | 
| 15 25 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| 16 26 | 
             
              name: sinatra
         | 
| 17 27 | 
             
              type: :runtime
         | 
| @@ -45,6 +55,11 @@ files: | |
| 45 55 | 
             
            - README.rdoc
         | 
| 46 56 | 
             
            - bin/weld
         | 
| 47 57 | 
             
            - examples/simple-config.yaml
         | 
| 58 | 
            +
            - lib/weld/cache.rb
         | 
| 59 | 
            +
            - lib/weld/cdn/s3.rb
         | 
| 60 | 
            +
            - lib/weld/cdn.rb
         | 
| 61 | 
            +
            - lib/weld/cli.rb
         | 
| 62 | 
            +
            - lib/weld/component.rb
         | 
| 48 63 | 
             
            - lib/weld/compressor/yui.rb
         | 
| 49 64 | 
             
            - lib/weld/compressor.rb
         | 
| 50 65 | 
             
            - lib/weld/server.rb
         |