Class: Lifer::Entry

Inherits:
Object
  • Object
show all
Defined in:
lib/lifer/entry.rb

Overview

An entry is a Lifer file that will be built into the output directory. There are more than one subclass of entry: Markdown entries are the most traditional, but HTML and text files are also very valid entries.

This class provides a baseline of the functionality that all entry subclasses should implement. It also provides the entry generator for all entry subclasses.

Direct Known Subclasses

HTML, Markdown, TXT

Defined Under Namespace

Classes: HTML, Markdown, TXT

Constant Summary collapse

DEFAULT_DATE =

We provide a default date for entries that have no date and entry types that otherwise could not have a date due to no real way of getting that metadata.

Time.new(1900, 01, 01, 0, 0, 0, "+00:00")
FILENAME_DATE_FORMAT =

If a filename contains a date, we should expect it to be in the following format.

/^(\d{4}-\d{1,2}-\d{1,2})-/
TAG_DELIMITER_REGEX =

If tags are represented in YAML frontmatter as a string, they’re split on commas and/or spaces.

/[,\s]+/
TRUNCATION_THRESHOLD =

We truncate anything that needs to be truncated (summaries, meta descriptions) at the following character count.

120

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file:, collection:) ⇒ Lifer::Entry

When a new entry is initialized we expect the file to already exist, and we expect to know which ‘Lifer::Collection` it belongs to.

Parameters:

  • file (String)

    An absolute path to a file.

  • collection (Lifer::Collection)

    A collection.



122
123
124
125
# File 'lib/lifer/entry.rb', line 122

def initialize(file:, collection:)
  @file = Pathname file
  @collection = collection
end

Class Attribute Details

.include_in_feedsObject

Returns the value of attribute include_in_feeds.



19
20
21
# File 'lib/lifer/entry.rb', line 19

def include_in_feeds
  @include_in_feeds
end

.input_extensionsObject

Returns the value of attribute input_extensions.



20
21
22
# File 'lib/lifer/entry.rb', line 20

def input_extensions
  @input_extensions
end

.output_extensionObject

Returns the value of attribute output_extension.



21
22
23
# File 'lib/lifer/entry.rb', line 21

def output_extension
  @output_extension
end

Instance Attribute Details

#collectionObject (readonly)

Returns the value of attribute collection.



52
53
54
# File 'lib/lifer/entry.rb', line 52

def collection
  @collection
end

#fileObject (readonly)

Returns the value of attribute file.



52
53
54
# File 'lib/lifer/entry.rb', line 52

def file
  @file
end

Class Method Details

.generate(file:, collection:) ⇒ Lifer::Entry

The entrypoint for generating entry objects. We should never end up with ‘Lifer::Entry` records: only subclasses.

Parameters:

  • file (String)

    The absolute filename of an entry file.

  • collection (Lifer::Collection)

    The collection for the entry.

Returns:



61
62
63
64
65
66
67
68
69
70
# File 'lib/lifer/entry.rb', line 61

def generate(file:, collection:)
  error!(file) unless File.exist?(file)

  if (new_entry = subclass_for(file)&.new(file:, collection:))
    Lifer.entry_manifest << new_entry
    new_entry.tags
  end

  new_entry
end

.manifestArray<Lifer::Entry>

Whenever an entry is generated it should be added to the entry manifest. This lets us get a list of all generated entries.

Returns:

  • (Array<Lifer::Entry>)

    A list of all entries that currently exist.



76
77
78
79
80
# File 'lib/lifer/entry.rb', line 76

def manifest
  return Lifer.entry_manifest if self == Lifer::Entry

  Lifer.entry_manifest.select { |entry| entry.class == self }
end

.supported?(filename, file_extensions = supported_file_extensions) ⇒ Boolean

Checks whether the given filename is supported entry type (using only its file extension).

Parameters:

  • filename (String)

    The absolute filename to an entry.

  • file_extensions (Array<String>) (defaults to: supported_file_extensions)

    An array of file extensions to check against.

Returns:

  • (Boolean)


89
90
91
# File 'lib/lifer/entry.rb', line 89

def supported?(filename, file_extensions= supported_file_extensions)
  file_extensions.any? { |ext| filename.end_with? ext }
end

Instance Method Details

#authorsArray<String>

Given the entry’s frontmatter, we should be able to get a list of authors. We always prefer authors (as opposed to a singular author) because it makes handling both cases easier in the long run.

The return value here is likely an author’s name. Whether that’s a full name, a first name, or a handle is up to the end user.

Returns:

  • (Array<String>)

    An array of authors’s names.



135
136
137
# File 'lib/lifer/entry.rb', line 135

def authors
  Array(frontmatter[:author] || frontmatter[:authors]).compact
end

#bodyString

This method returns the full text of the entry, only removing the frontmatter. It should not parse anything other than frontmatter.

Returns:

  • (String)

    The body of the entry.



143
144
145
146
147
# File 'lib/lifer/entry.rb', line 143

def body
  return full_text.strip unless frontmatter?

  full_text.gsub(Lifer::FRONTMATTER_REGEX, "").strip
end

#feedable?Boolean

Returns:

  • (Boolean)


149
150
151
152
153
154
155
156
# File 'lib/lifer/entry.rb', line 149

def feedable?
  if (setting = self.class.include_in_feeds).nil?
    raise NotImplementedError,
      I18n.t("entry.feedable_error", entry_class: self.class)
  end

  setting
end

#frontmatterHash

Frontmatter is a widely supported YAML metadata block found at the top of text–often Markdown–files. We attempt to parse all entries for frontmatter.

Returns:

  • (Hash)

    A hash representation of the entry frontmatter.



163
164
165
166
167
168
169
170
# File 'lib/lifer/entry.rb', line 163

def frontmatter
  return {} unless frontmatter?

  Lifer::Utilities.symbolize_keys(
    YAML.load full_text[Lifer::FRONTMATTER_REGEX, 1],
      permitted_classes: [Time]
  )
end

#full_textString

The full text of the entry.

Returns:

  • (String)


175
176
177
# File 'lib/lifer/entry.rb', line 175

def full_text
  @full_text ||= File.readlines(file).join if file
end

#pathString

The expected, absolute URI path to the entry. For example:

/index.html
/blog/my-trip-to-toronto.html

Returns:

  • (String)

    The absolute URI path to the entry.



210
# File 'lib/lifer/entry.rb', line 210

def path = permalink(host: "/")

Using the current Lifer configuration, we can calculate the expected permalink for the entry. For example:

https://example.com/index.html
https://example.com/blog/my-trip-to-toronto.html

This would be useful for indexes and feeds and so on.

Returns:

  • (String)

    A permalink to the current entry.



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/lifer/entry.rb', line 188

def permalink(host: Lifer.setting(:global, :host))
  cached_permalink_variable =
    "@entry_permalink_" + Digest::SHA1.hexdigest(host)

  instance_variable_get(cached_permalink_variable) ||
    instance_variable_set(
      cached_permalink_variable,
      File.join(
        host,
        Lifer::URIStrategy.find(collection.setting :uri_strategy)
          .new(root: Lifer.root)
          .output_file(self)
      )
    )
end

#published_atTime

The entry’s publication date. The published date can be inferred in a few ways. The priority is:

1. the frontmatter's `published_at` field
2. the frontmatter's `published` field
3. the frontamtter's `date` field
4. The date in the filename.

Since text files would only store dates as simple strings, it’s nice to attempt to convert those into Ruby date or datetime objects.

Returns:

  • (Time)

    A Ruby representation of the date and time provided by the entry frontmatter or filename.



225
226
227
228
229
230
231
# File 'lib/lifer/entry.rb', line 225

def published_at
  date_for frontmatter[:published_at],
    frontmatter[:published],
    frontmatter[:date],
    filename_date,
    missing_metadata_translation_key: "entry.no_published_at_metadata"
end

#summaryString

If given a summary in the frontmatter of the entry, we can use this to provide a summary.

Since subclasses may have more sophisticated access to the document, they may override this method with their own distinct implementations.

Returns:

  • (String)

    A summary of the entry.



240
241
242
# File 'lib/lifer/entry.rb', line 240

def summary
  return frontmatter[:summary] if frontmatter[:summary]
end

#tagsArray<Lifer::Tag>

Locates and returns all tags defined in the entry.

Returns:



247
248
249
250
# File 'lib/lifer/entry.rb', line 247

def tags
  @tags ||= candidate_tag_names
    .map { Lifer::Tag.build_or_update(name: _1, entries: [self]) }
end

#titleString

Returns the title of the entry. Every entry subclass must implement this method so that builders have access to some kind of title for each entry.

Returns:

  • (String)

Raises:

  • (NotImplementedError)


256
257
258
# File 'lib/lifer/entry.rb', line 256

def title
  raise NotImplementedError, I18n.t("shared.not_implemented_method")
end

#to_htmlObject

Raises:

  • (NotImplementedError)


260
261
262
# File 'lib/lifer/entry.rb', line 260

def to_html
  raise NotImplementedError, I18n.t("shared.not_implemented_method")
end

#updated_at(fallback: nil) ⇒ Time

The entry’s last updated date. In the frontmatter, the last updated date can be specified using one of two fields. In priority order:

1. the `updated_at` field
2. the `updated` field

The developer could set a fallback value as a fallback. For example, when building RSS feeds one might want the value of ‘#published_at` if there is no last updated date.

Parameters:

  • fallback (Time, String, NilClass) (defaults to: nil)

    Provide datetime data, a string that parses to a datetime object, or nil.

Returns:

  • (Time)

    A Ruby representation of the date and time provided by the entry frontmatter.



278
279
280
281
282
# File 'lib/lifer/entry.rb', line 278

def updated_at(fallback: nil)
  date_for frontmatter[:updated_at],
    frontmatter[:updated],
    default_date: fallback
end