Skip to content

Commit

Permalink
Merge pull request #968 from nanoc/post-compile-cached-content
Browse files Browse the repository at this point in the history
Only compile outdated reps
  • Loading branch information
denisdefreyne committed Oct 28, 2016
2 parents bec4edd + a0da776 commit e148ecc
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 38 deletions.
3 changes: 2 additions & 1 deletion lib/nanoc/base/compilation/compiler.rb
Expand Up @@ -206,7 +206,8 @@ def compile_reps
end

# Find item reps to compile and compile them
selector = Nanoc::Int::ItemRepSelector.new(@reps)
outdated_reps = @reps.select { |r| outdatedness_checker.outdated?(r) }
selector = Nanoc::Int::ItemRepSelector.new(outdated_reps)
selector.each do |rep|
@stack = []
compile_rep(rep)
Expand Down
2 changes: 2 additions & 0 deletions lib/nanoc/base/views.rb
Expand Up @@ -24,3 +24,5 @@

require_relative 'views/post_compile_item_view'
require_relative 'views/post_compile_item_collection_view'
require_relative 'views/post_compile_item_rep_view'
require_relative 'views/post_compile_item_rep_collection_view'
15 changes: 10 additions & 5 deletions lib/nanoc/base/views/item_rep_collection_view.rb
Expand Up @@ -19,19 +19,24 @@ def unwrap
@item_reps
end

# @api private
def view_class
Nanoc::ItemRepView
end

def to_ary
@item_reps.map { |ir| Nanoc::ItemRepView.new(ir, @context) }
@item_reps.map { |ir| view_class.new(ir, @context) }
end

# Calls the given block once for each item rep, passing that item rep as a parameter.
#
# @yieldparam [Nanoc::ItemRepView] item rep
# @yieldparam [Object] item rep view
#
# @yieldreturn [void]
#
# @return [self]
def each
@item_reps.each { |ir| yield Nanoc::ItemRepView.new(ir, @context) }
@item_reps.each { |ir| yield view_class.new(ir, @context) }
self
end

Expand All @@ -51,7 +56,7 @@ def [](rep_name)
case rep_name
when Symbol
res = @item_reps.find { |ir| ir.name == rep_name }
res && Nanoc::ItemRepView.new(res, @context)
res && view_class.new(res, @context)
when Fixnum
raise ArgumentError, "expected ItemRepCollectionView#[] to be called with a symbol (you likely want `.reps[:default]` rather than `.reps[#{rep_name}]`)"
else
Expand All @@ -70,7 +75,7 @@ def [](rep_name)
def fetch(rep_name)
res = @item_reps.find { |ir| ir.name == rep_name }
if res
Nanoc::ItemRepView.new(res, @context)
view_class.new(res, @context)
else
raise NoSuchItemRepError.new(rep_name)
end
Expand Down
8 changes: 8 additions & 0 deletions lib/nanoc/base/views/post_compile_item_rep_collection_view.rb
@@ -0,0 +1,8 @@
module Nanoc
class PostCompileItemRepCollectionView < Nanoc::ItemRepCollectionView
# @api private
def view_class
Nanoc::PostCompileItemRepView
end
end
end
18 changes: 18 additions & 0 deletions lib/nanoc/base/views/post_compile_item_rep_view.rb
@@ -0,0 +1,18 @@
module Nanoc
class PostCompileItemRepView < ::Nanoc::ItemRepView
def compiled_content(snapshot: nil)
if unwrap.binary?
raise Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem.new(unwrap)
end

snapshot_contents = @context.compiler.compiled_content_cache[unwrap]
snapshot_name = snapshot || (snapshot_contents[:pre] ? :pre : :last)

if snapshot_contents[snapshot_name]
snapshot_contents[snapshot_name].string
else
raise Nanoc::Int::Errors::NoSuchSnapshot.new(unwrap, snapshot_name)
end
end
end
end
4 changes: 4 additions & 0 deletions lib/nanoc/base/views/post_compile_item_view.rb
@@ -1,5 +1,9 @@
module Nanoc
class PostCompileItemView < Nanoc::ItemWithRepsView
def reps
Nanoc::PostCompileItemRepCollectionView.new(@context.reps[unwrap], @context)
end

# @deprecated Use {#modified_reps} instead
def modified
modified_reps
Expand Down
15 changes: 10 additions & 5 deletions spec/nanoc/base/views/item_rep_collection_view_spec.rb
@@ -1,4 +1,4 @@
describe Nanoc::ItemRepCollectionView do
shared_examples 'an item rep collection view' do
let(:view) { described_class.new(wrapped, view_context) }

let(:view_context) { double(:view_context) }
Expand Down Expand Up @@ -57,7 +57,7 @@
it 'returns an array of item rep views' do
expect(subject.class).to eq(Array)
expect(subject.size).to eq(3)
expect(subject[0].class).to eql(Nanoc::ItemRepView)
expect(subject[0].class).to eql(expected_view_class)
expect(subject[0].name).to eql(:foo)
end

Expand All @@ -79,7 +79,7 @@
let(:name) { :foo }

it 'returns a view' do
expect(subject.class).to eq(Nanoc::ItemRepView)
expect(subject.class).to eq(expected_view_class)
expect(subject.name).to eq(:foo)
end

Expand Down Expand Up @@ -120,7 +120,7 @@
let(:name) { :foo }

it 'returns a view' do
expect(subject.class).to eq(Nanoc::ItemRepView)
expect(subject.class).to eq(expected_view_class)
expect(subject.name).to eq(:foo)
end

Expand All @@ -133,6 +133,11 @@
describe '#inspect' do
subject { view.inspect }

it { is_expected.to eql('<Nanoc::ItemRepCollectionView>') }
it { is_expected.to eql('<' + described_class.name + '>') }
end
end

describe Nanoc::ItemRepCollectionView do
it_behaves_like 'an item rep collection view'
let(:expected_view_class) { Nanoc::ItemRepView }
end
@@ -0,0 +1,4 @@
describe Nanoc::PostCompileItemRepCollectionView do
it_behaves_like 'an item rep collection view'
let(:expected_view_class) { Nanoc::PostCompileItemRepView }
end
136 changes: 136 additions & 0 deletions spec/nanoc/base/views/post_compile_item_rep_view_spec.rb
@@ -0,0 +1,136 @@
describe Nanoc::PostCompileItemRepView do
let(:item_rep) { Nanoc::Int::ItemRep.new(item, :jacques) }
let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo/') }
let(:view) { described_class.new(item_rep, view_context) }

let(:view_context) do
Nanoc::ViewContext.new(
reps: reps,
items: items,
dependency_tracker: dependency_tracker,
compiler: compiler,
)
end

let(:reps) { double(:reps) }
let(:items) { double(:items) }
let(:dependency_tracker) { Nanoc::Int::DependencyTracker.new(double(:dependency_store)) }
let(:compiler) { double(:compiler, compiled_content_cache: compiled_content_cache) }

let(:snapshot_contents) do
{
last: Nanoc::Int::TextualContent.new('content-last'),
pre: Nanoc::Int::TextualContent.new('content-pre'),
donkey: Nanoc::Int::TextualContent.new('content-donkey'),
}
end

let(:compiled_content_cache) do
Nanoc::Int::CompiledContentCache.new.tap do |ccc|
ccc[item_rep] = snapshot_contents
end
end

describe '#compiled_content' do
subject { view.compiled_content }

context 'binary' do
let(:item) do
content = Nanoc::Int::Content.create('/foo.dat', binary: true)
Nanoc::Int::Item.new(content, {}, '/foo.dat')
end

it 'raises error' do
err = Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem
expect { subject }.to raise_error(err)
end
end

shared_examples 'returns pre content' do
example { expect(subject).to eq('content-pre') }
end

shared_examples 'returns last content' do
example { expect(subject).to eq('content-last') }
end

shared_examples 'returns donkey content' do
example { expect(subject).to eq('content-donkey') }
end

shared_examples 'raises no-such-snapshot error' do
it 'raises error' do
err = Nanoc::Int::Errors::NoSuchSnapshot
expect { subject }.to raise_error(err)
end
end

context 'textual' do
context 'snapshot provided' do
subject { view.compiled_content(snapshot: :donkey) }
let(:expected_snapshot) { :donkey }

context 'snapshot exists' do
include_examples 'returns donkey content'
end

context 'snapshot does not exist' do
let(:snapshot_contents) do
{
last: Nanoc::Int::TextualContent.new('content-last'),
pre: Nanoc::Int::TextualContent.new('content-pre'),
}
end

include_examples 'raises no-such-snapshot error'
end
end

context 'no snapshot provided' do
context 'pre and last snapshots exist' do
let(:snapshot_contents) do
{
last: Nanoc::Int::TextualContent.new('content-last'),
pre: Nanoc::Int::TextualContent.new('content-pre'),
donkey: Nanoc::Int::TextualContent.new('content-donkey'),
}
end

include_examples 'returns pre content'
end

context 'pre snapshot exists' do
let(:snapshot_contents) do
{
pre: Nanoc::Int::TextualContent.new('content-pre'),
donkey: Nanoc::Int::TextualContent.new('content-donkey'),
}
end

include_examples 'returns pre content'
end

context 'last snapshot exists' do
let(:snapshot_contents) do
{
last: Nanoc::Int::TextualContent.new('content-last'),
donkey: Nanoc::Int::TextualContent.new('content-donkey'),
}
end

include_examples 'returns last content'
end

context 'neither pre nor last snapshot exists' do
let(:snapshot_contents) do
{
donkey: Nanoc::Int::TextualContent.new('content-donkey'),
}
end

include_examples 'raises no-such-snapshot error'
end
end
end
end
end
44 changes: 32 additions & 12 deletions spec/nanoc/base/views/post_compile_item_view_spec.rb
@@ -1,19 +1,19 @@
describe Nanoc::PostCompileItemView do
shared_examples 'a method that returns modified reps only' do
let(:item) { Nanoc::Int::Item.new('blah', {}, '/foo.md') }
let(:rep_a) { Nanoc::Int::ItemRep.new(item, :no_mod) }
let(:rep_b) { Nanoc::Int::ItemRep.new(item, :modded).tap { |r| r.modified = true } }

let(:reps) do
Nanoc::Int::ItemRepRepo.new.tap do |reps|
reps << rep_a
reps << rep_b
end
let(:item) { Nanoc::Int::Item.new('blah', {}, '/foo.md') }
let(:rep_a) { Nanoc::Int::ItemRep.new(item, :no_mod) }
let(:rep_b) { Nanoc::Int::ItemRep.new(item, :modded).tap { |r| r.modified = true } }

let(:reps) do
Nanoc::Int::ItemRepRepo.new.tap do |reps|
reps << rep_a
reps << rep_b
end
end

let(:view_context) { double(:view_context, reps: reps) }
let(:view) { described_class.new(item, view_context) }
let(:view_context) { double(:view_context, reps: reps) }
let(:view) { described_class.new(item, view_context) }

shared_examples 'a method that returns modified reps only' do
it 'returns only modified items' do
expect(subject.size).to eq(1)
expect(subject.map(&:name)).to eq(%i(modded))
Expand All @@ -24,13 +24,33 @@
end
end

shared_examples 'a method that returns PostCompileItemRepViews' do
it 'returns PostCompileItemRepViews' do
expect(subject).to all(be_a(Nanoc::PostCompileItemRepView))
end
end

describe '#modified_reps' do
subject { view.modified_reps }

it_behaves_like 'a method that returns modified reps only'
it_behaves_like 'a method that returns PostCompileItemRepViews'
end

describe '#modified' do
subject { view.modified }

it_behaves_like 'a method that returns modified reps only'
it_behaves_like 'a method that returns PostCompileItemRepViews'
end

describe '#reps' do
subject { view.reps }

it_behaves_like 'a method that returns PostCompileItemRepViews'

it 'returns a PostCompileItemRepCollectionView' do
expect(subject).to be_a(Nanoc::PostCompileItemRepCollectionView)
end
end
end
15 changes: 0 additions & 15 deletions test/base/test_compiler.rb
Expand Up @@ -208,21 +208,6 @@ def test_disallow_duplicate_routes
end
end

def test_compile_should_recompile_all_reps
Nanoc::CLI.run %w(create_site bar)

FileUtils.cd('bar') do
Nanoc::CLI.run %w(compile)

site = Nanoc::Int::SiteLoader.new.new_from_cwd
site.compile

# At this point, even the already compiled items in the previous pass
# should have their compiled content assigned, so this should work:
site.compiler.reps[site.items['/index.*']][0].compiled_content
end
end

def test_disallow_multiple_snapshots_with_the_same_name
# Create site
Nanoc::CLI.run %w(create_site bar)
Expand Down

0 comments on commit e148ecc

Please sign in to comment.