Class: Nanoc3::ItemRep

Inherits:
Object
  • Object
show all
Defined in:
lib/nanoc3/base/item_rep.rb

Overview

A single representation (rep) of an item (Item). An item can have multiple representations. A representation has its own output file. A single item can therefore have multiple output files, each run through a different set of filters with a different layout.

An item representation is observable. The following events will be notified:

  • :compilation_started
  • :compilation_ended
  • :filtering_started
  • :filtering_ended

The compilation-related events have one parameters (the item representation); the filtering-related events have two (the item representation, and a symbol containing the filter class name).

Constant Summary

OUTDATEDNESS_REASON_DESCRIPTIONS =

The descriptive strings for each outdatedness reason. This hash is used by the #outdatedness_reason method.

{
  :no_mtime => 'No file modification time is available.',
  :forced => 'All pages are recompiled because of a `--force` flag given to the compilation command.',
  :no_raw_path => 'The routing rules do not specify a path where this item should be written to, i.e. the item representation will never be written to the output directory.',
  :not_written => 'This item representation has not yet been written to the output directory (but it does have a path).',
  :source_modified => 'The source file of this item has been modified since the last time this item representation was compiled.',
  :layouts_outdated => 'The source of one or more layouts has been modified since the last time this item representation was compiled.',
  :code_outdated => 'The code snippets in the `lib/` directory have been modified since the last time this item representation was compiled.',
  :config_outdated => 'The site configuration has been modified since the last time this item representation was compiled.',
  :rules_outdated => 'The rules file has been modified since the last time this item representation was compiled.',
}

Instance Attribute Summary (collapse)

Instance Method Summary (collapse)

Constructor Details

- (ItemRep) initialize(item, name)

Creates a new item representation for the given item.

belong.

Parameters:

  • item (Nanoc3::Item)

    The item to which the new representation will

  • name (Symbol)

    The unique name for the new item representation.



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/nanoc3/base/item_rep.rb', line 89

def initialize(item, name)
  # Set primary attributes
  @item   = item
  @name   = name

  # Set binary
  @binary = @item.binary?

  # Initialize content and filenames
  initialize_content
  @old_content = nil

  # Reset flags
  @compiled       = false
  @modified       = false
  @created        = false
  @written        = false
  @force_outdated = false
end

Instance Attribute Details

- (Boolean) binary (readonly) Also known as: binary?

True if this rep is currently binary; false otherwise

Returns:

  • (Boolean)

    true if this rep is currently binary; false otherwise



48
49
50
# File 'lib/nanoc3/base/item_rep.rb', line 48

def binary
  @binary
end

- (Boolean) compiled Also known as: compiled?

during the current or last compilation session; false otherwise

Returns:

  • (Boolean)

    true if this representation has already been compiled



63
64
65
# File 'lib/nanoc3/base/item_rep.rb', line 63

def compiled
  @compiled
end

- (Boolean) created Also known as: created?

current or last compilation session; false otherwise

Returns:

  • (Boolean)

    true if this rep’s output file was created during the



58
59
60
# File 'lib/nanoc3/base/item_rep.rb', line 58

def created
  @created
end

- (Boolean) force_outdated

of the --force commandline option); false otherwise

Returns:

  • (Boolean)

    true if this rep is forced to be dirty (e.g. because



45
46
47
# File 'lib/nanoc3/base/item_rep.rb', line 45

def force_outdated
  @force_outdated
end

- (Nanoc3::Item) item (readonly)

The item to which this rep belongs

Returns:



38
39
40
# File 'lib/nanoc3/base/item_rep.rb', line 38

def item
  @item
end

- (Boolean) modified Also known as: modified?

last time it was compiled; false otherwise

Returns:

  • (Boolean)

    true if this rep’s output file has changed since the



53
54
55
# File 'lib/nanoc3/base/item_rep.rb', line 53

def modified
  @modified
end

- (Symbol) name (readonly)

The representation's unique name

Returns:

  • (Symbol)

    The representation's unique name



41
42
43
# File 'lib/nanoc3/base/item_rep.rb', line 41

def name
  @name
end

- (String) path

starts with a slash and it is relative to the output directory. It does not include the path to the output directory. It will not include the filename if the filename is an index filename.

Returns:

  • (String)

    The item rep's path, as used when being linked to. It



76
77
78
# File 'lib/nanoc3/base/item_rep.rb', line 76

def path
  @path
end

- (String) raw_path

working directory and includes the path to the output directory. It also includes the filename, even if it is an index filename.

Returns:

  • (String)

    The item rep's raw path. It is relative to the current



81
82
83
# File 'lib/nanoc3/base/item_rep.rb', line 81

def raw_path
  @raw_path
end

- (Boolean) written (readonly) Also known as: written?

been written during the current or last compilation session; false otherwise

Returns:

  • (Boolean)

    true if this representation’s compiled content has



69
70
71
# File 'lib/nanoc3/base/item_rep.rb', line 69

def written
  @written
end

Instance Method Details

- (Hash) assigns

the content.

Returns:

  • (Hash)

    The assignments that should be available when compiling



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/nanoc3/base/item_rep.rb', line 179

def assigns
  if self.binary?
    content_or_filename_assigns = { :filename => @filenames[:last] }
  else
    content_or_filename_assigns = { :content => @content[:last] }
  end

  content_or_filename_assigns.merge({
    :item       => self.item,
    :item_rep   => self,
    :items      => self.item.site.items,
    :layouts    => self.item.site.layouts,
    :config     => self.item.site.config,
    :site       => self.item.site
  })
end

- (String) compiled_content(params = {})

Returns the compiled content from a given snapshot.

fetch the compiled content. By default, the returned compiled content will be the content compiled right before the first layout call (if any).

default snapshot if no snapshot is specified)

Parameters:

  • params (Hash) (defaults to: {})

    a customizable set of options

Options Hash (params):

  • :snapshot (String)

    The name of the snapshot from which to

Returns:

  • (String)

    The compiled content at the given snapshot (or the

Raises:



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/nanoc3/base/item_rep.rb', line 205

def compiled_content(params={})
  # Notify
  Nanoc3::NotificationCenter.post(:visit_started, self.item)
  Nanoc3::NotificationCenter.post(:visit_ended,   self.item)

  # Debug
  puts "*** Attempting to fetch content for #{self.inspect}" if $DEBUG

  # Require compilation
  raise Nanoc3::Errors::UnmetDependency.new(self) unless compiled?

  # Get name of last pre-layout snapshot
  snapshot_name = params[:snapshot]
  if @content[:pre]
    snapshot_name ||= :pre
  else
    snapshot_name ||= :last
  end

  # Check presence of snapshot
  if @content[snapshot_name].nil?
    warn "WARNING: The “#{self.item.identifier}” item (rep “#{self.name}”) does not have the requested snapshot named #{snapshot_name.inspect}.\n\n* Make sure that you are requesting the correct snapshot.\n* It is not possible to request the compiled content of a binary item representation; if this item is marked as binary even though you believe it should be textual, you may need to add the extension of this item to the site configuration’s `text_extensions` array.".make_compatible_with_env
  end

  # Get content
  @content[snapshot_name]
end

- (Object) content_at_snapshot(snapshot = :pre)

Deprecated.

Use #compiled_content instead.



234
235
236
# File 'lib/nanoc3/base/item_rep.rb', line 234

def content_at_snapshot(snapshot=:pre)
  compiled_content(:snapshot => snapshot)
end

- (String?) diff

Creates and returns a diff between the compiled content before the current compilation session and the content compiled in the current compilation session.

content in diff(1) format, or nil if there is no previous compiled content

Returns:

  • (String, nil)

    The difference between the old and new compiled



399
400
401
402
403
404
405
406
# File 'lib/nanoc3/base/item_rep.rb', line 399

def diff
  if self.binary?
    nil
  else
     @diff_thread.join if @diff_thread
    @diff
  end
end

- (void) filter(filter_name, filter_args = {})

This method returns an undefined value.

Runs the item content through the given filter with the given arguments. This method will replace the content of the :last snapshot with the filtered content of the last snapshot.

This method is supposed to be called only in a compilation rule block (see CompilerDSL#compile).

representations' content through

the filter's #run method

Parameters:

  • filter_name (Symbol)

    The name of the filter to run the item

  • filter_args (Hash) (defaults to: {})

    The filter arguments that should be passed to

Raises:



261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/nanoc3/base/item_rep.rb', line 261

def filter(filter_name, filter_args={})
  # Get filter class
  klass = filter_named(filter_name)
  raise Nanoc3::Errors::UnknownFilter.new(filter_name) if klass.nil?

  # Check whether filter can be applied
  if klass.from_binary? && !self.binary?
    raise Nanoc3::Errors::CannotUseBinaryFilter.new(self, klass)
  elsif !klass.from_binary? && self.binary?
    raise Nanoc3::Errors::CannotUseTextualFilter.new(self, klass)
  end

  # Create filter
  filter = klass.new(assigns)

  # Run filter
  Nanoc3::NotificationCenter.post(:filtering_started, self, filter_name)
  source = self.binary? ? @filenames[:last] : @content[:last]
  result = filter.run(source, filter_args)
  if klass.to_binary?
    @filenames[:last] = filter.output_filename
  else
    @content[:last] = result
  end
  @binary = klass.to_binary?
  Nanoc3::NotificationCenter.post(:filtering_ended, self, filter_name)

  # Check whether file was written
  if self.binary? && !File.file?(filter.output_filename)
    raise RuntimeError,
      "The #{filter_name.inspect} filter did not write anything to the required output file, #{filter.output_filename}."
  end

  # Create snapshot
  snapshot(@content[:post] ? :post : :pre) unless self.binary?
end

- (void) forget_progress

This method returns an undefined value.

Resets the compilation progress for this item representation. This is necessary when an unmet dependency is detected during compilation. This method should probably not be called directly.



243
244
245
# File 'lib/nanoc3/base/item_rep.rb', line 243

def forget_progress
  initialize_content
end

- (Object) inspect



408
409
410
# File 'lib/nanoc3/base/item_rep.rb', line 408

def inspect
  "<#{self.class}:0x#{self.object_id.to_s(16)} name=#{self.name} binary=#{self.binary?} raw_path=#{self.raw_path} item.identifier=#{self.item.identifier}>"
end

- (void) layout(layout_identifier)

This method returns an undefined value.

Lays out the item using the given layout. This method will replace the content of the :last snapshot with the laid out content of the last snapshot.

This method is supposed to be called only in a compilation rule block (see CompilerDSL#compile).

should be laid out with

Parameters:

  • layout_identifier (String)

    The identifier of the layout the item

Raises:



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
# File 'lib/nanoc3/base/item_rep.rb', line 309

def layout(layout_identifier)
  # Check whether item can be laid out
  raise Nanoc3::Errors::CannotLayoutBinaryItem.new(self) if self.binary?

  # Create "pre" snapshot
  snapshot(:pre) unless @content[:pre]

  # Create filter
  layout = layout_with_identifier(layout_identifier)
  filter, filter_name, filter_args = filter_for_layout(layout)

  # Layout
  @item.site.compiler.stack.push(layout)
  Nanoc3::NotificationCenter.post(:filtering_started, self, filter_name)
  @content[:last] = filter.run(layout.raw_content, filter_args)
  Nanoc3::NotificationCenter.post(:filtering_ended,   self, filter_name)
  @item.site.compiler.stack.pop

  # Create "post" snapshot
  snapshot(:post)
end

- (Boolean) outdated?

must be regenerated, false otherwise

Returns:

  • (Boolean)

    true if this item rep's output file is outdated and



173
174
175
# File 'lib/nanoc3/base/item_rep.rb', line 173

def outdated?
  !outdatedness_reason.nil?
end

- (Hash?) outdatedness_reason

Calculates the reason why this item representation is outdated. The output will be a hash with a :type key, containing the reason why the item is outdated in the form of a symbol, and a :description key, containing a descriptive string that can be printed if necessary.

For documentation on the types that this method can return, check the OUTDATEDNESS<em>REASON</em>DESCRIPTIONS hash in this class.

Returns:

  • (Hash, nil)

    A hash containing the reason why this item rep is outdated, both in the form of a symbol and as a descriptive string, or nil if the item representation is not outdated.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/nanoc3/base/item_rep.rb', line 120

def outdatedness_reason
  # Get reason symbol
  reason = lambda do
    # Outdated if we don't know
    return :no_mtime if @item.mtime.nil?

    # Outdated if the dependency tracker says so
    return :forced if @force_outdated

    # Outdated if compiled file doesn't exist (yet)
    return :no_raw_path if self.raw_path.nil?
    return :not_written if !File.file?(self.raw_path)

    # Get compiled mtime
    compiled_mtime = File.stat(self.raw_path).mtime

    # Outdated if file too old
    return :source_modified if @item.mtime > compiled_mtime

    # Outdated if layouts outdated
    return :layouts_outdated if @item.site.layouts.any? do |l|
      l.mtime.nil? || l.mtime > compiled_mtime
    end

    # Outdated if code outdated
    return :code_outdated if @item.site.code_snippets.any? do |cs|
      cs.mtime.nil? || cs.mtime > compiled_mtime
    end

    # Outdated if config outdated
    return :config_outdated if @item.site.config_mtime.nil?
    return :config_outdated if @item.site.config_mtime > compiled_mtime

    # Outdated if rules outdated
    return :rules_outdated if @item.site.rules_mtime.nil?
    return :rules_outdated if @item.site.rules_mtime > compiled_mtime

    return nil
  end[]

  # Build reason symbol and description
  if reason.nil?
    nil
  else
    {
      :type        => reason,
      :description => OUTDATEDNESS_REASON_DESCRIPTIONS[reason]
    }
  end
end

- (void) snapshot(snapshot_name)

This method returns an undefined value.

Creates a snapshot of the current compiled item content.

Parameters:

  • snapshot_name (Symbol)

    The name of the snapshot to create



336
337
338
339
# File 'lib/nanoc3/base/item_rep.rb', line 336

def snapshot(snapshot_name)
  target = self.binary? ? @filenames : @content
  target[snapshot_name] = target[:last]
end

- (void) write

This method returns an undefined value.

Writes the item rep's compiled content to the rep's output file.

This method should not be called directly, even in a compilation block; the compiler is responsible for calling this method.



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/nanoc3/base/item_rep.rb', line 347

def write
  # Create parent directory
  FileUtils.mkdir_p(File.dirname(self.raw_path))

  # Check if file will be created
  @created = !File.file?(self.raw_path)

  if self.binary?
    # Calculate hash of old content
    if File.file?(self.raw_path)
      hash_old = hash_for_file(self.raw_path)
      size_old = File.size(self.raw_path)
    end
    size_new = File.size(@filenames[:last])
    hash_new = hash_for_file(@filenames[:last]) if size_old == size_new

    # Check if file was modified
    @modified = (size_old != size_new || hash_old != hash_new)

    # Copy
    if @modified
      FileUtils.cp(@filenames[:last], self.raw_path)
    end
    @written = true
  else
    # Remember old content
    if File.file?(self.raw_path)
      @old_content = File.read(self.raw_path)
    end

    # Write
    new_content = @content[:last]
    if @old_content != new_content
      File.open(self.raw_path, 'w') { |io| io.write(new_content) }
    end
    @written = true

    # Generate diff
    generate_diff

    # Check if file was modified
    @modified = File.read(self.raw_path) != @old_content
  end
end