class Middleman::Builder
def binary_encode(string)
def binary_encode(string) string.force_encoding('ascii-8bit') if string.respond_to?(:force_encoding) string end
def clean
def clean @to_clean.each do |f| FileUtils.rm(f) trigger(:deleted, f) end end
def export_file!(output_file, source)
def export_file!(output_file, source) source = write_tempfile(output_file, source.to_s) if source.is_a? String method, source_path = if source.is_a? Tempfile [FileUtils.method(:mv), source.path] else [FileUtils.method(:cp), source.to_s] end mode = which_mode(output_file, source_path) if mode == :created || mode == :updated FileUtils.mkdir_p(output_file.dirname) method.call(source_path, output_file.to_s) end source.unlink if source.is_a? Tempfile trigger(mode, output_file) end
def initialize(app, opts={})
-
opts
(Hash
) -- The builder options -
app
(Middleman::Application
) -- The app to build.
def initialize(app, opts={}) @app = app @source_dir = Pathname(File.join(@app.root, @app.config[:source])) @build_dir = Pathname(@app.config[:build_dir]) if @build_dir.expand_path.relative_path_from(@source_dir).to_s =~ /\A[.\/]+\Z/ raise ":build_dir (#{@build_dir}) cannot be a parent of :source_dir (#{@source_dir})" end @glob = opts.fetch(:glob) @cleaning = opts.fetch(:clean) rack_app = ::Middleman::Rack.new(@app).to_app @rack = ::Rack::MockRequest.new(rack_app) @callbacks = ::Middleman::CallbackManager.new @callbacks.install_methods!(self, [:on_build_event]) end
def output_files
def output_files logger.debug '== Building files' # Sort paths to be built by the above order. This is primarily so Compass can # find files in the build folder when it needs to generate sprites for the # css files. # # Loop over all the paths and build them. @app.sitemap.resources .sort_by { |resource| SORT_ORDER.index(resource.ext) || 100 } .reject { |resource| resource.ext == '.css' } .select { |resource| !@glob || File.fnmatch(@glob, resource.destination_path) } .each(&method(:output_resource)) end
def output_resource(resource)
def output_resource(resource) output_file = @build_dir + resource.destination_path.gsub('%20', ' ') begin if resource.binary? export_file!(output_file, resource.file_descriptor[:full_path]) else response = @rack.get(URI.escape(resource.request_path)) # If we get a response, save it to a tempfile. if response.status == 200 export_file!(output_file, binary_encode(response.body)) else @has_error = true trigger(:error, output_file, response.body) end end rescue => e @has_error = true trigger(:error, output_file, "#{e}\n#{e.backtrace.join("\n")}") end return unless @cleaning return unless output_file.exist? # handle UTF-8-MAC filename on MacOS cleaned_name = if RUBY_PLATFORM =~ /darwin/ output_file.to_s.encode('UTF-8', 'UTF-8-MAC') else output_file end @to_clean.delete(Pathname(cleaned_name)) end
def prerender_css
def prerender_css logger.debug '== Prerendering CSS' css_files = @app.sitemap.resources.select do |resource| resource.ext == '.css' end.each(&method(:output_resource)) logger.debug '== Checking for Compass sprites' # Double-check for compass sprites @app.files.find_new_files! @app.sitemap.ensure_resource_list_updated! css_files end
def queue_current_paths
def queue_current_paths @to_clean = [] return unless File.exist?(@app.config[:build_dir]) paths = ::Middleman::Util.all_files_under(@app.config[:build_dir]).map do |path| Pathname(path) end @to_clean = paths.select do |path| path.to_s !~ /\/\./ || path.to_s =~ /\.(htaccess|htpasswd)/ end # handle UTF-8-MAC filename on MacOS @to_clean = @to_clean.map do |path| if RUBY_PLATFORM =~ /darwin/ Pathname(path.to_s.encode('UTF-8', 'UTF-8-MAC')) else Pathname(path) end end end
def run!
def run! @has_error = false @events = {} @app.execute_callbacks(:before_build, [self]) queue_current_paths if @cleaning prerender_css output_files clean if @cleaning ::Middleman::Profiling.report('build') @app.execute_callbacks(:after_build, [self]) !@has_error end
def trigger(event_type, target, extra=nil)
def trigger(event_type, target, extra=nil) @events[event_type] ||= [] @events[event_type] << target execute_callbacks(:on_build_event, [event_type, target, extra]) end
def which_mode(output_file, source)
def which_mode(output_file, source) if !output_file.exist? :created else FileUtils.compare_file(source.to_s, output_file.to_s) ? :identical : :updated end end
def write_tempfile(output_file, contents)
def write_tempfile(output_file, contents) file = Tempfile.new([ File.basename(output_file), File.extname(output_file) ]) file.binmode file.write(contents) file.close File.chmod(0644, file) file end