Class: Moft::Post

Inherits:
Object
  • Object
show all
Includes:
Comparable, Convertible
Defined in:
lib/moft/post.rb

Direct Known Subclasses

Draft

Constant Summary collapse

MATCHER =

Valid post name regex.

/^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
ATTRIBUTES_FOR_LIQUID =

Attributes for Liquid templates

%w[
  title
  url
  date
  id
  categories
  next
  previous
  tags
  content
  excerpt
  path
]

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Convertible

#converter, #do_layout, #output_ext, #render_all_layouts, #render_liquid, #to_s, #write

Constructor Details

#initialize(site, source, dir, name) ⇒ Post

Initialize this Post instance.

site - The Site. base - The String path to the dir containing the post file. name - The String filename of the post file.

Returns the new Post.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/moft/post.rb', line 49

def initialize(site, source, dir, name)
  @site = site
  @dir = dir
  @base = self.containing_dir(source, dir)
  @name = name

  self.categories = dir.downcase.split('/').reject { |x| x.empty? }
  self.process(name)
  self.read_yaml(@base, name)

  if self.data.has_key?('date')
    self.date = Time.parse(self.data["date"].to_s)
  end

  self.published = self.published?

  self.populate_categories
  self.populate_tags
end

Class Attribute Details

.lsiObject

Returns the value of attribute lsi.



7
8
9
# File 'lib/moft/post.rb', line 7

def lsi
  @lsi
end

Instance Attribute Details

#categoriesObject

Returns the value of attribute categories.



38
39
40
# File 'lib/moft/post.rb', line 38

def categories
  @categories
end

#contentObject

Returns the value of attribute content.



37
38
39
# File 'lib/moft/post.rb', line 37

def content
  @content
end

#dataObject

Returns the value of attribute data.



37
38
39
# File 'lib/moft/post.rb', line 37

def data
  @data
end

#dateObject

Returns the value of attribute date.



38
39
40
# File 'lib/moft/post.rb', line 38

def date
  @date
end

#extObject

Returns the value of attribute ext.



37
38
39
# File 'lib/moft/post.rb', line 37

def ext
  @ext
end

#extracted_excerptObject

Returns the value of attribute extracted_excerpt.



37
38
39
# File 'lib/moft/post.rb', line 37

def extracted_excerpt
  @extracted_excerpt
end

#nameObject (readonly)

Returns the value of attribute name.



40
41
42
# File 'lib/moft/post.rb', line 40

def name
  @name
end

#outputObject

Returns the value of attribute output.



37
38
39
# File 'lib/moft/post.rb', line 37

def output
  @output
end

#publishedObject

Returns the value of attribute published.



38
39
40
# File 'lib/moft/post.rb', line 38

def published
  @published
end

#siteObject

Returns the value of attribute site.



36
37
38
# File 'lib/moft/post.rb', line 36

def site
  @site
end

#slugObject

Returns the value of attribute slug.



38
39
40
# File 'lib/moft/post.rb', line 38

def slug
  @slug
end

#tagsObject

Returns the value of attribute tags.



38
39
40
# File 'lib/moft/post.rb', line 38

def tags
  @tags
end

Class Method Details

.valid?(name) ⇒ Boolean

Post name validator. Post filenames must be like: 2008-11-05-my-awesome-post.textile

Returns true if valid, false if not.

Returns:

  • (Boolean)


32
33
34
# File 'lib/moft/post.rb', line 32

def self.valid?(name)
  name =~ MATCHER
end

Instance Method Details

#<=>(other) ⇒ Object

Compares Post objects. First compares the Post date. If the dates are equal, it compares the Post slugs.

other - The other Post we are comparing to.

Returns -1, 0, 1



139
140
141
142
143
144
145
# File 'lib/moft/post.rb', line 139

def <=>(other)
  cmp = self.date <=> other.date
  if 0 == cmp
   cmp = self.slug <=> other.slug
  end
  return cmp
end

#build_indexObject



258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/moft/post.rb', line 258

def build_index
  
#      self.class.lsi ||= begin
#        puts "Starting the classifier..."
#        lsi = Classifier::LSI.new(:auto_rebuild => false)
#        $stdout.print("  Populating LSI... "); $stdout.flush
#        posts.each { |x| $stdout.print("."); $stdout.flush; lsi.add_item(x) }
#        $stdout.print("\n  Rebuilding LSI index... ")
#        lsi.build_index
#        puts ""
#        lsi
#      end
end

#containing_dir(source, dir) ⇒ Object

Get the full path to the directory containing the post files



89
90
91
# File 'lib/moft/post.rb', line 89

def containing_dir(source, dir)
  return File.join(source, dir, '_posts')
end

#destination(dest) ⇒ Object

Obtain destination path.

dest - The String path to the destination dir.

Returns destination file path String.



293
294
295
296
297
298
# File 'lib/moft/post.rb', line 293

def destination(dest)
  # The url needs to be unescaped in order to preserve the correct filename
  path = File.join(dest, CGI.unescape(self.url))
  path = File.join(path, "index.html") if template[/\.html$/].nil?
  path
end

#dirObject

The generated directory into which the post will be placed upon generation. This is derived from the permalink or, if permalink is absent, set to the default date e.g. “/2008/11/05/” if the permalink style is :date, otherwise nothing.

Returns the String directory.



175
176
177
# File 'lib/moft/post.rb', line 175

def dir
  File.dirname(url)
end

#excerptObject

The post excerpt. This is either a custom excerpt set in YAML front matter or the result of extract_excerpt.

Returns excerpt string.



108
109
110
111
112
113
114
# File 'lib/moft/post.rb', line 108

def excerpt
  if self.data.has_key? 'excerpt'
    self.data['excerpt']
  else
    self.extracted_excerpt
  end
end

#idObject

The UID for this post (useful in feeds). e.g. /2008/11/05/my-awesome-post

Returns the String UID.



238
239
240
# File 'lib/moft/post.rb', line 238

def id
  File.join(self.dir, self.slug)
end

#inspectObject

Returns the shorthand String identifier of this Post.



311
312
313
# File 'lib/moft/post.rb', line 311

def inspect
  "<Post: #{self.id}>"
end

#nextObject



315
316
317
318
319
320
321
322
323
# File 'lib/moft/post.rb', line 315

def next
  pos = self.site.posts.index(self)

  if pos && pos < self.site.posts.length-1
    self.site.posts[pos+1]
  else
    nil
  end
end

#pathObject

Public: the path to the post relative to the site source,

from the YAML Front-Matter or from a combination of
the directory it's in, "_posts", and the name of the
post file

Returns the path to the file relative to the site source



129
130
131
# File 'lib/moft/post.rb', line 129

def path
  self.data['path'] || File.join(@dir, '_posts', @name).sub(/\A\//, '')
end

The full path and filename of the post. Defined in the YAML of the post body (optional).

Returns the String permalink.



183
184
185
# File 'lib/moft/post.rb', line 183

def permalink
  self.data && self.data['permalink']
end

#populate_categoriesObject



77
78
79
80
81
82
# File 'lib/moft/post.rb', line 77

def populate_categories
  if self.categories.empty?
    self.categories = self.data.pluralized_array('category', 'categories').map {|c| c.downcase}
  end
  self.categories.flatten!
end

#populate_tagsObject



84
85
86
# File 'lib/moft/post.rb', line 84

def populate_tags
  self.tags = self.data.pluralized_array("tag", "tags").flatten
end

#previousObject



325
326
327
328
329
330
331
332
# File 'lib/moft/post.rb', line 325

def previous
  pos = self.site.posts.index(self)
  if pos && pos > 0
    self.site.posts[pos-1]
  else
    nil
  end
end

#process(name) ⇒ Object

Extract information from the post filename.

name - The String filename of the post file.

Returns nothing.



152
153
154
155
156
157
158
159
# File 'lib/moft/post.rb', line 152

def process(name)
  m, cats, date, slug, ext = *name.match(MATCHER)
  self.date = Time.parse(date)
  self.slug = slug
  self.ext = ext
rescue ArgumentError
  raise FatalException.new("Post #{name} does not have a valid date.")
end

#published?Boolean

Returns:

  • (Boolean)


69
70
71
72
73
74
75
# File 'lib/moft/post.rb', line 69

def published?
  if self.data.has_key?('published') && self.data['published'] == false
    false
  else
    true
  end
end

#read_yaml(base, name) ⇒ Object

Read the YAML frontmatter.

base - The String path to the dir containing the file. name - The String filename of the file.

Returns nothing.



99
100
101
102
# File 'lib/moft/post.rb', line 99

def read_yaml(base, name)
  super(base, name)
  self.extracted_excerpt = self.extract_excerpt
end

Calculate related posts.

Returns an Array of related Posts.



245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/moft/post.rb', line 245

def related_posts(posts)
  return [] unless posts.size > 1

  if self.site.lsi
    build_index

    related = self.class.lsi.find_related(self.content, 11)
    related - [self]
  else
    (posts - [self])[0..9]
  end
end

#render(layouts, site_payload) ⇒ Object

Add any necessary layouts to this post.

layouts - A Hash of => “layout”. site_payload - The site payload hash.

Returns nothing.



278
279
280
281
282
283
284
285
286
# File 'lib/moft/post.rb', line 278

def render(layouts, site_payload)
  # construct payload
  payload = {
    "site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
    "page" => self.to_liquid
  }.deep_merge(site_payload)

  do_layout(payload, layouts)
end

#templateObject



187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/moft/post.rb', line 187

def template
  case self.site.permalink_style
  when :pretty
    "/:categories/:year/:month/:day/:title/"
  when :none
    "/:categories/:title.html"
  when :date
    "/:categories/:year/:month/:day/:title.html"
  when :ordinal
    "/:categories/:year/:y_day/:title.html"
  else
    self.site.permalink_style.to_s
  end
end

#titleObject

Public: the Post title, from the YAML Front-Matter or from the slug

Returns the post title



119
120
121
# File 'lib/moft/post.rb', line 119

def title
  self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' ')
end

#to_liquidObject

Convert this post into a Hash for use in Liquid templates.

Returns the representative Hash.



303
304
305
306
307
308
# File 'lib/moft/post.rb', line 303

def to_liquid
  further_data = Hash[ATTRIBUTES_FOR_LIQUID.map { |attribute|
    [attribute, send(attribute)]
  }]
  data.deep_merge(further_data)
end

#transformObject

Transform the contents and excerpt based on the content type.

Returns nothing.



164
165
166
167
# File 'lib/moft/post.rb', line 164

def transform
  super
  self.extracted_excerpt = converter.convert(self.extracted_excerpt)
end

#urlObject

The generated relative url of this post. e.g. /2008/11/05/my-awesome-post.html

Returns the String URL.



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
232
# File 'lib/moft/post.rb', line 206

def url
  return @url if @url

  url = if permalink
    permalink
  else
    {
      "year"       => date.strftime("%Y"),
      "month"      => date.strftime("%m"),
      "day"        => date.strftime("%d"),
      "title"      => CGI.escape(slug),
      "i_day"      => date.strftime("%d").to_i.to_s,
      "i_month"    => date.strftime("%m").to_i.to_s,
      "categories" => categories.map { |c| URI.escape(c.to_s) }.join('/'),
      "short_month" => date.strftime("%b"),
      "y_day"      => date.strftime("%j"),
      "output_ext" => self.output_ext
    }.inject(template) { |result, token|
      result.gsub(/:#{Regexp.escape token.first}/, token.last)
    }.gsub(/\/\//, "/")
  end

  # sanitize url
  @url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
  @url += "/" if url =~ /\/$/
  @url
end