Class: Nanoc2::DataSources::Filesystem

Inherits:
Nanoc2::DataSource show all
Defined in:
lib/nanoc2/data_sources/filesystem.rb

Overview

The filesystem data source is the default data source for a new nanoc site. It stores all data as files on the hard disk.

None of the methods are documented in this file. See Nanoc2::DataSource for documentation on the overridden methods instead.

Pages

The filesystem data source stores its pages in nested directories. Each directory represents a single page. The root directory is the ‘content’ directory.

Every directory has a content file and a meta file. The content file contains the actual page content, while the meta file contains the page’s metadata, formatted as YAML.

Both content files and meta files are named after its parent directory (i.e. page). For example, a page named ‘foo’ will have a directorynamed ‘foo’, with e.g. a ‘foo.markdown’ content file and a ‘foo.yaml’ meta file.

Content file extensions are not used for determining the filter that should be run; the meta file defines the list of filters. The meta file extension must always be ‘yaml’, though.

Content files can also have the ‘index’ basename. Similarly, meta files can have the ‘meta’ basename. For example, a parent directory named ‘foo’ can have an ‘index.txt’ content file and a ‘meta.yaml’ meta file. This is to preserve backward compatibility.

Page defaults

The page defaults are loaded from a YAML-formatted file named ‘page_defaults.yaml’ at the top level of the nanoc site directory. For backward compatibility, the file can also be named ‘meta.yaml’.

Assets

Assets are stored in the ‘assets’ directory (surprise!). The structure is very similar to the structure of the ‘content’ directory, so see the Pages section for details on how this directory is structured.

Asset defaults

The asset defaults are stored similar to the way page defaults are stored, except that the asset defaults file is named ‘asset_defaults.yaml’ instead.

Layouts

Layouts are stored as directories in the ‘layouts’ directory. Each layout contains a content file and a meta file. The content file contain the actual layout, and the meta file describes how the page should be handled (contains the filter that should be used).

For backward compatibility, a layout can also be a single file in the ‘layouts’ directory. Such a layout cannot have any metadata; the filter used for this layout is determined from the file extension.

Templates

Templates are located in the ‘templates’ directroy. Every template is a directory consisting of a content file and a meta file, both named after the template. This is very similar to the way pages are stored, except that templates cannot be nested.

Code

Code is stored in ‘.rb’ files in the ‘lib’ directory. Code can reside in sub-directories.

Constant Summary collapse

PAGE_DEFAULTS_FILENAME =
'page_defaults.yaml'
PAGE_DEFAULTS_FILENAME_OLD =
'meta.yaml'
ASSET_DEFAULTS_FILENAME =
'asset_defaults.yaml'

Constants inherited from Plugin

Plugin::MAP

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Nanoc2::DataSource

#initialize, #loading

Methods inherited from Plugin

identifier, identifiers, named, register

Constructor Details

This class inherits a constructor from Nanoc2::DataSource

Instance Attribute Details

#vcsObject

VCSes ##########



84
85
86
# File 'lib/nanoc2/data_sources/filesystem.rb', line 84

def vcs
  @vcs
end

Instance Method Details

#asset_defaultsObject

Asset Defaults ##########



327
328
329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/nanoc2/data_sources/filesystem.rb', line 327

def asset_defaults # :nodoc:
  if File.file?(ASSET_DEFAULTS_FILENAME)
    # Get attributes
    attributes = YAML.load_file(ASSET_DEFAULTS_FILENAME) || {}

    # Get mtime
    mtime = File.stat(ASSET_DEFAULTS_FILENAME).mtime

    # Build asset defaults
    Nanoc2::AssetDefaults.new(attributes, mtime)
  else
    Nanoc2::AssetDefaults.new({})
  end
end

#assetsObject

Assets ##########



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/nanoc2/data_sources/filesystem.rb', line 212

def assets # :nodoc:
  meta_filenames('assets').map do |meta_filename|
    # Read metadata
    meta = YAML.load_file(meta_filename) || {}

    # Get content file
    content_filename = content_filename_for_dir(File.dirname(meta_filename))
    content_file = Nanoc2::Extra::FileProxy.new(content_filename)

    # Get attributes
    attributes = { 'extension' => File.extname(content_filename)[1..-1] }.merge(meta)

    # Get path
    path = meta_filename.sub(/^assets/, '').sub(/[^\/]+\.yaml$/, '')

    # Get modification times
    meta_mtime    = File.stat(meta_filename).mtime
    content_mtime = File.stat(content_filename).mtime
    mtime         = meta_mtime > content_mtime ? meta_mtime : content_mtime

    # Create asset object
    Nanoc2::Asset.new(content_file, attributes, path, mtime)
  end
end

#codeObject

Code ##########



536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'lib/nanoc2/data_sources/filesystem.rb', line 536

def code # :nodoc:
  # Get files
  filenames = Dir['lib/**/*.rb'].sort

  # Get data
  data = filenames.map { |filename| File.read(filename) + "\n" }.join('')

  # Get modification time
  mtimes = filenames.map { |filename| File.stat(filename).mtime }
  mtime = mtimes.inject { |memo, mtime| memo > mtime ? mtime : memo }

  # Build code
  Nanoc2::Code.new(data, mtime)
end

#delete_asset(asset) ⇒ Object

:nodoc:



282
283
284
# File 'lib/nanoc2/data_sources/filesystem.rb', line 282

def delete_asset(asset) # :nodoc:
  # TODO implement
end

#delete_layout(layout) ⇒ Object

:nodoc:



460
461
462
# File 'lib/nanoc2/data_sources/filesystem.rb', line 460

def delete_layout(layout) # :nodoc:
  # TODO implement
end

#delete_page(page) ⇒ Object

:nodoc:



206
207
208
# File 'lib/nanoc2/data_sources/filesystem.rb', line 206

def delete_page(page) # :nodoc:
  # TODO implement
end

#delete_template(template) ⇒ Object

:nodoc:



530
531
532
# File 'lib/nanoc2/data_sources/filesystem.rb', line 530

def delete_template(template) # :nodoc:
  # TODO implement
end

#destroyObject

:nodoc:



106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/nanoc2/data_sources/filesystem.rb', line 106

def destroy # :nodoc:
  # Remove files
  vcs.remove(ASSET_DEFAULTS_FILENAME)    if File.file?(ASSET_DEFAULTS_FILENAME)
  vcs.remove(PAGE_DEFAULTS_FILENAME)     if File.file?(PAGE_DEFAULTS_FILENAME)
  vcs.remove(PAGE_DEFAULTS_FILENAME_OLD) if File.file?(PAGE_DEFAULTS_FILENAME_OLD)

  # Remove directories
  vcs.remove('assets')
  vcs.remove('content')
  vcs.remove('templates')
  vcs.remove('layouts')
  vcs.remove('lib')
end

#downObject

:nodoc:



95
96
# File 'lib/nanoc2/data_sources/filesystem.rb', line 95

def down # :nodoc:
end

#layoutsObject

Layouts ##########



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
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/nanoc2/data_sources/filesystem.rb', line 363

def layouts # :nodoc:
  # Determine what layout directory structure is being used
  is_old_school = (Dir['layouts/*'].select { |f| File.file?(f) }.size > 0)

  if is_old_school
    # Warn about deprecation
    warn(
      'DEPRECATION WARNING: nanoc 2.1 changes the way layouts are ' +
      'stored. Future versions will not support these outdated sites. ' +
      'To update your site, issue \'nanoc update\'.'
    )

    Dir[File.join('layouts', '*')].reject { |f| f =~ /~$/ }.map do |filename|
      # Get content
      content = File.read(filename)

      # Get attributes
      attributes = { :extension => File.extname(filename)}

      # Get path
      path = File.basename(filename, attributes[:extension])

      # Get modification time
      mtime = File.stat(filename).mtime

      # Create layout object
      Nanoc2::Layout.new(content, attributes, path, mtime)
    end
  else
    meta_filenames('layouts').map do |meta_filename|
      # Get content
      content_filename  = content_filename_for_dir(File.dirname(meta_filename))
      content           = File.read(content_filename)

      # Get attributes
      attributes = YAML.load_file(meta_filename) || {}

      # Get path
      path = meta_filename.sub(/^layouts\//, '').sub(/\/[^\/]+\.yaml$/, '')

      # Get modification times
      meta_mtime    = File.stat(meta_filename).mtime
      content_mtime = File.stat(content_filename).mtime
      mtime         = meta_mtime > content_mtime ? meta_mtime : content_mtime

      # Create layout object
      Nanoc2::Layout.new(content, attributes, path, mtime)
    end
  end
end

#move_asset(asset, new_path) ⇒ Object

:nodoc:



278
279
280
# File 'lib/nanoc2/data_sources/filesystem.rb', line 278

def move_asset(asset, new_path) # :nodoc:
  # TODO implement
end

#move_layout(layout, new_path) ⇒ Object

:nodoc:



456
457
458
# File 'lib/nanoc2/data_sources/filesystem.rb', line 456

def move_layout(layout, new_path) # :nodoc:
  # TODO implement
end

#move_page(page, new_path) ⇒ Object

:nodoc:



202
203
204
# File 'lib/nanoc2/data_sources/filesystem.rb', line 202

def move_page(page, new_path) # :nodoc:
  # TODO implement
end

#move_template(template, new_name) ⇒ Object

:nodoc:



526
527
528
# File 'lib/nanoc2/data_sources/filesystem.rb', line 526

def move_template(template, new_name) # :nodoc:
  # TODO implement
end

#page_defaultsObject

Page Defaults ##########



288
289
290
291
292
293
294
295
296
297
298
# File 'lib/nanoc2/data_sources/filesystem.rb', line 288

def page_defaults # :nodoc:
  # Get attributes
  filename = File.file?(PAGE_DEFAULTS_FILENAME) ? PAGE_DEFAULTS_FILENAME : PAGE_DEFAULTS_FILENAME_OLD
  attributes = YAML.load_file(filename) || {}

  # Get mtime
  mtime = File.stat(filename).mtime

  # Build page defaults
  Nanoc2::PageDefaults.new(attributes, mtime)
end

#pagesObject

Pages ##########



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
# File 'lib/nanoc2/data_sources/filesystem.rb', line 128

def pages # :nodoc:
  meta_filenames('content').map do |meta_filename|
    # Read metadata
    meta = YAML.load_file(meta_filename) || {}

    if meta['is_draft']
      # Skip drafts
      nil
    else
      # Get content
      content_filename = content_filename_for_dir(File.dirname(meta_filename))
      content = File.read(content_filename)

      # Get attributes
      attributes = meta.merge(:file => Nanoc2::Extra::FileProxy.new(content_filename))

      # Get path
      path = meta_filename.sub(/^content/, '').sub(/[^\/]+\.yaml$/, '')

      # Get modification times
      meta_mtime    = File.stat(meta_filename).mtime
      content_mtime = File.stat(content_filename).mtime
      mtime         = meta_mtime > content_mtime ? meta_mtime : content_mtime

      # Create page object
      Nanoc2::Page.new(content, attributes, path, mtime)
    end
  end.compact
end

#save_asset(asset) ⇒ Object

:nodoc:



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/nanoc2/data_sources/filesystem.rb', line 237

def save_asset(asset) # :nodoc:
  # Determine meta file path
  last_component = asset.path.split('/')[-1]
  meta_filename  = 'assets' + asset.path + last_component + '.yaml'

  # Get existing path
  existing_path = nil
  existing_path = meta_filename_best  if File.file?(meta_filename_best)
  existing_path = meta_filename_worst if File.file?(meta_filename_worst)

  if meta_filename.nil?
    # Get filenames
    dir_path         = 'assets' + asset.path
    content_filename = 'assets' + asset.path + last_component + '.dat'

    # Notify
    Nanoc2::NotificationCenter.post(:file_created, meta_filename)
    Nanoc2::NotificationCenter.post(:file_created, content_filename)

    # Create directories if necessary
    FileUtils.mkdir_p(dir_path)
  else
    # Get filenames
    content_filename = content_filename_for_dir(File.dirname(meta_filename))

    # Notify
    Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
    Nanoc2::NotificationCenter.post(:file_updated, content_filename)
  end

  # Write files
  File.open(meta_filename,    'w') { |io| io.write(asset.attributes.to_split_yaml) }
  File.open(content_filename, 'w') { }

  # Add to working copy if possible
  if meta_filename.nil?
    vcs.add(meta_filename)
    vcs.add(content_filename)
  end
end

#save_asset_defaults(asset_defaults) ⇒ Object

:nodoc:



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/nanoc2/data_sources/filesystem.rb', line 342

def save_asset_defaults(asset_defaults) # :nodoc:
  # Notify
  if File.file?(ASSET_DEFAULTS_FILENAME)
    Nanoc2::NotificationCenter.post(:file_updated, ASSET_DEFAULTS_FILENAME)
    created  = false
  else
    Nanoc2::NotificationCenter.post(:file_created, ASSET_DEFAULTS_FILENAME)
    created  = true
  end

  # Write
  File.open(ASSET_DEFAULTS_FILENAME, 'w') do |io|
    io.write(asset_defaults.attributes.to_split_yaml)
  end

  # Add to working copy if possible
  vcs.add(ASSET_DEFAULTS_FILENAME) if created
end

#save_code(code) ⇒ Object

:nodoc:



551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/nanoc2/data_sources/filesystem.rb', line 551

def save_code(code) # :nodoc:
  # Check whether code existed
  existed = File.file?('lib/default.rb')

  # Remove all existing code files
  Dir['lib/**/*.rb'].each do |file|
    vcs.remove(file) unless file == 'lib/default.rb'
  end

  # Notify
  if existed
    Nanoc2::NotificationCenter.post(:file_updated, 'lib/default.rb')
  else
    Nanoc2::NotificationCenter.post(:file_created, 'lib/default.rb')
  end

  # Write new code
  File.open('lib/default.rb', 'w') do |io|
    io.write(code.data)
  end

  # Add to working copy if possible
  vcs.add('lib/default.rb') unless existed
end

#save_layout(layout) ⇒ Object

:nodoc:



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/nanoc2/data_sources/filesystem.rb', line 414

def save_layout(layout) # :nodoc:
  # Determine what layout directory structure is being used
  is_old_school = (Dir['layouts/*'].select { |f| File.file?(f) }.size > 0)
  error_outdated if is_old_school

  # Get paths
  last_component    = layout.path.split('/')[-1]
  dir_path          = 'layouts' + layout.path
  meta_filename     = dir_path + last_component + '.yaml'
  content_filename  = Dir[dir_path + last_component + '.*'][0]

  if File.file?(meta_filename)
    created = false

    # Notify
    Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
    Nanoc2::NotificationCenter.post(:file_updated, content_filename)
  else
    created = true

    # Create dir
    FileUtils.mkdir_p(dir_path)

    # Get content filename
    content_filename = dir_path + last_component + '.html'

    # Notify
    Nanoc2::NotificationCenter.post(:file_created, meta_filename)
    Nanoc2::NotificationCenter.post(:file_created, content_filename)
  end

  # Write files
  File.open(meta_filename,    'w') { |io| io.write(layout.attributes.to_split_yaml) }
  File.open(content_filename, 'w') { |io| io.write(layout.content) }

  # Add to working copy if possible
  if created
    vcs.add(meta_filename)
    vcs.add(content_filename)
  end
end

#save_page(page) ⇒ Object

:nodoc:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/nanoc2/data_sources/filesystem.rb', line 158

def save_page(page) # :nodoc:
  # Determine possible meta file paths
  last_component = page.path.split('/')[-1]
  meta_filename_worst = 'content' + page.path + 'index.yaml'
  meta_filename_best  = 'content' + page.path + (last_component || 'content') + '.yaml'

  # Get existing path
  existing_path = nil
  existing_path = meta_filename_best  if File.file?(meta_filename_best)
  existing_path = meta_filename_worst if File.file?(meta_filename_worst)

  if existing_path.nil?
    # Get filenames
    dir_path         = 'content' + page.path
    meta_filename    = meta_filename_best
    content_filename = 'content' + page.path + (last_component || 'content') + '.html'

    # Notify
    Nanoc2::NotificationCenter.post(:file_created, meta_filename)
    Nanoc2::NotificationCenter.post(:file_created, content_filename)

    # Create directories if necessary
    FileUtils.mkdir_p(dir_path)
  else
    # Get filenames
    meta_filename    = existing_path
    content_filename = content_filename_for_dir(File.dirname(existing_path))

    # Notify
    Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
    Nanoc2::NotificationCenter.post(:file_updated, content_filename)
  end

  # Write files
  File.open(meta_filename,    'w') { |io| io.write(page.attributes.to_split_yaml) }
  File.open(content_filename, 'w') { |io| io.write(page.content) }

  # Add to working copy if possible
  if existing_path.nil?
    vcs.add(meta_filename)
    vcs.add(content_filename)
  end
end

#save_page_defaults(page_defaults) ⇒ Object

:nodoc:



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/nanoc2/data_sources/filesystem.rb', line 300

def save_page_defaults(page_defaults) # :nodoc:
  # Notify
  if File.file?(PAGE_DEFAULTS_FILENAME)
    filename = PAGE_DEFAULTS_FILENAME
    created  = false
    Nanoc2::NotificationCenter.post(:file_updated, filename)
  elsif File.file?(PAGE_DEFAULTS_FILENAME_OLD)
    filename = PAGE_DEFAULTS_FILENAME_OLD
    created  = false
    Nanoc2::NotificationCenter.post(:file_updated, filename)
  else
    filename = PAGE_DEFAULTS_FILENAME
    created  = true
    Nanoc2::NotificationCenter.post(:file_created, filename)
  end

  # Write
  File.open(filename, 'w') do |io|
    io.write(page_defaults.attributes.to_split_yaml)
  end

  # Add to working copy if possible
  vcs.add(filename) if created
end

#save_template(template) ⇒ Object

:nodoc:



483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
# File 'lib/nanoc2/data_sources/filesystem.rb', line 483

def save_template(template) # :nodoc:
  # Determine possible meta file paths
  meta_filename_worst = 'templates/' + template.name + '/index.yaml'
  meta_filename_best  = 'templates/' + template.name + '/' + template.name + '.yaml'

  # Get existing path
  existing_path = nil
  existing_path = meta_filename_best  if File.file?(meta_filename_best)
  existing_path = meta_filename_worst if File.file?(meta_filename_worst)

  if existing_path.nil?
    # Get filenames
    dir_path         = 'templates/' + template.name
    meta_filename    = meta_filename_best
    content_filename = 'templates/' + template.name + '/' + template.name + '.html'

    # Notify
    Nanoc2::NotificationCenter.post(:file_created, meta_filename)
    Nanoc2::NotificationCenter.post(:file_created, content_filename)

    # Create directories if necessary
    FileUtils.mkdir_p(dir_path)
  else
    # Get filenames
    meta_filename    = existing_path
    content_filename = content_filename_for_dir(File.dirname(existing_path))

    # Notify
    Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
    Nanoc2::NotificationCenter.post(:file_updated, content_filename)
  end

  # Write files
  File.open(meta_filename,    'w') { |io| io.write(template.page_attributes.to_split_yaml) }
  File.open(content_filename, 'w') { |io| io.write(template.page_content) }

  # Add to working copy if possible
  if existing_path.nil?
    vcs.add(meta_filename)
    vcs.add(content_filename)
  end
end

#setupObject

:nodoc:



98
99
100
101
102
103
104
# File 'lib/nanoc2/data_sources/filesystem.rb', line 98

def setup # :nodoc:
  # Create directories
  %w( assets content templates layouts lib ).each do |dir|
    FileUtils.mkdir_p(dir)
    vcs.add(dir)
  end
end

#templatesObject

Templates ##########



466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/nanoc2/data_sources/filesystem.rb', line 466

def templates # :nodoc:
  meta_filenames('templates').map do |meta_filename|
    # Get name
    name = meta_filename.sub(/^templates\/(.*)\/[^\/]+\.yaml$/, '\1')

    # Get content
    content_filename  = content_filename_for_dir(File.dirname(meta_filename))
    content           = File.read(content_filename)

    # Get attributes
    attributes = YAML.load_file(meta_filename) || {}

    # Build template
    Nanoc2::Template.new(content, attributes, name)
  end
end

#upObject

Preparation ##########



92
93
# File 'lib/nanoc2/data_sources/filesystem.rb', line 92

def up # :nodoc:
end

#updateObject

:nodoc:



120
121
122
123
124
# File 'lib/nanoc2/data_sources/filesystem.rb', line 120

def update # :nodoc:
  update_page_defaults
  update_pages
  update_templates
end