Class: Serif::Site
- Inherits:
-
Object
- Object
- Serif::Site
- Defined in:
- lib/serif/site.rb
Instance Method Summary collapse
-
#archive_url_for_date(date) ⇒ Object
Returns the relative archive URL for the given date, using the value of config.archive_url_format.
-
#archives ⇒ Object
Returns a nested hash with the following structure:.
- #bypass?(filename) ⇒ Boolean
- #config ⇒ Object
-
#conflicts(content_to_check = nil) ⇒ Object
Returns a hash representing any conflicting URLs, in the form.
- #directory ⇒ Object
- #drafts ⇒ Object
- #generate ⇒ Object
-
#initialize(source_directory) ⇒ Site
constructor
A new instance of Site.
- #latest_update_time ⇒ Object
-
#posts ⇒ Object
Returns all of the site’s posts, in reverse chronological order by creation time.
-
#private_url(draft) ⇒ Object
Gives the URL absolute path to a private draft preview.
- #site_path(path) ⇒ Object
- #tmp_path(path) ⇒ Object
- #to_liquid ⇒ Object
Constructor Details
#initialize(source_directory) ⇒ Site
Returns a new instance of Site.
72 73 74 |
# File 'lib/serif/site.rb', line 72 def initialize(source_directory) @source_directory = source_directory end |
Instance Method Details
#archive_url_for_date(date) ⇒ Object
Returns the relative archive URL for the given date, using the value of config.archive_url_format
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/serif/site.rb', line 125 def archive_url_for_date(date) format = config.archive_url_format parts = { "year" => date.year.to_s, "month" => date.month.to_s.rjust(2, "0") } output = format parts.each do |placeholder, value| output = output.gsub(Regexp.quote(":" + placeholder), value) end output end |
#archives ⇒ Object
Returns a nested hash with the following structure:
{
:posts => [],
:years => [
{
:date => Date.new(2012),
:posts => [],
:months => [
{ :date => Date.new(2012, 12), :archive_url => "/archive/2012/12", :posts => [] },
{ :date => Date.new(2012, 11), :archive_url => "/archive/2012/11", :posts => [] },
# ...
]
},
# ...
]
}
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 201 202 203 204 |
# File 'lib/serif/site.rb', line 160 def archives h = {} h[:posts] = posts # group posts by Date instances for the first day of the year year_groups = posts.group_by { |post| Date.new(post.created.year) }.to_a # collect all elements as maps for the year start date and the posts in that year year_groups.map! do |year_start_date, posts_by_year| { :date => year_start_date, :posts => posts_by_year.sort_by { |post| post.created }.reverse } end year_groups.sort_by! { |year_hash| year_hash[:date] } year_groups.reverse! year_groups.each do |year_hash| year_posts = year_hash[:posts] # group the posts in the year by month month_groups = year_posts.group_by { |post| Date.new(post.created.year, post.created.month) }.to_a # collect the elements as maps for the month start date and the posts in that month month_groups.map! do |month_start_date, posts_by_month| { :date => month_start_date, :posts => posts_by_month.sort_by { |post| post.created }.reverse, :archive_url => archive_url_for_date(month_start_date) } end month_groups.sort_by! { |month_hash| month_hash[:date] } month_groups.reverse! # set the months for the current year year_hash[:months] = month_groups end h[:years] = year_groups # return the final hash h end |
#bypass?(filename) ⇒ Boolean
119 120 121 |
# File 'lib/serif/site.rb', line 119 def bypass?(filename) !%w[.html .xml].include?(File.extname(filename)) end |
#config ⇒ Object
90 91 92 |
# File 'lib/serif/site.rb', line 90 def config @config ||= Serif::Config.new(File.join(@source_directory, "_config.yml")) end |
#conflicts(content_to_check = nil) ⇒ Object
Returns a hash representing any conflicting URLs, in the form
{ "/url_1" => [e_1, e_2, ..., e_n], ... }
The elements e_i are the conflicting Post and Draft instances that share the URL “/url_1”.
Note that if n = 1 (that is, the array value is [e_1], containing a single element), then it is not included in the Hash, since it does not contribute to a conflict.
If there are no conflicts found, returns nil.
If an argument is specified, its #url value is compared against all post and draft URLs, and the value returned is either:
-
an array of post/draft instances that conflict, including the argument given; or,
-
nil if there is no conflict.
228 229 230 231 232 233 234 235 236 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 |
# File 'lib/serif/site.rb', line 228 def conflicts(content_to_check = nil) if content_to_check content = drafts + posts + [content_to_check] # In the event that the given argument is actually one of the # drafts + posts, we need to de-duplicate, otherwise our return # value will contain two of the same Draft/Post, which isn't # actually a conflict. # # So to do that, we can use the path on the filesystem. However, # we can't just rely on calling #path, because if content_to_check # doesn't have a #path value, it'll be nil and it's possible that # we might expand checking to multiple files/Drafts/Posts. # # Thus, if #path is nil, simply rely on object_id. # # FIXME: Replace this with a proper implementation of # ContentFile equality/hashing. content.uniq! { |e| e.path ? e.path : e.object_id } conflicts = content.select { |e| e.url == content_to_check.url } if conflicts.length <= 1 return nil else return conflicts end end conflicts = (drafts + posts).group_by { |e| e.url } conflicts.reject! { |k, v| v.length == 1 } if conflicts.empty? nil else conflicts end end |
#directory ⇒ Object
76 77 78 |
# File 'lib/serif/site.rb', line 76 def directory @source_directory end |
#generate ⇒ Object
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 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 391 392 393 |
# File 'lib/serif/site.rb', line 281 def generate FileUtils.cd(@source_directory) FileUtils.rm_rf("tmp/_site") FileUtils.mkdir_p("tmp/_site") files = Dir["**/*"].select { |f| f !~ /\A_/ && File.file?(f) } default_layout = Liquid::Template.parse(File.read("_layouts/default.html")) if conflicts raise PostConflictError, "Generating would cause a conflict." end # preprocess any drafts marked for autopublish, before grabbing the posts # to operate on. preprocess_autopublish_drafts # preprocess any posts that might have had an update flag set in the header preprocess_autoupdate_posts posts = self.posts files.each do |path| puts "Processing file: #{path}" dirname = File.dirname(path) filename = File.basename(path) FileUtils.mkdir_p(tmp_path(dirname)) if bypass?(filename) FileUtils.cp(path, tmp_path(path)) else File.open(tmp_path(path), "w") do |f| file = File.read(path) title = nil layout_option = :default if Redhead::String.has_headers?(file) file_with_headers = Redhead::String[file] title = file_with_headers.headers[:title] && file_with_headers.headers[:title].value layout_option = file_with_headers.headers[:layout] && file_with_headers.headers[:layout].value layout_option ||= :default # all good? use the headered string file = file_with_headers end if layout_option == "none" f.puts Liquid::Template.parse(file.to_s).render!("site" => self) else if layout_option == :default layout = default_layout else layout_file = File.join(self.directory, "_layouts", "#{layout_option}.html") layout = Liquid::Template.parse(File.read(layout_file)) end f.puts layout.render!("site" => self, "page" => { "title" => title }, "content" => Liquid::Template.parse(file.to_s).render!("site" => self)) end end end end # run through the posts + nil so we can keep |a, b| such that a hits every element # while iterating. posts.each.with_index do |post, i| # the posts are iterated over in reverse chrological order, and # next_post here is post published chronologically after than # the post in the iteration. # # if i == 0, we don't want posts.last, so return nil if i - 1 == -1 next_post = (i == 0 ? nil : posts[i - 1]) prev_post = posts[i + 1] puts "Processing post: #{post.path}" FileUtils.mkdir_p(tmp_path(File.dirname(post.url))) post_layout = default_layout if post.headers[:layout] post_layout = Liquid::Template.parse(File.read(File.join(self.directory, "_layouts", "#{post.headers[:layout]}.html"))) end File.open(tmp_path(post.url + ".html"), "w") do |f| # variables available in the post template post_template_variables = { "post" => post, "post_page" => true, "prev_post" => prev_post, "next_post" => next_post } f.puts post_layout.render!( "site" => self, "page" => { "title" => post.title }, "post_page" => true, "content" => Liquid::Template.parse(File.read("_templates/post.html")).render!(post_template_variables) ) end end generate_draft_previews(default_layout) generate_archives(default_layout) if Dir.exist?("_site") FileUtils.mv("_site", "/tmp/_site.#{Time.now.strftime("%Y-%m-%d-%H-%M-%S.%6N")}") end FileUtils.mv("tmp/_site", ".") && FileUtils.rm_rf("tmp/_site") FileUtils.rmdir("tmp") end |
#latest_update_time ⇒ Object
102 103 104 105 |
# File 'lib/serif/site.rb', line 102 def latest_update_time most_recent = posts.max_by { |p| p.updated } most_recent ? most_recent.updated : Time.now end |
#posts ⇒ Object
Returns all of the site’s posts, in reverse chronological order by creation time.
82 83 84 |
# File 'lib/serif/site.rb', line 82 def posts Post.all(self).sort_by { |entry| entry.created }.reverse end |
#private_url(draft) ⇒ Object
Gives the URL absolute path to a private draft preview.
If the draft has no such preview available, returns nil.
110 111 112 113 114 115 116 117 |
# File 'lib/serif/site.rb', line 110 def private_url(draft) private_draft_pattern = site_path("/drafts/#{draft.slug}/*") file = Dir[private_draft_pattern].first return nil unless file "/drafts/#{draft.slug}/#{File.basename(file, ".html")}" end |
#site_path(path) ⇒ Object
94 95 96 |
# File 'lib/serif/site.rb', line 94 def site_path(path) File.join("_site", path) end |
#tmp_path(path) ⇒ Object
98 99 100 |
# File 'lib/serif/site.rb', line 98 def tmp_path(path) File.join("tmp", site_path(path)) end |
#to_liquid ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 278 279 |
# File 'lib/serif/site.rb', line 267 def to_liquid @liquid_cache_store ||= TimeoutCache.new(1) cached_value = @liquid_cache_store[:liquid] return cached_value if cached_value @liquid_cache_store[:liquid] = { "posts" => posts, "latest_update_time" => latest_update_time, "archive" => self.class.stringify_keys(archives), "directory" => directory } end |