sitefuel 0.0.0a
Sign up to get free protection for your applications and to get access to all the features.
- data/README +86 -0
- data/RELEASE_NOTES +7 -0
- data/bin/sitefuel +162 -0
- data/lib/sitefuel/Configuration.rb +35 -0
- data/lib/sitefuel/SiteFuelLogger.rb +128 -0
- data/lib/sitefuel/SiteFuelRuntime.rb +293 -0
- data/lib/sitefuel/extensions/ArrayComparisons.rb +34 -0
- data/lib/sitefuel/extensions/DynamicClassMethods.rb +19 -0
- data/lib/sitefuel/extensions/FileComparison.rb +24 -0
- data/lib/sitefuel/extensions/Silently.rb +27 -0
- data/lib/sitefuel/extensions/StringFormatting.rb +75 -0
- data/lib/sitefuel/extensions/SymbolComparison.rb +13 -0
- data/lib/sitefuel/external/AbstractExternalProgram.rb +616 -0
- data/lib/sitefuel/external/ExternalProgramTestCase.rb +67 -0
- data/lib/sitefuel/external/GIT.rb +9 -0
- data/lib/sitefuel/external/JPEGTran.rb +68 -0
- data/lib/sitefuel/external/PNGCrush.rb +66 -0
- data/lib/sitefuel/processors/AbstractExternalProgramProcessor.rb +72 -0
- data/lib/sitefuel/processors/AbstractProcessor.rb +378 -0
- data/lib/sitefuel/processors/AbstractStringBasedProcessor.rb +88 -0
- data/lib/sitefuel/processors/CSSProcessor.rb +84 -0
- data/lib/sitefuel/processors/HAMLProcessor.rb +52 -0
- data/lib/sitefuel/processors/HTMLProcessor.rb +211 -0
- data/lib/sitefuel/processors/JavaScriptProcessor.rb +57 -0
- data/lib/sitefuel/processors/PHPProcessor.rb +32 -0
- data/lib/sitefuel/processors/PNGProcessor.rb +80 -0
- data/lib/sitefuel/processors/RHTMLProcessor.rb +25 -0
- data/lib/sitefuel/processors/SASSProcessor.rb +50 -0
- data/test/processor_listing.rb +28 -0
- data/test/test_AbstractExternalProgram.rb +186 -0
- data/test/test_AbstractProcessor.rb +237 -0
- data/test/test_AbstractStringBasedProcessor.rb +48 -0
- data/test/test_AllProcessors.rb +65 -0
- data/test/test_ArrayComparisons.rb +32 -0
- data/test/test_CSSProcessor.rb +120 -0
- data/test/test_FileComparisons.rb +42 -0
- data/test/test_HAMLProcessor.rb.rb +60 -0
- data/test/test_HTMLProcessor.rb +186 -0
- data/test/test_JPEGTran.rb +40 -0
- data/test/test_JavaScriptProcessor.rb +63 -0
- data/test/test_PHPProcessor.rb +51 -0
- data/test/test_PNGCrush.rb +58 -0
- data/test/test_PNGProcessor.rb +32 -0
- data/test/test_RHTMLProcessor.rb +62 -0
- data/test/test_SASSProcessor.rb +68 -0
- data/test/test_SiteFuelLogging.rb +79 -0
- data/test/test_SiteFuelRuntime.rb +96 -0
- data/test/test_StringFormatting.rb +51 -0
- data/test/test_SymbolComparison.rb +27 -0
- data/test/test_images/sample_jpg01.jpg +0 -0
- data/test/test_images/sample_png01.png +0 -0
- data/test/test_programs/versioning.rb +26 -0
- data/test/test_sites/simplehtml/deployment.yml +22 -0
- data/test/test_sites/simplehtml/index.html +66 -0
- data/test/test_sites/simplehtml/style.css +40 -0
- data/test/ts_all.rb +39 -0
- metadata +165 -0
data/README
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
|
2
|
+
|
3
|
+
|
4
|
+
SiteFuel
|
5
|
+
http://sitefuel.org
|
6
|
+
v0.1.0 (initial release)
|
7
|
+
|
8
|
+
|
9
|
+
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
|
10
|
+
|
11
|
+
A program and API for minifying websites and web applications and
|
12
|
+
deploying them from version control.
|
13
|
+
|
14
|
+
* Get started: http://sitefuel.org/getstarted
|
15
|
+
* Lists: http://sitefuel.org/mailinglists
|
16
|
+
* Report a bug: http://sitefuel.org/bug
|
17
|
+
|
18
|
+
* Contribute! http://sitefuel.org/contribute
|
19
|
+
|
20
|
+
|
21
|
+
== Table of Contents
|
22
|
+
|
23
|
+
1. Introduction
|
24
|
+
. System Requirements
|
25
|
+
. Supported Formats
|
26
|
+
. Installation
|
27
|
+
. Getting Started
|
28
|
+
. Reporting a Bug
|
29
|
+
. A Note about the API
|
30
|
+
. Further Reading
|
31
|
+
|
32
|
+
|
33
|
+
== Introduction
|
34
|
+
SiteFuel is a Ruby program
|
35
|
+
|
36
|
+
Use SiteFuel to:
|
37
|
+
* deploy your website out of SVN or GIT
|
38
|
+
* minify your
|
39
|
+
|
40
|
+
|
41
|
+
== System Requirements
|
42
|
+
|
43
|
+
* Ruby 1.8.6 or greater.
|
44
|
+
* pngcrush (for PNG compression)
|
45
|
+
* jpegtran (for JPEG compression)
|
46
|
+
* git (for deploying sites from Git repositories)
|
47
|
+
* svn (for deploying sites from SVN repositories)
|
48
|
+
|
49
|
+
SiteFuel hasn't been tested on Windows machines.
|
50
|
+
|
51
|
+
|
52
|
+
== Installation
|
53
|
+
|
54
|
+
SiteFuel is deployed as a RubyGem through http://gemcutter.org. You
|
55
|
+
should be able to install it directly using RubyGems:
|
56
|
+
|
57
|
+
$ gem install -t sitefuel
|
58
|
+
|
59
|
+
You can also download a ZIP-file containing SiteFuel and all of the gems
|
60
|
+
it depends on from http://sitefuel.org/download/sitefuel-complete.zip
|
61
|
+
|
62
|
+
Note that for PNG and JPEG compression to be enabled you must install
|
63
|
+
the 'pngcrush' and 'jpegtran' programs.
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
== Getting Started
|
68
|
+
|
69
|
+
SiteFuel
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
== Reporting a Bug
|
75
|
+
|
76
|
+
|
77
|
+
== A Note About the API
|
78
|
+
|
79
|
+
The general API in SiteFuel should be considered unstable while SiteFuel
|
80
|
+
is <1.0. For practical reasons we can't increment the major version
|
81
|
+
number each time there is a non-backwards compatible API change....
|
82
|
+
|
83
|
+
|
84
|
+
== Further Reading
|
85
|
+
* http://sitefuel.org - main sitefuel website
|
86
|
+
* http://sitefuel.org/help - pointers to various resources
|
data/RELEASE_NOTES
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
== 0.1.0
|
2
|
+
* initial public release of the SiteFuel alpha
|
3
|
+
* support for trivial minification of HTML, RHTML, and PHP.
|
4
|
+
* uses CSSMin and JSMin to minify CSS and JavaScript; both in individual
|
5
|
+
files and embedded in HTML.
|
6
|
+
* uses pngcrush and jpegtran to minify PNG and JPEG images.
|
7
|
+
* support for the Git and SVN version control systems.
|
data/bin/sitefuel
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
#!/usr/bin/ruby -wrubygems
|
2
|
+
|
3
|
+
# == Synopsis
|
4
|
+
#
|
5
|
+
# A lightweight ruby framework for processing and deploying websites, either
|
6
|
+
# from
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
#
|
10
|
+
# sitefuel deploy|process SOURCE [OPTIONS]
|
11
|
+
#
|
12
|
+
# == Introduction
|
13
|
+
# sitefuel is a lightweight ruby framework for deploying websites directly from
|
14
|
+
# version control. sitefuel includes support for compressing HTML and CSS as
|
15
|
+
# well as optimizing PNG graphics. Support is planned for SASS; compressing
|
16
|
+
# JavaScript; automatically creating sprites; and supporting more image formats.
|
17
|
+
# (and more!)
|
18
|
+
#
|
19
|
+
# TODO: add more details
|
20
|
+
#
|
21
|
+
# * More information: http://sitefuel.org
|
22
|
+
# * Getting started: http://sitefuel.org/getstarted
|
23
|
+
# * Documentation: http://sitefuel.org/documentation
|
24
|
+
#
|
25
|
+
# == Commands
|
26
|
+
#
|
27
|
+
# deploy:: Deploy a site using sitefuel.
|
28
|
+
# process:: Modify an existing website inplace.
|
29
|
+
# stage:: Simulates a deployment without actually changing anything.
|
30
|
+
# help:: Show this message.
|
31
|
+
#
|
32
|
+
# == Examples
|
33
|
+
# Process an already deployed site:
|
34
|
+
# sitefuel process /var/www/
|
35
|
+
#
|
36
|
+
# Deploy a site from SVN:
|
37
|
+
# sitefuel deploy svn+ssh://sitefuel.org/svn/tags/21 /var/www/
|
38
|
+
#
|
39
|
+
# Specify a non-default deployment file:
|
40
|
+
# sitefuel process /var/www/ -c customdeployment.yml
|
41
|
+
#
|
42
|
+
#
|
43
|
+
# == Author
|
44
|
+
# Zanoccio, LLC.
|
45
|
+
#
|
46
|
+
# == Copyright
|
47
|
+
# Copyright (c) 2009 Zanoccio.
|
48
|
+
#
|
49
|
+
# == License
|
50
|
+
# This program is free software; you can redistribute it and/or
|
51
|
+
# modify it under the terms of the GNU General Public License
|
52
|
+
# as published by the Free Software Foundation; either version 2
|
53
|
+
# of the License, or (at your option) any later version.
|
54
|
+
#
|
55
|
+
# This program is distributed in the hope that it will be useful,
|
56
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
57
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
58
|
+
# GNU General Public License for more details.
|
59
|
+
#
|
60
|
+
# You should have received a copy of the GNU General Public License
|
61
|
+
# along with this program; if not, write to the Free Software
|
62
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
63
|
+
#
|
64
|
+
|
65
|
+
# add source/ to the load path
|
66
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
67
|
+
|
68
|
+
begin
|
69
|
+
require 'rubygems'
|
70
|
+
rescue LoadError
|
71
|
+
# attempt to continue running the program
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
require 'rdoc/usage'
|
76
|
+
require 'term/ansicolor'
|
77
|
+
|
78
|
+
require 'sitefuel/SiteFuelRuntime'
|
79
|
+
|
80
|
+
include Term::ANSIColor
|
81
|
+
|
82
|
+
def puts_and_exit(*args)
|
83
|
+
puts(*args)
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
$HELP_HINT_LINE = "type \'#{$0} help\' for usage"
|
88
|
+
|
89
|
+
# parse command line arguments and configure a SiteFuelRuntime
|
90
|
+
def parse_command_line(runtime)
|
91
|
+
|
92
|
+
#### BUILD THE OPTIONS PARSER
|
93
|
+
opts = OptionParser.new
|
94
|
+
|
95
|
+
# the banner text comes from the header comment for this file
|
96
|
+
|
97
|
+
opts.on('-oARG', '-o=ARG', '-o PLACE', '--output=ARG', '--output PLACE', String,
|
98
|
+
'Where to put a deployed site') do |out|
|
99
|
+
runtime.deploy_to = out
|
100
|
+
end
|
101
|
+
opts.on('-v', '--version', 'Gives the version of sitefuel') do
|
102
|
+
puts 'SiteFuel ' + VERSION_TEXT
|
103
|
+
end
|
104
|
+
|
105
|
+
opts.on('--[no-]verbose', 'List actions as they are preformed') do
|
106
|
+
puts 'cause SiteFuel to be verbose listing actions as they are preformed'
|
107
|
+
end
|
108
|
+
|
109
|
+
opts.on('--only-list-recognized-files', 'Only list summaries for files which were changed') do
|
110
|
+
runtime.only_list_recognized_files = true
|
111
|
+
end
|
112
|
+
|
113
|
+
opts.on('-h', '--help', '-?', '--about', 'Gives help for a specific command.') do
|
114
|
+
RDoc::usage_no_exit('Synopsis', 'Usage', 'Introduction')
|
115
|
+
puts opts
|
116
|
+
RDoc::usage_no_exit('Examples', 'Author', 'Copyright', 'License')
|
117
|
+
exit
|
118
|
+
end
|
119
|
+
|
120
|
+
opts.separator ''
|
121
|
+
opts.separator 'Specific options for a command can be seen with: '
|
122
|
+
opts.separator ' COMMAND --help'
|
123
|
+
|
124
|
+
|
125
|
+
#### ATTEMPT TO PARSE THE COMMAND LINE
|
126
|
+
begin
|
127
|
+
commands = opts.parse(*ARGV)
|
128
|
+
rescue OptionParser::InvalidOption => iopt
|
129
|
+
puts iopt
|
130
|
+
puts_and_exit $HELP_HINT_LINE
|
131
|
+
rescue => exception
|
132
|
+
# TODO: add better handling for the various exceptions (unnecessary
|
133
|
+
# argument, missing argument, etc.)
|
134
|
+
puts_and_exit "couldn't parse command line: #{exception}", $HELP_HINT_LINE
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# note that --help will have already been intercepted but 'help' still needs
|
139
|
+
# special treatment
|
140
|
+
puts_and_exit 'no command given', $HELP_HINT_LINE if commands.length < 1
|
141
|
+
puts_and_exit opts if commands[0].downcase == 'help'
|
142
|
+
|
143
|
+
case commands[0].downcase
|
144
|
+
when 'deploy'
|
145
|
+
runtime.deploy_from = commands[1]
|
146
|
+
runtime.deploy
|
147
|
+
|
148
|
+
when 'stage'
|
149
|
+
runtime.deploy_from = commands[1]
|
150
|
+
runtime.stage
|
151
|
+
|
152
|
+
else
|
153
|
+
puts_and_exit "unknown command: '#{commands[0]}'", $HELP_HINT_LINE
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def main
|
158
|
+
runtime = SiteFuel::SiteFuelRuntime.new
|
159
|
+
parse_command_line(runtime)
|
160
|
+
end
|
161
|
+
|
162
|
+
main()
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# File:: configuration.rb
|
3
|
+
# Author:: wkm
|
4
|
+
# Copyright:: 2009
|
5
|
+
# License:: GPL
|
6
|
+
#
|
7
|
+
# Load and process SiteFuel YAML configuration files
|
8
|
+
#
|
9
|
+
|
10
|
+
module SiteFuel
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
class Configuration
|
14
|
+
|
15
|
+
# exception which represents that a configuration could not be found
|
16
|
+
class NotFound < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
# given a directory path will attempt to location a configuration file
|
20
|
+
# and load it, returning a SiteFuel::Configuration class
|
21
|
+
def self.load(path)
|
22
|
+
unless File.exist?(path)
|
23
|
+
throw NotFound, path
|
24
|
+
end
|
25
|
+
|
26
|
+
yamlconfig = YAML::load_file(configfile)
|
27
|
+
|
28
|
+
Configuration.new(yamlconfig)
|
29
|
+
end
|
30
|
+
|
31
|
+
# builds a sitefuel configuration from a parsed YAML file
|
32
|
+
def initialize(yamlconfig)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# File:: SiteFuelLogger.rb
|
3
|
+
# Author:: wkm
|
4
|
+
# Copyright:: 2009
|
5
|
+
# License:: GPL
|
6
|
+
#
|
7
|
+
|
8
|
+
module SiteFuel
|
9
|
+
|
10
|
+
require 'logger'
|
11
|
+
require 'singleton'
|
12
|
+
require 'term/ansicolor'
|
13
|
+
|
14
|
+
include Term::ANSIColor
|
15
|
+
|
16
|
+
# Singleton abstraction around the Logger:: library, typically every processor
|
17
|
+
# will hook into using this logger while letting us control it's placement
|
18
|
+
# and display globally
|
19
|
+
class SiteFuelLogger < Logger
|
20
|
+
include Singleton
|
21
|
+
|
22
|
+
# the number of fatal messages logged so far (even if lower than #level)
|
23
|
+
attr_reader :fatal_count
|
24
|
+
|
25
|
+
# the number of error messages logged so far (even if lower than #level)
|
26
|
+
attr_reader :error_count
|
27
|
+
|
28
|
+
# the number of warning messages logged so far (even if lower than #level)
|
29
|
+
attr_reader :warn_count
|
30
|
+
|
31
|
+
# the number of info messages logged so far (even if lower than #level)
|
32
|
+
attr_reader :info_count
|
33
|
+
|
34
|
+
# the number of debug messages logged so far (even if lower than #level)
|
35
|
+
attr_reader :debug_count
|
36
|
+
|
37
|
+
# adjust the style of logging:
|
38
|
+
#
|
39
|
+
# default:: use the Logger logging style
|
40
|
+
# clean:: gives a clean logging output intended for human use
|
41
|
+
attr_accessor :log_style
|
42
|
+
|
43
|
+
def initialize(filename = STDOUT)
|
44
|
+
super(filename)
|
45
|
+
|
46
|
+
@fatal_count = 0
|
47
|
+
@error_count = 0
|
48
|
+
@warn_count = 0
|
49
|
+
@info_count = 0
|
50
|
+
@debug_count = 0
|
51
|
+
|
52
|
+
self.level = WARN
|
53
|
+
@log_style = :default
|
54
|
+
@progname = 'SiteFuel'
|
55
|
+
end
|
56
|
+
|
57
|
+
def fatal(*args)
|
58
|
+
@fatal_count += 1
|
59
|
+
super(*args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def error(*args)
|
63
|
+
@error_count += 1
|
64
|
+
super(*args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def warn(*args)
|
68
|
+
@warn_count += 1
|
69
|
+
super(*args)
|
70
|
+
end
|
71
|
+
|
72
|
+
def info(*args)
|
73
|
+
@info_count += 1
|
74
|
+
super(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
def debug(*args)
|
78
|
+
@debug_count += 1
|
79
|
+
super(*args)
|
80
|
+
end
|
81
|
+
|
82
|
+
# implement our own #add so we can have cleaner logs
|
83
|
+
def format_message(severity, date_time, program_name, msg)
|
84
|
+
case @log_style
|
85
|
+
when :default
|
86
|
+
super(severity, date_time, program_name, msg)
|
87
|
+
|
88
|
+
when :clean
|
89
|
+
string = "#{severity}: #{msg}\n"
|
90
|
+
case severity
|
91
|
+
when 'ERROR', 'FATAL'
|
92
|
+
string = bold(red(string))
|
93
|
+
|
94
|
+
when 'WARN'
|
95
|
+
string = yellow(string)
|
96
|
+
|
97
|
+
when 'DEBUG'
|
98
|
+
string = string
|
99
|
+
end
|
100
|
+
string
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# mixin for adding logging functionality to any class, typically included
|
106
|
+
# by every SiteFuel class
|
107
|
+
module Logging
|
108
|
+
# sets the logger for a class
|
109
|
+
def logger=(logger)
|
110
|
+
@logger = logger
|
111
|
+
end
|
112
|
+
|
113
|
+
# adds a fatal error to the log
|
114
|
+
def fatal(*args) @logger.fatal(*args); end
|
115
|
+
|
116
|
+
# adds an error to the log
|
117
|
+
def error(*args) @logger.error(*args); end
|
118
|
+
|
119
|
+
# adds a warning to the log
|
120
|
+
def warn(*args) @logger.warn(*args); end
|
121
|
+
|
122
|
+
# adds an info message to the log
|
123
|
+
def info(*args) @logger.info(*args); end
|
124
|
+
|
125
|
+
# adds a debugging message to the log
|
126
|
+
def debug(*args) @logger.debug(*args); end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,293 @@
|
|
1
|
+
#
|
2
|
+
# File:: SiteFuelRuntime.rb
|
3
|
+
# Author:: wkm
|
4
|
+
# Copyright:: 2009
|
5
|
+
# License:: GPL
|
6
|
+
#
|
7
|
+
# Defines the primary interface class used by sitefuel to do actual work.
|
8
|
+
# Keeping this as a class let's us abstract away the command line interface
|
9
|
+
# while automatically having a direct API for programatically accessing
|
10
|
+
# sitefuel.
|
11
|
+
#
|
12
|
+
|
13
|
+
|
14
|
+
module SiteFuel
|
15
|
+
|
16
|
+
require 'optparse'
|
17
|
+
require 'term/ansicolor'
|
18
|
+
|
19
|
+
include Term::ANSIColor
|
20
|
+
|
21
|
+
require 'sitefuel/SiteFuelLogger'
|
22
|
+
|
23
|
+
require 'sitefuel/extensions/StringFormatting'
|
24
|
+
require 'sitefuel/extensions/FileComparison'
|
25
|
+
|
26
|
+
# we need the AbstractProcessor symbol when we go child-class hunting
|
27
|
+
require 'sitefuel/processors/AbstractProcessor'
|
28
|
+
|
29
|
+
# version of SiteFuel
|
30
|
+
VERSION = [0, 0, 1].freeze
|
31
|
+
|
32
|
+
# a human readable version
|
33
|
+
VERSION_TEXT = VERSION.join('.').freeze
|
34
|
+
|
35
|
+
|
36
|
+
class SiteFuelRuntime
|
37
|
+
|
38
|
+
include SiteFuel::Logging
|
39
|
+
|
40
|
+
# what action is the runtime supposed to preform
|
41
|
+
attr_accessor :action
|
42
|
+
|
43
|
+
# what is the source *from* which we are deploying
|
44
|
+
attr_accessor :deploy_from
|
45
|
+
|
46
|
+
# what is the source *to* which we are deploying
|
47
|
+
attr_accessor :deploy_to
|
48
|
+
|
49
|
+
# configuration loaded from a deployment.yml file
|
50
|
+
attr_accessor :deploymentconfiguration
|
51
|
+
|
52
|
+
# only lists file which have a known processor
|
53
|
+
attr_accessor :only_list_recognized_files
|
54
|
+
|
55
|
+
def initialize
|
56
|
+
@processors = SiteFuelRuntime.find_processors
|
57
|
+
self.logger = SiteFuelLogger.instance
|
58
|
+
SiteFuelLogger.instance.log_style = :clean
|
59
|
+
|
60
|
+
@only_list_recognized_files = false
|
61
|
+
end
|
62
|
+
|
63
|
+
# gives true if the given file (typically a processor) has already been
|
64
|
+
# loaded (by looking into $"). Unfortunately #require is easily tricked,
|
65
|
+
# so this function uses some heuristics to prevent processors from being
|
66
|
+
# loaded twice (by basically comparing the "core" part of the filename)
|
67
|
+
def self.processor_loaded?(file)
|
68
|
+
$".map do |f|
|
69
|
+
if File.equivalent?(file, f)
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
|
77
|
+
# finds all processors under processors/ and loads them. Any file matching
|
78
|
+
# *Processor.rb will be loaded
|
79
|
+
def self.load_processors
|
80
|
+
dir = File.dirname(__FILE__).split(File::SEPARATOR)
|
81
|
+
dir = File.join(*dir[0..-2]) + File::SEPARATOR
|
82
|
+
|
83
|
+
# build up the search pattern by taking this file's directory and shoving
|
84
|
+
# it onto the search pattern
|
85
|
+
patt = File.join(dir, 'sitefuel/processors/*Processor.rb')
|
86
|
+
|
87
|
+
# find all file matching that pattern
|
88
|
+
files = Dir[patt]
|
89
|
+
|
90
|
+
# rip off the path prefix eg. 'sitefuel/lib/b/foo.rb' becomes 'b/foo.rb'
|
91
|
+
files = files.map do |filename|
|
92
|
+
filename.gsub(Regexp.new("^"+Regexp.escape(dir)), '')
|
93
|
+
end
|
94
|
+
|
95
|
+
# get rid of anything we've already loaded
|
96
|
+
files = files.delete_if { |file| processor_loaded?(file) }
|
97
|
+
|
98
|
+
# load whatever files we're left with
|
99
|
+
files.each { |f| require f }
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
# returns a list of processors found by looking for all children of
|
104
|
+
# SiteFuel::Processor::AbstractProcessor
|
105
|
+
#
|
106
|
+
# for a processor to be automatically included it has to:
|
107
|
+
# * be loaded (see #load_processors)
|
108
|
+
# * be a child class of AbstractProcessor
|
109
|
+
# * the class name must end with Processor
|
110
|
+
#
|
111
|
+
def self.find_processors
|
112
|
+
Processor::AbstractProcessor.find_processors
|
113
|
+
end
|
114
|
+
|
115
|
+
# lists the actions which are possible with this runtime.
|
116
|
+
def actions
|
117
|
+
[ :deploy, :stage ]
|
118
|
+
end
|
119
|
+
|
120
|
+
# gives the array of processors available to this runtime
|
121
|
+
attr_reader :processors
|
122
|
+
|
123
|
+
# adds a processor or an array of processors to the runtime
|
124
|
+
def add_processor(proc)
|
125
|
+
case proc
|
126
|
+
when Array
|
127
|
+
proc.each { |p|
|
128
|
+
@processors << p
|
129
|
+
}
|
130
|
+
else
|
131
|
+
@processors << proc
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# gives the processor to use for a given file
|
136
|
+
def choose_processor(filename)
|
137
|
+
matchingprocs = processors.clone.delete_if {|proc| not proc.processes_file?(filename) }
|
138
|
+
|
139
|
+
case
|
140
|
+
when matchingprocs.length > 1
|
141
|
+
chosen = matchingprocs.first
|
142
|
+
raise Processor::MultipleApplicableProcessors.new(filename, matchingprocs, chosen)
|
143
|
+
when matchingprocs.length == 1
|
144
|
+
return matchingprocs.first
|
145
|
+
else
|
146
|
+
return nil
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# like #choose_processor but prints a message if there are clashing
|
151
|
+
# processors and returns the first of the clashing processors.
|
152
|
+
# (effectively alerting the user, but continuing to work)
|
153
|
+
def choose_processor!(filename)
|
154
|
+
begin
|
155
|
+
choose_processor(filename)
|
156
|
+
rescue Processor::MultipleApplicableProcessors => exception
|
157
|
+
# log the exception
|
158
|
+
warn exception
|
159
|
+
exception.chosen_processor
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# implements the stage command. Staging, by itself, will give statistics on
|
165
|
+
# the deployment; how many bytes were saved by minification; etc.
|
166
|
+
#
|
167
|
+
# However, #stage when part of #deploy will go and create the requisite files
|
168
|
+
# in a temporary directory
|
169
|
+
def stage
|
170
|
+
return nil if @deploy_from == nil
|
171
|
+
|
172
|
+
puts '== %s '.format('Staging').ljust(80, '=')
|
173
|
+
|
174
|
+
# find all files under deploy_from
|
175
|
+
files = find_all_files @deploy_from
|
176
|
+
|
177
|
+
total_original_size = 0
|
178
|
+
total_processed_size = 0
|
179
|
+
@resource_processors = {}
|
180
|
+
@processor_statistics = Hash.new([0, 0, 0])
|
181
|
+
files.each do |filename|
|
182
|
+
processor = choose_processor!(filename)
|
183
|
+
if processor == nil
|
184
|
+
@resource_processors[filename] = nil
|
185
|
+
else
|
186
|
+
@resource_processors[filename] = processor.process_file(filename)
|
187
|
+
end
|
188
|
+
|
189
|
+
processor = @resource_processors[filename]
|
190
|
+
if processor == nil
|
191
|
+
if only_list_recognized_files == false
|
192
|
+
puts '%s %s' %['--'.ljust(8), filename.cabbrev(65)]
|
193
|
+
end
|
194
|
+
else
|
195
|
+
total_original_size += processor.original_size
|
196
|
+
total_processed_size += processor.processed_size
|
197
|
+
|
198
|
+
stats = @processor_statistics[processor.class.processor_name].clone
|
199
|
+
stats[0] += 1
|
200
|
+
stats[1] += processor.original_size
|
201
|
+
stats[2] += processor.processed_size
|
202
|
+
@processor_statistics[processor.class.processor_name] = stats
|
203
|
+
|
204
|
+
puts '%s %s %4.3f' %
|
205
|
+
[
|
206
|
+
cyan(processor.class.processor_name.ljust(8)),
|
207
|
+
filename.cabbrev(65).ljust(65),
|
208
|
+
processor.processed_size.prec_f/processor.original_size.prec_f
|
209
|
+
]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
puts '='*80
|
214
|
+
puts 'Size delta: %+5d bytes; %4.3f' %
|
215
|
+
[
|
216
|
+
total_processed_size - total_original_size,
|
217
|
+
total_processed_size.prec_f/total_original_size.prec_f
|
218
|
+
]
|
219
|
+
|
220
|
+
staging_statistics
|
221
|
+
end
|
222
|
+
|
223
|
+
# outputs a little grid showing the number of files of each processor
|
224
|
+
# and the total savings
|
225
|
+
#
|
226
|
+
# todo: this should be computed in the staging step
|
227
|
+
def staging_statistics
|
228
|
+
puts ''
|
229
|
+
puts ' %s | %s | %s | %s' %
|
230
|
+
['processor', '# files', 'delta', 'ratio'].map{|v| bold(v)}
|
231
|
+
puts ' -----------+----------+-------------+-------'
|
232
|
+
|
233
|
+
@processor_statistics.keys.sort.each do |key|
|
234
|
+
puts ' %s|%9d |%+12d | %4.3f' %
|
235
|
+
[
|
236
|
+
key.ljust(10),
|
237
|
+
@processor_statistics[key][0],
|
238
|
+
@processor_statistics[key][2] - @processor_statistics[key][1],
|
239
|
+
@processor_statistics[key][2].prec_f / @processor_statistics[key][1].prec_f
|
240
|
+
]
|
241
|
+
end
|
242
|
+
puts ' -----------+----------+-------------+-------'
|
243
|
+
puts ''
|
244
|
+
end
|
245
|
+
|
246
|
+
# create a deployment
|
247
|
+
def deploy
|
248
|
+
# first we have to stage the files
|
249
|
+
stage
|
250
|
+
|
251
|
+
files = find_all_files @deploy_from
|
252
|
+
|
253
|
+
return if @deploy_to == nil
|
254
|
+
puts
|
255
|
+
puts bold('Deploying:')
|
256
|
+
|
257
|
+
unless File.exists?(@deploy_to)
|
258
|
+
Dir.mkdir(@deploy_to)
|
259
|
+
end
|
260
|
+
# write out content
|
261
|
+
files.each do |filename|
|
262
|
+
results = @resource_processors[filename]
|
263
|
+
if results == nil
|
264
|
+
putc 'I'
|
265
|
+
else
|
266
|
+
putc '.'
|
267
|
+
results.save(@deploy_to)
|
268
|
+
end
|
269
|
+
STDOUT.flush
|
270
|
+
end
|
271
|
+
puts
|
272
|
+
end
|
273
|
+
|
274
|
+
# gives an array listing of all files on a given path
|
275
|
+
#
|
276
|
+
# This is a very lightweight wrapper around Dir.
|
277
|
+
def find_all_files(path)
|
278
|
+
Dir[File.join(path, "**/*")]
|
279
|
+
end
|
280
|
+
|
281
|
+
# changes the verbosity of the runtime by adjusting the log level
|
282
|
+
def verbosity(level = 1)
|
283
|
+
case level
|
284
|
+
when 1
|
285
|
+
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
|
291
|
+
# load the various processors
|
292
|
+
SiteFuelRuntime.load_processors
|
293
|
+
end
|