Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting content_for directly #1073

Merged
merged 9 commits into from Jan 22, 2017
135 changes: 97 additions & 38 deletions lib/nanoc/helpers/capturing.rb
@@ -1,38 +1,26 @@
module Nanoc::Helpers
# @see http://nanoc.ws/doc/reference/helpers/#capturing
module Capturing
# @overload content_for(name, params = {}, &block)
# @param [Symbol, String] name
# @option params [Symbol] existing
# @return [void]
#
# @overload content_for(item, name)
# @param [Symbol, String] name
# @return [String]
def content_for(*args, &block)
if block_given? # Set content
# Get args
case args.size
when 1
name = args[0]
params = {}
when 2
name = args[0]
params = args[1]
else
raise ArgumentError, 'expected 1 or 2 argument (the name ' \
"of the capture, and optionally params) but got #{args.size} instead"
end
name = args[0]
existing_behavior = params.fetch(:existing, :error)
# @api private
class SetContent
include Nanoc::Helpers::Capturing

def initialize(name, params, item)
@name = name
@params = params
@item = item
end

def run(&block)
existing_behavior = @params.fetch(:existing, :error)

# Capture
content_string = capture(&block)

# Get existing contents and prep for store
snapshot_repo = @item._context.snapshot_repo
rep = @item.reps[:default].unwrap
capture_name = "__capture_#{name}".to_sym
capture_name = "__capture_#{@name}".to_sym
old_content_string =
case existing_behavior
when :overwrite
Expand All @@ -44,7 +32,7 @@ def content_for(*args, &block)
contents = snapshot_repo.get(rep, capture_name)
if contents && contents.string != content_string
# FIXME: get proper exception
raise "a capture named #{name.inspect} for #{@item.identifier} already exists"
raise "a capture named #{@name.inspect} for #{@item.identifier} already exists"
else
''
end
Expand All @@ -56,33 +44,104 @@ def content_for(*args, &block)
# Store
new_content = Nanoc::Int::TextualContent.new(old_content_string + content_string)
snapshot_repo.set(rep, capture_name, new_content)
else # Get content
if args.size != 2
raise ArgumentError, 'expected 2 arguments (the item ' \
"and the name of the capture) but got #{args.size} instead"
end
item = args[0]
name = args[1]
end
end

# @api private
class GetContent
def initialize(requested_item, name, item, config)
@requested_item = requested_item
@name = name
@item = item
@config = config
end

rep = item.reps[:default].unwrap
def run
rep = @requested_item.reps[:default].unwrap

# Create dependency
if @item.nil? || item != @item.unwrap
if @item.nil? || @requested_item != @item.unwrap
dependency_tracker = @config._context.dependency_tracker
dependency_tracker.bounce(item.unwrap, compiled_content: true)
dependency_tracker.bounce(@requested_item.unwrap, compiled_content: true)

unless rep.compiled?
Fiber.yield(Nanoc::Int::Errors::UnmetDependency.new(rep))
return content_for(*args, &block)
return run
end
end

snapshot_repo = @config._context.snapshot_repo
content = snapshot_repo.get(rep, "__capture_#{name}".to_sym)
content = snapshot_repo.get(rep, "__capture_#{@name}".to_sym)
content ? content.string : nil
end
end

# @overload content_for(name, &block)
# @param [Symbol, String] name
# @return [void]
#
# @overload content_for(name, params, &block)
# @param [Symbol, String] name
# @option params [Symbol] existing
# @return [void]
#
# @overload content_for(name, content)
# @param [Symbol, String] name
# @param [String] content
# @return [void]
#
# @overload content_for(name, params, content)
# @param [Symbol, String] name
# @param [String] content
# @option params [Symbol] existing
# @return [void]
#
# @overload content_for(item, name)
# @param [Symbol, String] name
# @return [String]
def content_for(*args, &block)
if block_given? # Set content
name = args[0]
params =
case args.size
when 1
{}
when 2
args[1]
else
raise ArgumentError, 'expected 1 or 2 argument (the name ' \
"of the capture, and optionally params) but got #{args.size} instead"
end

SetContent.new(name, params, @item).run(&block)
elsif args.size > 1 && (args.first.is_a?(Symbol) || args.first.is_a?(String)) # Set content
name = args[0]
content = args.last
params =
case args.size
when 2
{}
when 3
args[1]
else
raise ArgumentError, 'expected 2 or 3 arguments (the name ' \
"of the capture, optionally params, and the content) but got #{args.size} instead"
end

_erbout = '' # rubocop:disable Lint/UnderscorePrefixedVariableName
SetContent.new(name, params, @item).run { _erbout << content }
else # Get content
if args.size != 2
raise ArgumentError, 'expected 2 arguments (the item ' \
"and the name of the capture) but got #{args.size} instead"
end
requested_item = args[0]
name = args[1]

GetContent.new(requested_item, name, @item, @config).run
end
end

# @return [String]
def capture(&block)
# Get erbout so far
Expand Down
149 changes: 109 additions & 40 deletions spec/nanoc/helpers/capturing_spec.rb
Expand Up @@ -5,68 +5,122 @@
ctx.create_rep(ctx.item, '/about.html')
end

describe 'with name + block' do
describe 'setting content' do
let(:_erbout) { 'existing content' }

context 'only name given' do
subject { helper.content_for(:foo) { _erbout << 'foo' } }
let(:params) { raise 'overwrite me' }

it 'stores snapshot content' do
subject
expect(ctx.snapshot_repo.get(ctx.item.reps[:default].unwrap, :__capture_foo).string).to eql('foo')
let(:contents_enumerator) { %w(foo bar).to_enum }

shared_examples 'setting content' do
context 'only name given' do
subject { subject_proc_without_params.call }

it 'stores snapshot content' do
subject
expect(ctx.snapshot_repo.get(ctx.item.reps[:default].unwrap, :__capture_foo).string).to eql('foo')
end
end
end

context 'name and params given' do
subject { helper.content_for(:foo, params) { _erbout << 'foo' } }
let(:params) { raise 'overwrite me' }
context 'name and params given' do
subject { subject_proc_with_params.call }
let(:params) { raise 'overwrite me' }

context 'no existing behavior specified' do
let(:params) { {} }
context 'no existing behavior specified' do
let(:params) { {} }

it 'errors after two times' do
helper.content_for(:foo, params) { _erbout << 'foo' }
expect { helper.content_for(:foo, params) { _erbout << 'bar' } }.to raise_error(RuntimeError)
it 'errors after two times' do
subject_proc_with_params.call
expect { subject_proc_with_params.call }.to raise_error(RuntimeError)
end
end
end

context 'existing behavior is :overwrite' do
let(:params) { { existing: :overwrite } }
context 'existing behavior is :overwrite' do
let(:params) { { existing: :overwrite } }

it 'overwrites' do
helper.content_for(:foo, params) { _erbout << 'foo' }
helper.content_for(:foo, params) { _erbout << 'bar' }
expect(ctx.snapshot_repo.get(ctx.item.reps[:default].unwrap, :__capture_foo).string).to eql('bar')
it 'overwrites' do
subject_proc_with_params.call
subject_proc_with_params.call
expect(ctx.snapshot_repo.get(ctx.item.reps[:default].unwrap, :__capture_foo).string).to eql('bar')
end
end
end

context 'existing behavior is :append' do
let(:params) { { existing: :append } }
context 'existing behavior is :append' do
let(:params) { { existing: :append } }

it 'appends' do
helper.content_for(:foo, params) { _erbout << 'foo' }
helper.content_for(:foo, params) { _erbout << 'bar' }
expect(ctx.snapshot_repo.get(ctx.item.reps[:default].unwrap, :__capture_foo).string).to eql('foobar')
it 'appends' do
subject_proc_with_params.call
subject_proc_with_params.call
expect(ctx.snapshot_repo.get(ctx.item.reps[:default].unwrap, :__capture_foo).string).to eql('foobar')
end
end
end

context 'existing behavior is :error' do
let(:params) { { existing: :error } }
context 'existing behavior is :error' do
let(:params) { { existing: :error } }

it 'errors after two times' do
helper.content_for(:foo, params) { _erbout << 'foo' }
expect { helper.content_for(:foo, params) { _erbout << 'bar' } }.to raise_error(RuntimeError)
it 'errors after two times' do
subject_proc_with_params.call
expect { subject_proc_with_params.call }.to raise_error(RuntimeError)
end
end
end

context 'existing behavior is :something else' do
let(:params) { { existing: :donkey } }
context 'existing behavior is :something else' do
let(:params) { { existing: :donkey } }

it 'errors' do
expect { subject }.to raise_error(ArgumentError)
it 'errors' do
expect { subject }.to raise_error(ArgumentError)
end
end
end
end

context 'symbol name + block' do
let(:subject_proc_without_params) do
-> { helper.content_for(:foo) { _erbout << contents_enumerator.next } }
end

let(:subject_proc_with_params) do
-> { helper.content_for(:foo, params) { _erbout << contents_enumerator.next } }
end

include_examples 'setting content'
end

context 'string name + block' do
let(:subject_proc_without_params) do
-> { helper.content_for('foo') { _erbout << contents_enumerator.next } }
end

let(:subject_proc_with_params) do
-> { helper.content_for('foo', params) { _erbout << contents_enumerator.next } }
end

include_examples 'setting content'
end

context 'symbol name + string' do
let(:subject_proc_without_params) do
-> { helper.content_for(:foo, contents_enumerator.next) }
end

let(:subject_proc_with_params) do
-> { helper.content_for(:foo, params, contents_enumerator.next) }
end

include_examples 'setting content'
end

context 'string name + string' do
let(:subject_proc_without_params) do
-> { helper.content_for('foo', contents_enumerator.next) }
end

let(:subject_proc_with_params) do
-> { helper.content_for('foo', params, contents_enumerator.next) }
end

include_examples 'setting content'
end
end

describe 'with item + name' do
Expand Down Expand Up @@ -103,6 +157,21 @@
expect(ctx.dependency_tracker).to receive(:bounce).with(item.unwrap, compiled_content: true)
expect { subject }.to raise_error(FiberError)
end

it 're-runs when fiber is resumed' do
expect(ctx.dependency_tracker).to receive(:bounce).with(item.unwrap, compiled_content: true).twice

fiber = Fiber.new { subject }
expect(fiber.resume).to be_a(Nanoc::Int::Errors::UnmetDependency)

item.reps[:default].unwrap.compiled = true
ctx.snapshot_repo.set(
item.reps[:default].unwrap,
:__capture_foo,
Nanoc::Int::TextualContent.new('content after compilation'),
)
expect(fiber.resume).to eql('content after compilation')
end
end

context 'other item is compiled' do
Expand Down