Skip to content

Commit

Permalink
Add convenience view methods
Browse files Browse the repository at this point in the history
  • Loading branch information
denisdefreyne committed May 11, 2015
1 parent 5d9a7e8 commit 16a6f2f
Show file tree
Hide file tree
Showing 10 changed files with 277 additions and 0 deletions.
22 changes: 22 additions & 0 deletions lib/nanoc/base/views/config.rb
Expand Up @@ -2,6 +2,9 @@

module Nanoc
class ConfigView
# @api private
NONE = Object.new

# @api private
def initialize(config)
@config = config
Expand All @@ -12,6 +15,25 @@ def unwrap
@config
end

# @see Hash#fetch
def fetch(key, fallback=NONE, &block)
@config.fetch(key) do
if !fallback.equal?(NONE)
fallback
elsif block_given?
yield(key)
else
raise KeyError, "key not found: #{key.inspect}"
end
end
end

# @see Hash#key?
def key?(key)
@config.key?(key)
end

# @see Hash#[]
def [](key)
@config[key]
end
Expand Down
31 changes: 31 additions & 0 deletions lib/nanoc/base/views/item.rb
Expand Up @@ -2,6 +2,9 @@

module Nanoc
class ItemView
# @api private
NONE = Object.new

# @api private
def initialize(item)
@item = item
Expand All @@ -25,6 +28,30 @@ def identifier
@item.identifier
end

# @see Hash#fetch
def fetch(key, fallback=NONE, &block)
res = @item[key] # necessary for dependency tracking

if @item.attributes.key?(key)
res
else
if !fallback.equal?(NONE)
fallback
elsif block_given?
yield(key)
else
raise KeyError, "key not found: #{key.inspect}"
end
end
end

# @see Hash#key?
def key?(key)
_res = @item[key] # necessary for dependency tracking
@item.attributes.key?(key)
end

# @see Hash#[]
def [](key)
@item[key]
end
Expand All @@ -41,6 +68,10 @@ def children
@item.children.map { |i| Nanoc::ItemView.new(i) }
end

def parent
Nanoc::ItemView.new(@item.parent)
end

def binary?
@item.binary?
end
Expand Down
5 changes: 5 additions & 0 deletions lib/nanoc/base/views/item_collection.rb
Expand Up @@ -23,6 +23,11 @@ def each
@items.each { |i| yield view_class.new(i) }
end

# @return [Integer]
def size
@items.size
end

def at(arg)
item = @items.at(arg)
item && view_class.new(item)
Expand Down
4 changes: 4 additions & 0 deletions lib/nanoc/base/views/mutable_item.rb
Expand Up @@ -5,5 +5,9 @@ class MutableItemView < Nanoc::ItemView
def []=(key, value)
unwrap[key] = value
end

def update_attributes(hash)
hash.each { |k, v| unwrap[k] = v }
end
end
end
4 changes: 4 additions & 0 deletions lib/nanoc/base/views/mutable_item_collection.rb
Expand Up @@ -14,5 +14,9 @@ def create(content, attributes, identifier, params = {})
def delete_if(&block)
@items.delete_if(&block)
end

def concat(other)
@items.concat(other)
end
end
end
69 changes: 69 additions & 0 deletions spec/nanoc/base/views/config_spec.rb
@@ -0,0 +1,69 @@
# encoding: utf-8

describe Nanoc::ConfigView do
let(:config) do
{ amount: 9000, animal: 'donkey' }
end

let(:view) { described_class.new(config) }

describe '#[]' do
subject { view[key] }

context 'with existant key' do
let(:key) { :animal }
it { should eql?('donkey') }
end

context 'with non-existant key' do
let(:key) { :weapon }
it { should eql?(nil) }
end
end

describe '#fetch' do
context 'with existant key' do
let(:key) { :animal }

subject { view.fetch(key) }

it { should eql?('donkey') }
end

context 'with non-existant key' do
let(:key) { :weapon }

context 'with fallback' do
subject { view.fetch(key, 'nothing sorry') }
it { should eql?('nothing sorry') }
end

context 'with block' do
subject { view.fetch(key) { 'nothing sorry' } }
it { should eql?('nothing sorry') }
end

context 'with no fallback and no block' do
subject { view.fetch(key) }

it 'raises' do
expect { subject }.to raise_error(KeyError)
end
end
end
end

describe '#key?' do
subject { view.key?(key) }

context 'with existant key' do
let(:key) { :animal }
it { should eql?(true) }
end

context 'with non-existant key' do
let(:key) { :weapon }
it { should eql?(false) }
end
end
end
14 changes: 14 additions & 0 deletions spec/nanoc/base/views/item_collection_spec.rb
Expand Up @@ -13,6 +13,20 @@
# …
end

describe '#size' do
let(:wrapped) do
[
Nanoc::Int::Item.new('foo', {}, '/foo/'),
Nanoc::Int::Item.new('bar', {}, '/bar/'),
Nanoc::Int::Item.new('baz', {}, '/baz/'),
]
end

subject { view.size }

it { should == 3 }
end

describe '#at' do
subject { view.at(arg) }

Expand Down
96 changes: 96 additions & 0 deletions spec/nanoc/base/views/item_spec.rb
Expand Up @@ -59,4 +59,100 @@

it { should == described_class.hash ^ '/foo/'.hash }
end

describe '#parent' do
let(:parent_item) do
Nanoc::Int::Item.new('parent', {}, '/parent/')
end

let(:item) do
Nanoc::Int::Item.new('me', {}, '/me/').tap { |i| i.parent = parent_item }
end

let(:view) { described_class.new(item) }

subject { view.parent }

it 'returns a view for the parent' do
expect(subject.class).to eql(Nanoc::ItemView)
expect(subject.unwrap).to eql(parent_item)
end
end

describe '#[]' do
let(:item) { Nanoc::Int::Item.new('stuff', { animal: 'donkey' }, '/foo/') }
let(:view) { described_class.new(item) }

subject { view[key] }

context 'with existant key' do
let(:key) { :animal }
it { should eql?('donkey') }
end

context 'with non-existant key' do
let(:key) { :weapon }
it { should eql?(nil) }
end
end

describe '#fetch' do
let(:item) { Nanoc::Int::Item.new('stuff', { animal: 'donkey' }, '/foo/') }
let(:view) { described_class.new(item) }

before do
expect(Nanoc::Int::NotificationCenter).to receive(:post).twice
end

context 'with existant key' do
let(:key) { :animal }

subject { view.fetch(key) }

it { should eql?('donkey') }
end

context 'with non-existant key' do
let(:key) { :weapon }

context 'with fallback' do
subject { view.fetch(key, 'nothing sorry') }
it { should eql?('nothing sorry') }
end

context 'with block' do
subject { view.fetch(key) { 'nothing sorry' } }
it { should eql?('nothing sorry') }
end

context 'with no fallback and no block' do
subject { view.fetch(key) }

it 'raises' do
expect { subject }.to raise_error(KeyError)
end
end
end
end

describe '#key?' do
let(:item) { Nanoc::Int::Item.new('stuff', { animal: 'donkey' }, '/foo/') }
let(:view) { described_class.new(item) }

before do
expect(Nanoc::Int::NotificationCenter).to receive(:post).twice
end

subject { view.key?(key) }

context 'with existant key' do
let(:key) { :animal }
it { should eql?(true) }
end

context 'with non-existant key' do
let(:key) { :weapon }
it { should eql?(false) }
end
end
end
19 changes: 19 additions & 0 deletions spec/nanoc/base/views/mutable_item_collection_spec.rb
Expand Up @@ -46,4 +46,23 @@
expect(mutable_item_collection).not_to be_empty
end
end

describe '#concat' do
let(:mutable_item_collection) do
[Nanoc::Int::Item.new('content', {}, '/asdf/')]
end

let(:items_array_to_concat) do
[Nanoc::Int::Item.new('shiny', {}, '/new/')]
end

let(:view) { described_class.new(mutable_item_collection) }

subject { view.concat(items_array_to_concat) }

it 'concats' do
expect { subject }.to change { view.size }.from(1).to(2)
expect(view[1].unwrap).to eql(items_array_to_concat[0])
end
end
end
13 changes: 13 additions & 0 deletions spec/nanoc/base/views/mutable_item_spec.rb
Expand Up @@ -10,4 +10,17 @@
expect(view[:title]).to eq('Donkey')
end
end

describe '#update_attributes' do
let(:item) { Nanoc::Int::Item.new('content', {}, '/asdf/') }
let(:view) { described_class.new(item) }

let(:update) { { friend: 'Giraffe' } }

subject { view.update_attributes(update) }

it 'sets attributes' do
expect { subject }.to change { view[:friend] }.from(nil).to('Giraffe')
end
end
end

0 comments on commit 16a6f2f

Please sign in to comment.