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

Make ActionSequence immutable #1158

Merged
merged 7 commits into from Apr 16, 2017
Merged
44 changes: 8 additions & 36 deletions lib/nanoc/base/entities/action_sequence.rb
Expand Up @@ -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
Expand All @@ -11,6 +12,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
Expand All @@ -21,42 +28,18 @@ 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) }
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] }
end

# TODO: Add contract
def serialize
memoized def serialize
to_a.map(&:serialize)
end

Expand Down Expand Up @@ -91,16 +74,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
1 change: 1 addition & 0 deletions 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'
Expand Down
45 changes: 45 additions & 0 deletions lib/nanoc/base/services/action_sequence_builder.rb
@@ -0,0 +1,45 @@
module Nanoc::Int
class ActionSequenceBuilder
include Nanoc::Int::ContractsSupport

def initialize(item_rep)
@item_rep = item_rep
@actions = []
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 => Nanoc::Int::ActionSequence
def 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
21 changes: 7 additions & 14 deletions lib/nanoc/rule_dsl/action_sequence_calculator.rb
Expand Up @@ -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
Expand All @@ -68,17 +67,11 @@ 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)
copy_paths_from_routing_rules(compact_snapshots(executor.action_sequence), rep: rep)
end

# @param [Nanoc::Int::Layout] layout
Expand All @@ -91,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

Expand Down
47 changes: 40 additions & 7 deletions lib/nanoc/rule_dsl/recording_executor.rb
Expand Up @@ -3,32 +3,65 @@ module RuleDSL
class RecordingExecutor
include Nanoc::Int::ContractsSupport

def initialize(action_sequence)
@action_sequence = action_sequence
contract Nanoc::Int::ItemRep => C::Any
def initialize(rep)
@action_sequence_builder = Nanoc::Int::ActionSequenceBuilder.new(rep)

@any_layouts = false
@last_snapshot = false
@pre_snapshot = false
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 = {})
unless layout_identifier.is_a?(String)
raise ArgumentError.new('The layout passed to #layout must be a string')
end

unless @action_sequence.any_layouts?
@action_sequence.add_snapshot(:pre, nil)
unless any_layouts?
@pre_snapshot = true
@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
when :pre
@pre_snapshot = true
end
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
end

contract C::None => C::Bool
def last_snapshot?
@last_snapshot
end

contract C::None => C::Bool
def pre_snapshot?
@pre_snapshot
end
end
end
end