Skip to content

Commit

Permalink
Completely skip computation if nothing has changed
Browse files Browse the repository at this point in the history
This commit introduces a global dirty state. This check is used when
invoking a project. It checks to see if any of the known files have
changed, or if any files have been deleted or added. Projects are dirty
if the assetfile has changed. When projects are clean invoking them does
nothing. This check saves costly build time. The build tree is
constructed internally every time. This can be costly. Running those
rake tasks would not change anything anyway so that process can be by
passed.

This commit should make the preview server much faster. Say you have a
set set of assets: images, application.js, application.css, and
index.html. Opening the preview server will send an HTTP request for
index.html. This request will compile the pipeline. Further requests to
application.js, application.css and images should return almost
instantly because pipeline does not have to do any processing.
  • Loading branch information
twinturbo committed Oct 31, 2012
1 parent 26b512c commit 75f79b8
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 9 deletions.
13 changes: 13 additions & 0 deletions lib/rake-pipeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ def invoke
def setup
setup_filters
generate_rake_tasks
record_input_files
end

# Set up the filters. This will loop through all of the filters for
Expand Down Expand Up @@ -447,5 +448,17 @@ def assert_input_provided
end
end

# This is needed to ensure that every file procesed in the pipeline
# has an entry in the manifest. This is used to compare input files
# for the global dirty check
def record_input_files
input_files.each do |file|
full_path = file.fullpath

if File.exists?(full_path) && !manifest[full_path]
manifest[full_path] ||= ManifestEntry.new({}, File.mtime(full_path).to_i)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rake-pipeline/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ def generate_rake_tasks
@rake_tasks = outputs.map do |output, inputs|
additional_paths = []
inputs.each do |input|

create_file_task(input.fullpath).dynamic do
additional_paths += additional_dependencies(input)
end
Expand Down
19 changes: 19 additions & 0 deletions lib/rake-pipeline/manifest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,25 @@ def [](key)
def []=(key, value)
@entries[key] = value
end

def empty?
entries.empty?
end

def files
entries.inject({}) do |hash, pair|
file = pair.first
entry = pair.last

hash.merge!(file => entry.mtime)

entry.deps.each_pair do |name, time|
hash.merge!(name => time)
end

hash
end
end
end
end
end
69 changes: 60 additions & 9 deletions lib/rake-pipeline/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,15 @@ def build(&block)
# @see Rake::Pipeline#invoke
def invoke
@invoke_mutex.synchronize do
if assetfile_path
source = File.read(assetfile_path)
if digest(source) != assetfile_digest
rebuild_from_assetfile(assetfile_path, source)
end
end

last_manifest.read_manifest
pipelines.each(&:invoke)
manifest.write_manifest

if dirty?
rebuild_from_assetfile(assetfile_path) if assetfile_dirty?

pipelines.each(&:invoke)

manifest.write_manifest
end
end
end

Expand Down Expand Up @@ -264,6 +263,58 @@ def setup_pipelines
def digest(str)
Digest::SHA1.hexdigest(str)
end

def dirty?
assetfile_dirty? || files_dirty?
end

def assetfile_dirty?
if assetfile_path
source = File.read(assetfile_path)
digest(source) != assetfile_digest
else
false
end
end

# Returns true if any of these conditions are met:
# The pipeline hasn't been invoked yet
# The input files have been modified
# Any of the input files have been deleted
# There are new input files
def files_dirty?
return true if manifest.empty?

previous_files = manifest.files

input_files.each do |input_file|
if !File.exists? input_file
return true # existing input file has been deleted
elsif !previous_files[input_file]
return true # there is a new file in the pipeline
elsif File.mtime(input_file).to_i != previous_files[input_file]
return true # existing file has been changed
end
end

false
end

def input_files
static_input_files = pipelines.collect do |p|
p.input_files.reject { |file| file.in_directory? tmpdir }.map(&:fullpath)
end.flatten

dynamic_input_files = static_input_files.collect do |file|
if manifest[file]
manifest[file].deps.keys
else
nil
end
end.flatten.compact

static_input_files + dynamic_input_files
end
end
end
end
1 change: 1 addition & 0 deletions spec/rake_acceptance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ def copy_files

it "can be configured using the pipeline" do
pipeline = Rake::Pipeline.new
pipeline.project = Rake::Pipeline::Project.new
pipeline.add_input tmp, 'app/javascripts/*.js'
pipeline.output_root = File.expand_path("public")
pipeline.tmpdir = "temporary"
Expand Down

0 comments on commit 75f79b8

Please sign in to comment.