class Nanoc::Int::Compiler

Responsible for compiling a site’s item representations.

The compilation process makes use of notifications (see {Nanoc::Int::NotificationCenter}) to track dependencies between items, layouts, etc. The following notifications are used:

@api private

Attributes

action_provider[R]

@api private

checksum_store[R]

@api private

compiled_content_cache[R]

@api private

dependency_store[R]

@api private

outdatedness_checker[R]

@api private

reps[R]

@api private

rule_memory_store[R]

@api private

site[R]

@api private

stack[R]

The compilation stack. When the compiler begins compiling a rep or a layout, it will be placed on the stack; when it is done compiling the rep or layout, it will be removed from the stack.

@return [Array] The compilation stack

Public Class Methods

new(site, compiled_content_cache:, checksum_store:, rule_memory_store:, action_provider:, dependency_store:, outdatedness_checker:, reps:) click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 72
def initialize(site, compiled_content_cache,, checksum_store,, rule_memory_store,, action_provider,, dependency_store,, outdatedness_checker,, reps))
  @site = site

  @compiled_content_cache = compiled_content_cache
  @checksum_store         = checksum_store
  @rule_memory_store      = rule_memory_store
  @dependency_store       = dependency_store
  @outdatedness_checker   = outdatedness_checker
  @reps                   = reps
  @action_provider        = action_provider

  @stack = []
end

Public Instance Methods

assigns_for(rep) click to toggle source

@param [Nanoc::Int::ItemRep] rep The item representation for which the

assigns should be fetched

@return [Hash] The assigns that should be used in the next filter/layout

operation

@api private

# File lib/nanoc/base/compilation/compiler.rb, line 150
def assigns_for(rep)
  content_or_filename_assigns =
    if rep.binary?
      { filename: rep.snapshot_contents[:last].filename }
    else
      { content: rep.snapshot_contents[:last].string }
    end

  view_context = create_view_context

  # TODO: Do not expose @site (necessary for captures store though…)
  content_or_filename_assigns.merge(
    item: Nanoc::ItemWithRepsView.new(rep.item, view_context),
    rep: Nanoc::ItemRepView.new(rep, view_context),
    item_rep: Nanoc::ItemRepView.new(rep, view_context),
    items: Nanoc::ItemCollectionWithRepsView.new(site.items, view_context),
    layouts: Nanoc::LayoutCollectionView.new(site.layouts, view_context),
    config: Nanoc::ConfigView.new(site.config, view_context),
    site: Nanoc::SiteView.new(site, view_context),
  )
end
build_reps() click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 137
def build_reps
  builder = Nanoc::Int::ItemRepBuilder.new(
    site, action_provider, @reps)
  builder.run
end
create_view_context() click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 172
def create_view_context
  Nanoc::ViewContext.new(reps: @reps, items: @site.items)
end
filter_name_and_args_for_layout(layout) click to toggle source

@api private

# File lib/nanoc/base/compilation/compiler.rb, line 177
def filter_name_and_args_for_layout(layout)
  mem = action_provider.memory_for(layout)
  if mem.nil? || mem.size != 1 || !mem[0].is_a?(Nanoc::Int::RuleMemoryActions::Filter)
    # FIXME: Provide a nicer error message
    raise Nanoc::Int::Errors::Generic, "No rule memory found for #{layout.identifier}"
  end
  [mem[0].filter_name, mem[0].params]
end
load_stores() click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 113
def load_stores
  stores.each(&:load)
end
run() click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 93
def run
  load_stores
  @site.freeze

  # Determine which reps need to be recompiled
  forget_dependencies_if_outdated

  @stack = []
  dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store)
  dependency_tracker.run do
    compile_reps
  end
  store
ensure
  Nanoc::Int::TempFilenameFactory.instance.cleanup(
    Nanoc::Filter::TMP_BINARY_ITEMS_DIR)
  Nanoc::Int::TempFilenameFactory.instance.cleanup(
    Nanoc::Int::ItemRepWriter::TMP_TEXT_ITEMS_DIR)
end
run_all() click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 86
def run_all
  @action_provider.preprocess(@site)
  build_reps
  run
  @action_provider.postprocess(@site, @reps)
end
store() click to toggle source

Store the modified helper data used for compiling the site.

@return [void]

# File lib/nanoc/base/compilation/compiler.rb, line 120
def store
  # Calculate rule memory
  (@reps.to_a + @site.layouts.to_a).each do |obj|
    rule_memory_store[obj] = action_provider.memory_for(obj).serialize
  end

  # Calculate checksums
  objects_to_checksum =
    site.items.to_a + site.layouts.to_a + site.code_snippets + [site.config]
  objects_to_checksum.each do |obj|
    checksum_store[obj] = Nanoc::Int::Checksummer.calc(obj)
  end

  # Store
  stores.each(&:store)
end

Private Instance Methods

can_reuse_content_for_rep?(rep) click to toggle source

@return [Boolean]

# File lib/nanoc/base/compilation/compiler.rb, line 244
def can_reuse_content_for_rep?(rep)
  !rep.item.forced_outdated? && !outdatedness_checker.outdated?(rep) && compiled_content_cache[rep]
end
compile_rep(rep) click to toggle source

Compiles the given item representation.

This method should not be called directly; please use {Nanoc::Int::Compiler#run} instead, and pass this item representation's item as its first argument.

@param [Nanoc::Int::ItemRep] rep The rep that is to be compiled

@return [void]

# File lib/nanoc/base/compilation/compiler.rb, line 218
def compile_rep(rep)
  Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
  Nanoc::Int::NotificationCenter.post(:processing_started,  rep)
  Nanoc::Int::NotificationCenter.post(:visit_started,       rep.item)

  if can_reuse_content_for_rep?(rep)
    Nanoc::Int::NotificationCenter.post(:cached_content_used, rep)
    rep.snapshot_contents = compiled_content_cache[rep]
  else
    recalculate_content_for_rep(rep)
  end

  rep.compiled = true
  compiled_content_cache[rep] = rep.snapshot_contents

  Nanoc::Int::NotificationCenter.post(:processing_ended,  rep)
  Nanoc::Int::NotificationCenter.post(:compilation_ended, rep)
rescue => e
  rep.forget_progress
  Nanoc::Int::NotificationCenter.post(:compilation_failed, rep, e)
  raise e
ensure
  Nanoc::Int::NotificationCenter.post(:visit_ended, rep.item)
end
compile_reps() click to toggle source
# File lib/nanoc/base/compilation/compiler.rb, line 188
def compile_reps
  # Listen to processing start/stop
  Nanoc::Int::NotificationCenter.on(:processing_started, self) { |obj| @stack.push(obj) }
  Nanoc::Int::NotificationCenter.on(:processing_ended, self) { |_obj| @stack.pop }

  # Assign snapshots
  @reps.each do |rep|
    rep.snapshot_defs = action_provider.snapshots_defs_for(rep)
  end

  # Find item reps to compile and compile them
  selector = Nanoc::Int::ItemRepSelector.new(@reps)
  selector.each do |rep|
    @stack = []
    compile_rep(rep)
  end
ensure
  Nanoc::Int::NotificationCenter.remove(:processing_started, self)
  Nanoc::Int::NotificationCenter.remove(:processing_ended,   self)
end
forget_dependencies_if_outdated() click to toggle source

Clears the list of dependencies for items that will be recompiled.

@return [void]

# File lib/nanoc/base/compilation/compiler.rb, line 269
def forget_dependencies_if_outdated
  @site.items.each do |i|
    if @reps[i].any? { |r| outdatedness_checker.outdated?(r) }
      @dependency_store.forget_dependencies_for(i)
    end
  end
end
recalculate_content_for_rep(rep) click to toggle source

@return [void]

# File lib/nanoc/base/compilation/compiler.rb, line 249
def recalculate_content_for_rep(rep)
  executor = Nanoc::Int::Executor.new(self)

  action_provider.memory_for(rep).each do |action|
    case action
    when Nanoc::Int::RuleMemoryActions::Filter
      executor.filter(rep, action.filter_name, action.params)
    when Nanoc::Int::RuleMemoryActions::Layout
      executor.layout(rep, action.layout_identifier, action.params)
    when Nanoc::Int::RuleMemoryActions::Snapshot
      executor.snapshot(rep, action.snapshot_name, final: action.final?, path: action.path)
    else
      raise "Internal inconsistency: unknown action #{action.inspect}"
    end
  end
end
stores() click to toggle source

Returns all stores that can load/store data that can be used for compilation.

# File lib/nanoc/base/compilation/compiler.rb, line 279
def stores
  [
    checksum_store,
    compiled_content_cache,
    @dependency_store,
    rule_memory_store,
  ]
end