From 8b381ad3e731d9aa2e5002479b473738d39d0a41 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 13:08:57 +0200 Subject: [PATCH 1/7] Refactor: Add action seq convenience methods to RecordingExecutor --- lib/nanoc/rule_dsl/action_sequence_calculator.rb | 12 +++--------- lib/nanoc/rule_dsl/recording_executor.rb | 12 ++++++++++++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/nanoc/rule_dsl/action_sequence_calculator.rb b/lib/nanoc/rule_dsl/action_sequence_calculator.rb index 8f90b9cdca..b5fae8b68f 100644 --- a/lib/nanoc/rule_dsl/action_sequence_calculator.rb +++ b/lib/nanoc/rule_dsl/action_sequence_calculator.rb @@ -68,15 +68,9 @@ def new_action_sequence_for_rep(rep) executor.snapshot(:raw) rule.apply_to(rep, executor: executor, site: @site, view_context: view_context) - if action_sequence.any_layouts? - executor.snapshot(:post) - end - unless action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:last) } - executor.snapshot(:last) - end - unless action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:pre) } - executor.snapshot(:pre) - end + executor.snapshot(:post) if executor.any_layouts? + executor.snapshot(:last) unless executor.last_snapshot? + executor.snapshot(:pre) unless executor.pre_snapshot? copy_paths_from_routing_rules(compact_snapshots(action_sequence), rep: rep) end diff --git a/lib/nanoc/rule_dsl/recording_executor.rb b/lib/nanoc/rule_dsl/recording_executor.rb index 12d50f29a0..a2622a0617 100644 --- a/lib/nanoc/rule_dsl/recording_executor.rb +++ b/lib/nanoc/rule_dsl/recording_executor.rb @@ -29,6 +29,18 @@ def snapshot(snapshot_name, path: nil) @action_sequence.add_snapshot(snapshot_name, path && path.to_s) nil end + + def any_layouts? + @action_sequence.any_layouts? + end + + def last_snapshot? + @action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:last) } + end + + def pre_snapshot? + @action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:pre) } + end end end end From 10bb99e1be6febd3c432b0dd40c00cbf89f38ad0 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 13:16:53 +0200 Subject: [PATCH 2/7] Pass item rep, not action seq, to recording executor --- lib/nanoc/rule_dsl/action_sequence_calculator.rb | 5 ++--- lib/nanoc/rule_dsl/recording_executor.rb | 10 ++++++++-- spec/nanoc/rule_dsl/recording_executor_spec.rb | 7 ++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/nanoc/rule_dsl/action_sequence_calculator.rb b/lib/nanoc/rule_dsl/action_sequence_calculator.rb index b5fae8b68f..ef6d239854 100644 --- a/lib/nanoc/rule_dsl/action_sequence_calculator.rb +++ b/lib/nanoc/rule_dsl/action_sequence_calculator.rb @@ -58,8 +58,7 @@ def new_action_sequence_for_rep(rep) dependency_tracker = Nanoc::Int::DependencyTracker::Null.new view_context = @site.compiler.compilation_context.create_view_context(dependency_tracker) - action_sequence = Nanoc::Int::ActionSequence.new(rep) - executor = Nanoc::RuleDSL::RecordingExecutor.new(action_sequence) + executor = Nanoc::RuleDSL::RecordingExecutor.new(rep) rule = @rules_collection.compilation_rule_for(rep) unless rule @@ -72,7 +71,7 @@ def new_action_sequence_for_rep(rep) executor.snapshot(:last) unless executor.last_snapshot? executor.snapshot(:pre) unless executor.pre_snapshot? - copy_paths_from_routing_rules(compact_snapshots(action_sequence), rep: rep) + copy_paths_from_routing_rules(compact_snapshots(executor.action_sequence), rep: rep) end # @param [Nanoc::Int::Layout] layout diff --git a/lib/nanoc/rule_dsl/recording_executor.rb b/lib/nanoc/rule_dsl/recording_executor.rb index a2622a0617..3b4591340d 100644 --- a/lib/nanoc/rule_dsl/recording_executor.rb +++ b/lib/nanoc/rule_dsl/recording_executor.rb @@ -3,8 +3,11 @@ module RuleDSL class RecordingExecutor include Nanoc::Int::ContractsSupport - def initialize(action_sequence) - @action_sequence = action_sequence + attr_reader :action_sequence + + contract Nanoc::Int::ItemRep => C::Any + def initialize(rep) + @action_sequence = Nanoc::Int::ActionSequence.new(rep) end def filter(filter_name, filter_args = {}) @@ -30,14 +33,17 @@ def snapshot(snapshot_name, path: nil) nil end + contract C::None => C::Bool def any_layouts? @action_sequence.any_layouts? end + contract C::None => C::Bool def last_snapshot? @action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:last) } end + contract C::None => C::Bool def pre_snapshot? @action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:pre) } end diff --git a/spec/nanoc/rule_dsl/recording_executor_spec.rb b/spec/nanoc/rule_dsl/recording_executor_spec.rb index c3df35c400..f53b02d39e 100644 --- a/spec/nanoc/rule_dsl/recording_executor_spec.rb +++ b/spec/nanoc/rule_dsl/recording_executor_spec.rb @@ -1,8 +1,9 @@ describe Nanoc::RuleDSL::RecordingExecutor do - let(:executor) { described_class.new(action_sequence) } + let(:executor) { described_class.new(rep) } - let(:action_sequence) { Nanoc::Int::ActionSequence.new(rep) } - let(:rep) { double(:rep) } + let(:action_sequence) { executor.action_sequence } + let(:item) { Nanoc::Int::Item.new('stuff', {}, '/foo.md') } + let(:rep) { Nanoc::Int::ItemRep.new(item, :default) } describe '#filter' do it 'records filter call without arguments' do From 7848ecde0430c23b8d41e862ef67d95779ccf91b Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 13:23:04 +0200 Subject: [PATCH 3/7] Precompute action seq properties --- lib/nanoc/base/entities/action_sequence.rb | 5 ----- lib/nanoc/rule_dsl/recording_executor.rb | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/nanoc/base/entities/action_sequence.rb b/lib/nanoc/base/entities/action_sequence.rb index 77d1325e1d..4a080eaca5 100644 --- a/lib/nanoc/base/entities/action_sequence.rb +++ b/lib/nanoc/base/entities/action_sequence.rb @@ -45,11 +45,6 @@ def snapshot_actions @actions.select { |a| a.is_a?(Nanoc::Int::ProcessingActions::Snapshot) } end - contract C::None => C::Bool - def any_layouts? - @actions.any? { |a| a.is_a?(Nanoc::Int::ProcessingActions::Layout) } - end - contract C::None => Array def paths snapshot_actions.map { |a| [a.snapshot_names, a.paths] } diff --git a/lib/nanoc/rule_dsl/recording_executor.rb b/lib/nanoc/rule_dsl/recording_executor.rb index 3b4591340d..c6f564104e 100644 --- a/lib/nanoc/rule_dsl/recording_executor.rb +++ b/lib/nanoc/rule_dsl/recording_executor.rb @@ -8,6 +8,10 @@ class RecordingExecutor contract Nanoc::Int::ItemRep => C::Any def initialize(rep) @action_sequence = Nanoc::Int::ActionSequence.new(rep) + + @any_layouts = false + @last_snapshot = false + @pre_snapshot = false end def filter(filter_name, filter_args = {}) @@ -19,33 +23,41 @@ def layout(layout_identifier, extra_filter_args = {}) raise ArgumentError.new('The layout passed to #layout must be a string') end - unless @action_sequence.any_layouts? + unless any_layouts? + @pre_snapshot = true @action_sequence.add_snapshot(:pre, nil) end @action_sequence.add_layout(layout_identifier, extra_filter_args) + @any_layouts = true end Pathlike = C::Maybe[C::Or[String, Nanoc::Identifier]] contract Symbol, C::KeywordArgs[path: C::Optional[Pathlike]] => nil def snapshot(snapshot_name, path: nil) @action_sequence.add_snapshot(snapshot_name, path && path.to_s) + case snapshot_name + when :last + @last_snapshot = true + when :pre + @pre_snapshot = true + end nil end contract C::None => C::Bool def any_layouts? - @action_sequence.any_layouts? + @any_layouts end contract C::None => C::Bool def last_snapshot? - @action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:last) } + @last_snapshot end contract C::None => C::Bool def pre_snapshot? - @action_sequence.snapshot_actions.any? { |sa| sa.snapshot_names.include?(:pre) } + @pre_snapshot end end end From 556ca1f319455549eb8cf2ff0d633232a87d18df Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 13:41:14 +0200 Subject: [PATCH 4/7] Refactor: Add ActionSequenceBuilder (delegates for now) --- lib/nanoc/base/entities/action_sequence.rb | 6 ++++ lib/nanoc/base/services.rb | 1 + .../base/services/action_sequence_builder.rb | 33 +++++++++++++++++++ lib/nanoc/rule_dsl/recording_executor.rb | 16 +++++---- 4 files changed, 50 insertions(+), 6 deletions(-) create mode 100644 lib/nanoc/base/services/action_sequence_builder.rb diff --git a/lib/nanoc/base/entities/action_sequence.rb b/lib/nanoc/base/entities/action_sequence.rb index 4a080eaca5..39b319d97d 100644 --- a/lib/nanoc/base/entities/action_sequence.rb +++ b/lib/nanoc/base/entities/action_sequence.rb @@ -11,6 +11,12 @@ def initialize(item_rep, actions: []) @actions = actions end + def self.build(rep) + builder = Nanoc::Int::ActionSequenceBuilder.new(rep) + yield(builder) + builder.action_sequence + end + contract C::None => Numeric def size @actions.size diff --git a/lib/nanoc/base/services.rb b/lib/nanoc/base/services.rb index b6edfd46d7..732ec3bf63 100644 --- a/lib/nanoc/base/services.rb +++ b/lib/nanoc/base/services.rb @@ -1,4 +1,5 @@ require_relative 'services/action_provider' +require_relative 'services/action_sequence_builder' require_relative 'services/checksummer' require_relative 'services/compilation_context' require_relative 'services/compiler' diff --git a/lib/nanoc/base/services/action_sequence_builder.rb b/lib/nanoc/base/services/action_sequence_builder.rb new file mode 100644 index 0000000000..9794934dc1 --- /dev/null +++ b/lib/nanoc/base/services/action_sequence_builder.rb @@ -0,0 +1,33 @@ +module Nanoc::Int + class ActionSequenceBuilder + include Nanoc::Int::ContractsSupport + + def initialize(rep) + @rep = rep + @action_sequence = Nanoc::Int::ActionSequence.new(rep) + end + + contract Symbol, Hash => self + def add_filter(filter_name, params) + @action_sequence.add_filter(filter_name, params) + self + end + + contract String, C::Maybe[Hash] => self + def add_layout(layout_identifier, params) + @action_sequence.add_layout(layout_identifier, params) + self + end + + contract Symbol, C::Maybe[String] => self + def add_snapshot(snapshot_name, path) + @action_sequence.add_snapshot(snapshot_name, path) + self + end + + contract C::None => Nanoc::Int::ActionSequence + def action_sequence + @action_sequence + end + end +end diff --git a/lib/nanoc/rule_dsl/recording_executor.rb b/lib/nanoc/rule_dsl/recording_executor.rb index c6f564104e..24b8503180 100644 --- a/lib/nanoc/rule_dsl/recording_executor.rb +++ b/lib/nanoc/rule_dsl/recording_executor.rb @@ -3,11 +3,10 @@ module RuleDSL class RecordingExecutor include Nanoc::Int::ContractsSupport - attr_reader :action_sequence - contract Nanoc::Int::ItemRep => C::Any def initialize(rep) @action_sequence = Nanoc::Int::ActionSequence.new(rep) + @action_sequence_builder = Nanoc::Int::ActionSequenceBuilder.new(rep) @any_layouts = false @last_snapshot = false @@ -15,7 +14,7 @@ def initialize(rep) end def filter(filter_name, filter_args = {}) - @action_sequence.add_filter(filter_name, filter_args) + @action_sequence_builder.add_filter(filter_name, filter_args) end def layout(layout_identifier, extra_filter_args = {}) @@ -25,17 +24,17 @@ def layout(layout_identifier, extra_filter_args = {}) unless any_layouts? @pre_snapshot = true - @action_sequence.add_snapshot(:pre, nil) + @action_sequence_builder.add_snapshot(:pre, nil) end - @action_sequence.add_layout(layout_identifier, extra_filter_args) + @action_sequence_builder.add_layout(layout_identifier, extra_filter_args) @any_layouts = true end Pathlike = C::Maybe[C::Or[String, Nanoc::Identifier]] contract Symbol, C::KeywordArgs[path: C::Optional[Pathlike]] => nil def snapshot(snapshot_name, path: nil) - @action_sequence.add_snapshot(snapshot_name, path && path.to_s) + @action_sequence_builder.add_snapshot(snapshot_name, path && path.to_s) case snapshot_name when :last @last_snapshot = true @@ -45,6 +44,11 @@ def snapshot(snapshot_name, path: nil) nil end + contract C::None => Nanoc::Int::ActionSequence + def action_sequence + @action_sequence_builder.action_sequence + end + contract C::None => C::Bool def any_layouts? @any_layouts From eeb30005a2a29f9b34d0625f7f5d25b9565e9346 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 13:54:30 +0200 Subject: [PATCH 5/7] Refactor: Use ActionSequenceBuilder in ActionSequence spec --- .../rule_dsl/action_sequence_calculator.rb | 4 +- .../base/entities/action_sequence_spec.rb | 211 +++++++++++------- spec/nanoc/base/services/executor_spec.rb | 4 +- .../services/outdatedness_checker_spec.rb | 46 ++-- .../base/services/outdatedness_rules_spec.rb | 28 +-- .../action_sequence_calculator_spec.rb | 28 +-- 6 files changed, 188 insertions(+), 133 deletions(-) diff --git a/lib/nanoc/rule_dsl/action_sequence_calculator.rb b/lib/nanoc/rule_dsl/action_sequence_calculator.rb index ef6d239854..1d56f23685 100644 --- a/lib/nanoc/rule_dsl/action_sequence_calculator.rb +++ b/lib/nanoc/rule_dsl/action_sequence_calculator.rb @@ -84,8 +84,8 @@ def new_action_sequence_for_layout(layout) raise NoActionSequenceForLayoutException.new(layout) end - Nanoc::Int::ActionSequence.new(layout).tap do |rm| - rm.add_filter(res[0], res[1]) + Nanoc::Int::ActionSequence.build(layout) do |b| + b.add_filter(res[0], res[1]) end end diff --git a/spec/nanoc/base/entities/action_sequence_spec.rb b/spec/nanoc/base/entities/action_sequence_spec.rb index 1eaa72ad27..be89c8cc3c 100644 --- a/spec/nanoc/base/entities/action_sequence_spec.rb +++ b/spec/nanoc/base/entities/action_sequence_spec.rb @@ -1,17 +1,26 @@ describe Nanoc::Int::ActionSequence do - let(:action_sequence) { described_class.new(rep) } - let(:rep) { double(:rep) } + let(:action_sequence) { raise 'override me' } + + let(:item) { Nanoc::Int::Item.new('foo', {}, '/foo.md') } + let(:rep) { Nanoc::Int::ItemRep.new(item, :default) } describe '#size' do subject { action_sequence.size } context 'no actions' do + let(:action_sequence) do + described_class.build(rep) do |b| + end + end + it { is_expected.to eql(0) } end context 'some actions' do - before do - action_sequence.add_filter(:foo, {}) + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_filter(:foo, {}) + end end it { is_expected.to eql(1) } @@ -23,12 +32,19 @@ let(:index) { 0 } context 'no actions' do + let(:action_sequence) do + described_class.build(rep) do |b| + end + end + it { is_expected.to be_nil } end context 'some actions' do - before do - action_sequence.add_filter(:foo, {}) + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_filter(:foo, {}) + end end it { is_expected.to be_a(Nanoc::Int::ProcessingActions::Filter) } @@ -36,9 +52,13 @@ end describe '#add_filter' do - example do - action_sequence.add_filter(:foo, donkey: 123) + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_filter(:foo, donkey: 123) + end + end + example do expect(action_sequence.size).to eql(1) expect(action_sequence[0]).to be_a(Nanoc::Int::ProcessingActions::Filter) expect(action_sequence[0].filter_name).to eql(:foo) @@ -47,9 +67,13 @@ end describe '#add_layout' do - example do - action_sequence.add_layout('/foo.*', donkey: 123) + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_layout('/foo.*', donkey: 123) + end + end + example do expect(action_sequence.size).to eql(1) expect(action_sequence[0]).to be_a(Nanoc::Int::ProcessingActions::Layout) expect(action_sequence[0].layout_identifier).to eql('/foo.*') @@ -59,9 +83,13 @@ describe '#add_snapshot' do context 'snapshot does not yet exist' do - example do - action_sequence.add_snapshot(:before_layout, '/foo.md') + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_snapshot(:before_layout, '/foo.md') + end + end + example do expect(action_sequence.size).to eql(1) expect(action_sequence[0]).to be_a(Nanoc::Int::ProcessingActions::Snapshot) expect(action_sequence[0].snapshot_names).to eql([:before_layout]) @@ -70,22 +98,23 @@ end context 'snapshot already exist' do - before do - action_sequence.add_snapshot(:before_layout, '/bar.md') - end - it 'raises' do - expect { action_sequence.add_snapshot(:before_layout, '/foo.md') } - .to raise_error(Nanoc::Int::Errors::CannotCreateMultipleSnapshotsWithSameName) + described_class.build(rep) do |b| + b.add_snapshot(:before_layout, '/bar.md') + expect { b.add_snapshot(:before_layout, '/foo.md') } + .to raise_error(Nanoc::Int::Errors::CannotCreateMultipleSnapshotsWithSameName) + end end end end describe '#each' do - before do - action_sequence.add_filter(:erb, awesomeness: 'high') - action_sequence.add_snapshot(:bar, '/foo.md') - action_sequence.add_layout('/default.erb', somelayoutparam: 'yes') + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_filter(:erb, awesomeness: 'high') + b.add_snapshot(:bar, '/foo.md') + b.add_layout('/default.erb', somelayoutparam: 'yes') + end end example do @@ -96,10 +125,12 @@ end describe '#map' do - before do - action_sequence.add_filter(:erb, awesomeness: 'high') - action_sequence.add_snapshot(:bar, '/foo.md') - action_sequence.add_layout('/default.erb', somelayoutparam: 'yes') + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_filter(:erb, awesomeness: 'high') + b.add_snapshot(:bar, '/foo.md') + b.add_layout('/default.erb', somelayoutparam: 'yes') + end end example do @@ -112,10 +143,12 @@ describe '#serialize' do subject { action_sequence.serialize } - before do - action_sequence.add_filter(:erb, awesomeness: 'high') - action_sequence.add_snapshot(:bar, '/foo.md') - action_sequence.add_layout('/default.erb', somelayoutparam: 'yes') + let(:action_sequence) do + described_class.build(rep) do |b| + b.add_filter(:erb, awesomeness: 'high') + b.add_snapshot(:bar, '/foo.md') + b.add_layout('/default.erb', somelayoutparam: 'yes') + end end example do @@ -130,8 +163,6 @@ end describe '#snapshots_defs' do - subject { action_sequence.snapshots_defs } - let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } let(:rep) { Nanoc::Int::ItemRep.new(item, :default) } @@ -164,42 +195,55 @@ def run(content, params = {}); end end it 'has no snapshot defs by default' do - expect(subject).to be_empty + action_sequence = + described_class.build(rep) do |b| + end + + expect(action_sequence.snapshots_defs).to be_empty end context 'textual item' do let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } it 'generates initial textual snapshot def' do - action_sequence.add_snapshot(:giraffe, nil) - - expect(subject.size).to eq(1) - expect(subject[0].name).to eq(:giraffe) - expect(subject[0]).not_to be_binary + action_sequence = + described_class.build(rep) do |b| + b.add_snapshot(:giraffe, nil) + end + + expect(action_sequence.snapshots_defs.size).to eq(1) + expect(action_sequence.snapshots_defs[0].name).to eq(:giraffe) + expect(action_sequence.snapshots_defs[0]).not_to be_binary end it 'generated follow-up textual snapshot def if previous filter is textual' do - action_sequence.add_snapshot(:giraffe, nil) - action_sequence.add_filter(:RuleMemSpec_filter_t2t, arguments: 'irrelevant') - action_sequence.add_snapshot(:zebra, nil) - - expect(subject.size).to eq(2) - expect(subject[0].name).to eq(:giraffe) - expect(subject[0]).not_to be_binary - expect(subject[1].name).to eq(:zebra) - expect(subject[1]).not_to be_binary + action_sequence = + described_class.build(rep) do |b| + b.add_snapshot(:giraffe, nil) + b.add_filter(:RuleMemSpec_filter_t2t, arguments: 'irrelevant') + b.add_snapshot(:zebra, nil) + end + + expect(action_sequence.snapshots_defs.size).to eq(2) + expect(action_sequence.snapshots_defs[0].name).to eq(:giraffe) + expect(action_sequence.snapshots_defs[0]).not_to be_binary + expect(action_sequence.snapshots_defs[1].name).to eq(:zebra) + expect(action_sequence.snapshots_defs[1]).not_to be_binary end it 'generated follow-up binary snapshot def if previous filter is text-to-bianry' do - action_sequence.add_snapshot(:giraffe, nil) - action_sequence.add_filter(:RuleMemSpec_filter_t2b, arguments: 'irrelevant') - action_sequence.add_snapshot(:zebra, nil) - - expect(subject.size).to eq(2) - expect(subject[0].name).to eq(:giraffe) - expect(subject[0]).not_to be_binary - expect(subject[1].name).to eq(:zebra) - expect(subject[1]).to be_binary + action_sequence = + described_class.build(rep) do |b| + b.add_snapshot(:giraffe, nil) + b.add_filter(:RuleMemSpec_filter_t2b, arguments: 'irrelevant') + b.add_snapshot(:zebra, nil) + end + + expect(action_sequence.snapshots_defs.size).to eq(2) + expect(action_sequence.snapshots_defs[0].name).to eq(:giraffe) + expect(action_sequence.snapshots_defs[0]).not_to be_binary + expect(action_sequence.snapshots_defs[1].name).to eq(:zebra) + expect(action_sequence.snapshots_defs[1]).to be_binary end end @@ -207,35 +251,44 @@ def run(content, params = {}); end let(:item) { Nanoc::Int::Item.new(Nanoc::Int::BinaryContent.new('/asdf.dat'), {}, '/foo.md') } it 'generates initial binary snapshot def' do - action_sequence.add_snapshot(:giraffe, nil) - - expect(subject.size).to eq(1) - expect(subject[0].name).to eq(:giraffe) - expect(subject[0]).to be_binary + action_sequence = + described_class.build(rep) do |b| + b.add_snapshot(:giraffe, nil) + end + + expect(action_sequence.snapshots_defs.size).to eq(1) + expect(action_sequence.snapshots_defs[0].name).to eq(:giraffe) + expect(action_sequence.snapshots_defs[0]).to be_binary end it 'generated follow-up binary snapshot def if previous filter is binary' do - action_sequence.add_snapshot(:giraffe, nil) - action_sequence.add_filter(:RuleMemSpec_filter_b2b, arguments: 'irrelevant') - action_sequence.add_snapshot(:zebra, nil) - - expect(subject.size).to eq(2) - expect(subject[0].name).to eq(:giraffe) - expect(subject[0]).to be_binary - expect(subject[1].name).to eq(:zebra) - expect(subject[1]).to be_binary + action_sequence = + described_class.build(rep) do |b| + b.add_snapshot(:giraffe, nil) + b.add_filter(:RuleMemSpec_filter_b2b, arguments: 'irrelevant') + b.add_snapshot(:zebra, nil) + end + + expect(action_sequence.snapshots_defs.size).to eq(2) + expect(action_sequence.snapshots_defs[0].name).to eq(:giraffe) + expect(action_sequence.snapshots_defs[0]).to be_binary + expect(action_sequence.snapshots_defs[1].name).to eq(:zebra) + expect(action_sequence.snapshots_defs[1]).to be_binary end it 'generated follow-up textual snapshot def if previous filter is binary-to-text' do - action_sequence.add_snapshot(:giraffe, nil) - action_sequence.add_filter(:RuleMemSpec_filter_b2t, arguments: 'irrelevant') - action_sequence.add_snapshot(:zebra, nil) - - expect(subject.size).to eq(2) - expect(subject[0].name).to eq(:giraffe) - expect(subject[0]).to be_binary - expect(subject[1].name).to eq(:zebra) - expect(subject[1]).not_to be_binary + action_sequence = + described_class.build(rep) do |b| + b.add_snapshot(:giraffe, nil) + b.add_filter(:RuleMemSpec_filter_b2t, arguments: 'irrelevant') + b.add_snapshot(:zebra, nil) + end + + expect(action_sequence.snapshots_defs.size).to eq(2) + expect(action_sequence.snapshots_defs[0].name).to eq(:giraffe) + expect(action_sequence.snapshots_defs[0]).to be_binary + expect(action_sequence.snapshots_defs[1].name).to eq(:zebra) + expect(action_sequence.snapshots_defs[1]).not_to be_binary end end end diff --git a/spec/nanoc/base/services/executor_spec.rb b/spec/nanoc/base/services/executor_spec.rb index 18b9eceb40..3a975f5508 100644 --- a/spec/nanoc/base/services/executor_spec.rb +++ b/spec/nanoc/base/services/executor_spec.rb @@ -408,8 +408,8 @@ def run(_content, params = {}) end let(:action_sequence) do - Nanoc::Int::ActionSequence.new(rep).tap do |seq| - seq.add_filter(:erb, {}) + Nanoc::Int::ActionSequence.build(rep) do |b| + b.add_filter(:erb, {}) end end diff --git a/spec/nanoc/base/services/outdatedness_checker_spec.rb b/spec/nanoc/base/services/outdatedness_checker_spec.rb index e7c7f6eddf..f806cf2342 100644 --- a/spec/nanoc/base/services/outdatedness_checker_spec.rb +++ b/spec/nanoc/base/services/outdatedness_checker_spec.rb @@ -32,8 +32,8 @@ end let(:old_action_sequence_for_item_rep) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |seq| - seq.add_filter(:erb, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_filter(:erb, {}) end end @@ -74,8 +74,8 @@ context 'action sequence differs' do let(:new_action_sequence_for_item_rep) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |seq| - seq.add_filter(:super_erb, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_filter(:super_erb, {}) end end @@ -92,8 +92,8 @@ context 'action sequence differs' do let(:new_action_sequence_for_item_rep) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |seq| - seq.add_filter(:super_erb, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_filter(:super_erb, {}) end end @@ -123,8 +123,8 @@ let(:items) { Nanoc::Int::IdentifiableCollection.new(config, [item, other_item]) } let(:old_action_sequence_for_other_item_rep) do - Nanoc::Int::ActionSequence.new(other_item_rep).tap do |seq| - seq.add_filter(:erb, {}) + Nanoc::Int::ActionSequence.build(other_item_rep) do |b| + b.add_filter(:erb, {}) end end @@ -252,9 +252,9 @@ context 'path changed' do let(:new_action_sequence_for_other_item_rep) do - Nanoc::Int::ActionSequence.new(other_item_rep).tap do |seq| - seq.add_filter(:erb, {}) - seq.add_snapshot(:donkey, '/giraffe.txt') + Nanoc::Int::ActionSequence.build(other_item_rep) do |b| + b.add_filter(:erb, {}) + b.add_snapshot(:donkey, '/giraffe.txt') end end @@ -296,9 +296,9 @@ context 'path changed' do let(:new_action_sequence_for_other_item_rep) do - Nanoc::Int::ActionSequence.new(other_item_rep).tap do |seq| - seq.add_filter(:erb, {}) - seq.add_snapshot(:donkey, '/giraffe.txt') + Nanoc::Int::ActionSequence.build(other_item_rep) do |b| + b.add_filter(:erb, {}) + b.add_snapshot(:donkey, '/giraffe.txt') end end @@ -329,9 +329,9 @@ context 'path changed' do let(:new_action_sequence_for_other_item_rep) do - Nanoc::Int::ActionSequence.new(other_item_rep).tap do |seq| - seq.add_filter(:erb, {}) - seq.add_snapshot(:donkey, '/giraffe.txt') + Nanoc::Int::ActionSequence.build(other_item_rep) do |b| + b.add_filter(:erb, {}) + b.add_snapshot(:donkey, '/giraffe.txt') end end @@ -356,9 +356,9 @@ context 'path changed' do let(:new_action_sequence_for_other_item_rep) do - Nanoc::Int::ActionSequence.new(other_item_rep).tap do |seq| - seq.add_filter(:erb, {}) - seq.add_snapshot(:donkey, '/giraffe.txt') + Nanoc::Int::ActionSequence.build(other_item_rep) do |b| + b.add_filter(:erb, {}) + b.add_snapshot(:donkey, '/giraffe.txt') end end @@ -389,9 +389,9 @@ context 'rules changed' do let(:new_action_sequence_for_other_item_rep) do - Nanoc::Int::ActionSequence.new(other_item_rep).tap do |seq| - seq.add_filter(:erb, {}) - seq.add_filter(:donkey, {}) + Nanoc::Int::ActionSequence.build(other_item_rep) do |b| + b.add_filter(:erb, {}) + b.add_filter(:donkey, {}) end end diff --git a/spec/nanoc/base/services/outdatedness_rules_spec.rb b/spec/nanoc/base/services/outdatedness_rules_spec.rb index 49739dc489..68b8aa41ef 100644 --- a/spec/nanoc/base/services/outdatedness_rules_spec.rb +++ b/spec/nanoc/base/services/outdatedness_rules_spec.rb @@ -305,8 +305,8 @@ let(:rule_class) { Nanoc::Int::OutdatednessRules::RulesModified } let(:old_mem) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |mem| - mem.add_filter(:erb, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_filter(:erb, {}) end end @@ -323,9 +323,9 @@ context 'memory is different' do let(:new_mem) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |mem| - mem.add_filter(:erb, {}) - mem.add_filter(:donkey, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_filter(:erb, {}) + b.add_filter(:donkey, {}) end end @@ -463,9 +463,9 @@ context 'unknown filter' do let(:mem) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |mem| - mem.add_snapshot(:donkey, '/foo.md') - mem.add_filter(:asdf, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_snapshot(:donkey, '/foo.md') + b.add_filter(:asdf, {}) end end @@ -474,9 +474,9 @@ context 'known filter, not always outdated' do let(:mem) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |mem| - mem.add_snapshot(:donkey, '/foo.md') - mem.add_filter(:erb, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_snapshot(:donkey, '/foo.md') + b.add_filter(:erb, {}) end end @@ -485,9 +485,9 @@ context 'known filter, always outdated' do let(:mem) do - Nanoc::Int::ActionSequence.new(item_rep).tap do |mem| - mem.add_snapshot(:donkey, '/foo.md') - mem.add_filter(:xsl, {}) + Nanoc::Int::ActionSequence.build(item_rep) do |b| + b.add_snapshot(:donkey, '/foo.md') + b.add_filter(:xsl, {}) end end diff --git a/spec/nanoc/rule_dsl/action_sequence_calculator_spec.rb b/spec/nanoc/rule_dsl/action_sequence_calculator_spec.rb index 740dd04ffe..8797a3da64 100644 --- a/spec/nanoc/rule_dsl/action_sequence_calculator_spec.rb +++ b/spec/nanoc/rule_dsl/action_sequence_calculator_spec.rb @@ -175,21 +175,23 @@ describe '#compact_snapshots' do subject { action_sequence_calculator.compact_snapshots(action_sequence) } - let(:action_sequence) { Nanoc::Int::ActionSequence.new(rep) } - let(:rep) { double(:rep) } - - before do - action_sequence.add_snapshot(:a1, nil) - action_sequence.add_snapshot(:a2, '/a2.md') - action_sequence.add_snapshot(:a3, nil) - action_sequence.add_filter(:erb, awesomeness: 'high') - action_sequence.add_snapshot(:b1, '/b1.md') - action_sequence.add_snapshot(:b2, nil) - action_sequence.add_snapshot(:b3, '/b3.md') - action_sequence.add_filter(:erb, awesomeness: 'high') - action_sequence.add_snapshot(:c, nil) + let(:action_sequence) do + Nanoc::Int::ActionSequence.build(rep) do |b| + b.add_snapshot(:a1, nil) + b.add_snapshot(:a2, '/a2.md') + b.add_snapshot(:a3, nil) + b.add_filter(:erb, awesomeness: 'high') + b.add_snapshot(:b1, '/b1.md') + b.add_snapshot(:b2, nil) + b.add_snapshot(:b3, '/b3.md') + b.add_filter(:erb, awesomeness: 'high') + b.add_snapshot(:c, nil) + end end + let(:item) { Nanoc::Int::Item.new('asdf', {}, '/foo.md') } + let(:rep) { Nanoc::Int::ItemRep.new(item, :default) } + example do expect(subject[0]).to be_a(Nanoc::Int::ProcessingActions::Snapshot) expect(subject[0].snapshot_names).to eql(%i[a1 a2 a3]) From 408172d945b6d6c58479a959a2f6a66c840b5980 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 14:05:04 +0200 Subject: [PATCH 6/7] Make ActionSequence immutable --- lib/nanoc/base/entities/action_sequence.rb | 30 ------------------- .../base/services/action_sequence_builder.rb | 26 +++++++++++----- lib/nanoc/rule_dsl/recording_executor.rb | 1 - 3 files changed, 19 insertions(+), 38 deletions(-) diff --git a/lib/nanoc/base/entities/action_sequence.rb b/lib/nanoc/base/entities/action_sequence.rb index 39b319d97d..e52c7e9d7f 100644 --- a/lib/nanoc/base/entities/action_sequence.rb +++ b/lib/nanoc/base/entities/action_sequence.rb @@ -27,25 +27,6 @@ def [](idx) @actions[idx] end - contract Symbol, Hash => self - def add_filter(filter_name, params) - @actions << Nanoc::Int::ProcessingActions::Filter.new(filter_name, params) - self - end - - contract String, C::Maybe[Hash] => self - def add_layout(layout_identifier, params) - @actions << Nanoc::Int::ProcessingActions::Layout.new(layout_identifier, params) - self - end - - contract Symbol, C::Maybe[String] => self - def add_snapshot(snapshot_name, path) - will_add_snapshot(snapshot_name) - @actions << Nanoc::Int::ProcessingActions::Snapshot.new([snapshot_name], path ? [path] : []) - self - end - contract C::None => C::ArrayOf[Nanoc::Int::ProcessingAction] def snapshot_actions @actions.select { |a| a.is_a?(Nanoc::Int::ProcessingActions::Snapshot) } @@ -92,16 +73,5 @@ def snapshots_defs snapshot_defs end - - private - - def will_add_snapshot(name) - @_snapshot_names ||= Set.new - if @_snapshot_names.include?(name) - raise Nanoc::Int::Errors::CannotCreateMultipleSnapshotsWithSameName.new(@item_rep, name) - else - @_snapshot_names << name - end - end end end diff --git a/lib/nanoc/base/services/action_sequence_builder.rb b/lib/nanoc/base/services/action_sequence_builder.rb index 9794934dc1..25a6fe67c8 100644 --- a/lib/nanoc/base/services/action_sequence_builder.rb +++ b/lib/nanoc/base/services/action_sequence_builder.rb @@ -2,32 +2,44 @@ module Nanoc::Int class ActionSequenceBuilder include Nanoc::Int::ContractsSupport - def initialize(rep) - @rep = rep - @action_sequence = Nanoc::Int::ActionSequence.new(rep) + def initialize(item_rep) + @item_rep = item_rep + @actions = [] end contract Symbol, Hash => self def add_filter(filter_name, params) - @action_sequence.add_filter(filter_name, params) + @actions << Nanoc::Int::ProcessingActions::Filter.new(filter_name, params) self end contract String, C::Maybe[Hash] => self def add_layout(layout_identifier, params) - @action_sequence.add_layout(layout_identifier, params) + @actions << Nanoc::Int::ProcessingActions::Layout.new(layout_identifier, params) self end contract Symbol, C::Maybe[String] => self def add_snapshot(snapshot_name, path) - @action_sequence.add_snapshot(snapshot_name, path) + will_add_snapshot(snapshot_name) + @actions << Nanoc::Int::ProcessingActions::Snapshot.new([snapshot_name], path ? [path] : []) self end contract C::None => Nanoc::Int::ActionSequence def action_sequence - @action_sequence + Nanoc::Int::ActionSequence.new(@item_rep, actions: @actions) + end + + private + + def will_add_snapshot(name) + @_snapshot_names ||= Set.new + if @_snapshot_names.include?(name) + raise Nanoc::Int::Errors::CannotCreateMultipleSnapshotsWithSameName.new(@item_rep, name) + else + @_snapshot_names << name + end end end end diff --git a/lib/nanoc/rule_dsl/recording_executor.rb b/lib/nanoc/rule_dsl/recording_executor.rb index 24b8503180..36c8f696a1 100644 --- a/lib/nanoc/rule_dsl/recording_executor.rb +++ b/lib/nanoc/rule_dsl/recording_executor.rb @@ -5,7 +5,6 @@ class RecordingExecutor contract Nanoc::Int::ItemRep => C::Any def initialize(rep) - @action_sequence = Nanoc::Int::ActionSequence.new(rep) @action_sequence_builder = Nanoc::Int::ActionSequenceBuilder.new(rep) @any_layouts = false From 28dee7c7b2ca73ac6eff42622101f029f4f33539 Mon Sep 17 00:00:00 2001 From: Denis Defreyne Date: Sun, 16 Apr 2017 14:05:59 +0200 Subject: [PATCH 7/7] Memoize ActionSequence#serialize --- lib/nanoc/base/entities/action_sequence.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/nanoc/base/entities/action_sequence.rb b/lib/nanoc/base/entities/action_sequence.rb index e52c7e9d7f..30d6f4022f 100644 --- a/lib/nanoc/base/entities/action_sequence.rb +++ b/lib/nanoc/base/entities/action_sequence.rb @@ -2,6 +2,7 @@ module Nanoc::Int class ActionSequence include Nanoc::Int::ContractsSupport include Enumerable + extend Nanoc::Int::Memoization attr_reader :item_rep attr_reader :actions @@ -38,7 +39,7 @@ def paths end # TODO: Add contract - def serialize + memoized def serialize to_a.map(&:serialize) end