Skip to content
This repository has been archived by the owner on Jul 17, 2018. It is now read-only.

Commit

Permalink
Merge pull request #49 from aprescott/update-now-header
Browse files Browse the repository at this point in the history
Posts should support "update: now" header
  • Loading branch information
aprescott committed Mar 18, 2013
2 parents 82b9b95 + b494e78 commit d9f6c89
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# next release

* The admin interface now supports bookmarks for quickly creating drafts. Selected text on the page will be turned into Markdown. (#50)
* Posts with a header of `update: now` will have their `Updated` timestamps updated automatically on the next generate. (#49)
* The 'markdown' filter now properly turns single quotes in Markdown into curly quotes. (#40)
* Add `archive_page` template flag set to true when processing archive pages. (#41)
* Make the `month` archive variable available to layouts as well as the archive template. (#41)
Expand Down
11 changes: 6 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ PATH
specs:
serif (0.3.3)
liquid (~> 2.4)
nokogiri (~> 1.5)
rack (~> 1.0)
redcarpet (~> 2.2)
redhead (~> 0.0.8)
Expand All @@ -17,14 +18,14 @@ GEM
diff-lcs (1.1.3)
liquid (2.5.0)
multi_json (1.6.1)
nokogiri (1.5.5)
nokogiri (1.5.6)
rack (1.5.2)
rack-protection (1.5.0)
rack
rake (0.9.6)
redcarpet (2.2.2)
redhead (0.0.8)
reverse_markdown (0.4.3)
reverse_markdown (0.4.4)
nokogiri
rouge (0.3.2)
thor
Expand All @@ -45,8 +46,8 @@ GEM
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
thor (0.17.0)
tilt (1.3.5)
timecop (0.5.9.2)
tilt (1.3.6)
timecop (0.6.1)
timeout_cache (0.0.2)

PLATFORMS
Expand All @@ -57,4 +58,4 @@ DEPENDENCIES
rspec (~> 2.5)
serif!
simplecov (~> 0.7)
timecop (~> 0.5.5)
timecop (~> 0.6.1)
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ Now visit <http://localhost:8000/> to view the site.
* [Basic usage](#basics)
* [Content and site structure](#content-and-site-structure)
* [Publishing drafts](#publishing-drafts)
* [Updating posts](#updating-posts)
* [Archive pages](#archive-pages)
* [Configuration](#configuration)
* [Deploying](#deploying)
* [Customising the admin interface](#customising-the-admin-interface)
* [Custom tags](#custom-tags)
* [Custom tags and filters](#custom-tags-and-filters)
* [Template variables](#template-variables)
* [Developing Serif](#developing-serif)
* [Changes and what's new](#changes-and-whats-new)
Expand Down Expand Up @@ -280,6 +281,26 @@ This is a draft that will be published now.

On the next site generation (`serif generate`) this draft will be automatically published, using the current time as the creation timestamp.

# Updating posts

When you update a draft, you need to remember to change the updated time. As luck would have it, Serif takes care of timestamps for you! Just use a header of `update: now` at the top of your post:

```
title: My blog post
Created: 2013-01-01T12:01:30+00:00
update: now
```

Now the next time the site is generated, the timestamp will be updated:

```
title: My blog post
Created: 2013-01-01T12:01:30+00:00
Updated: 2013-03-18T19:03:30+00:00
```

Admin users: this is all done for you.

# Archive pages

By default, archive pages are made available at `/archive/:year/month`, e.g., `/archive/2012/11`. Individual archive pages can be customised by editing the `_templates/archive_page.html` file, which is used for each month.
Expand Down
9 changes: 8 additions & 1 deletion lib/serif/content_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,18 @@ def inspect

def set_publish_time(time)
@source.headers[:created] = time.xmlschema
@cached_headers = nil
headers_changed!
end

def set_updated_time(time)
@source.headers[:updated] = time.xmlschema
headers_changed!
end

# Invalidates the cached headers entirely.
#
# Any methods which alter headers should call this.
def headers_changed!
@cached_headers = nil
end

Expand Down
6 changes: 2 additions & 4 deletions lib/serif/draft.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,16 @@ def publish!
@path = Post.from_slug(site, slug).path
end

# sets the autopublish flag to the given value.
#
# if the assigned value is truthy, the "publish" header
# is set to "now", otherwise the header is removed.
def autopublish=(value)
@autopublish = value

if value
@source.headers[:publish] = "now"
else
@source.headers.delete(:publish)
end

headers_changed!
end

# Checks the value of the "publish" header, and returns
Expand Down
30 changes: 30 additions & 0 deletions lib/serif/post.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@ def url
output
end

# if the assigned value is truthy, the "update" header
# is set to "now", otherwise the header is removed.
def autoupdate=(value)
if value
@source.headers[:update] = "now"
else
@source.headers.delete(:update)
end

headers_changed!
end

# returns true if the post has been marked as needing a
# new updated timestamp header.
#
# this is based on the presence of an "update: now" header.
def autoupdate?
update_header = headers[:update]
update_header && update_header.strip == "now"
end

# Updates the updated timestamp and saves the contents.
#
# If there is an "update" header (see autoupdate?), it is deleted.
def update!
@source.headers.delete(:update)
set_updated_time(Time.now)
save
end

def self.all(site)
files = Dir[File.join(site.directory, dirname, "*")].select { |f| File.file?(f) }.map { |f| File.expand_path(f) }
files.map { |f| new(site, f) }
Expand Down
12 changes: 12 additions & 0 deletions lib/serif/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ def generate
# 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|
Expand Down Expand Up @@ -346,6 +349,15 @@ def generate

private

def preprocess_autoupdate_posts
posts.each do |p|
if p.autoupdate?
puts "Auto-updating timestamp for: #{p.title} / #{p.slug}"
p.update!
end
end
end

# generates draft preview files for any unpublished drafts.
#
# uses the same template as live posts.
Expand Down
2 changes: 1 addition & 1 deletion serif.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ Gem::Specification.new do |s|
s.add_development_dependency("rake", "~> 0.9")
s.add_development_dependency("rspec", "~> 2.5")
s.add_development_dependency("simplecov", "~> 0.7")
s.add_development_dependency("timecop", "~> 0.5.5")
s.add_development_dependency("timecop", "~> 0.6.1")
end
14 changes: 14 additions & 0 deletions test/draft_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,20 @@

draft.delete!
end

it "carries its value through to #autopublish?" do
draft = D.new(@site)
draft.slug = "test-draft"
draft.title = "Some draft title"
draft.autopublish = false
draft.autopublish?.should be_false

draft.autopublish = true
draft.autopublish?.should be_true

draft.autopublish = false
draft.autopublish?.should be_false
end
end

describe "#autopublish?" do
Expand Down
89 changes: 89 additions & 0 deletions test/post_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@
@posts = subject.posts
end

around :each do |example|
begin
d = Serif::Draft.new(subject)
d.slug = "foo-bar-bar-temp"
d.title = "Testing title"
d.save("# some content")
d.publish!
@temporary_post = Serif::Post.from_slug(subject, d.slug)

example.run
ensure
FileUtils.rm(@temporary_post.path)
end
end

it "uses the config file's permalink value" do
@posts.all? { |p| p.url == "/test-blog/#{p.slug}" }.should be_true
end
Expand All @@ -18,4 +33,78 @@
@posts.all? { |p| p.inspect.should include(p.headers.inspect) }
end
end

describe "#autoupdate=" do
it "sets the 'update' header to 'now' if truthy assigned value" do
@temporary_post.autoupdate = true
@temporary_post.headers[:update].should == "now"
end

it "removes the 'update' header entirely if falsey assigned value" do
@temporary_post.autoupdate = false
@temporary_post.headers.key?(:update).should be_false
end

it "marks the post as autoupdate? == true" do
@temporary_post.autoupdate?.should be_false
@temporary_post.autoupdate = true
@temporary_post.autoupdate?.should be_true
end
end

describe "#autoupdate?" do
it "returns true if there is an update: now header" do
@temporary_post.stub(:headers) { { :update => "foo" } }
@temporary_post.autoupdate?.should be_false
@temporary_post.stub(:headers) { { :update => "now" } }
@temporary_post.autoupdate?.should be_true
end

it "is ignorant of whitespace in the update header value" do
@temporary_post.stub(:headers) { { :update => "now" } }
@temporary_post.autoupdate?.should be_true

(1..3).each do |left|
(1..3).each do |right|
@temporary_post.stub(:headers) { { :update => "#{" " * left}now#{" " * right}"} }
@temporary_post.autoupdate?.should be_true
end
end
end
end

describe "#update!" do
it "sets the updated header timestamp to the current time" do
old_update_time = @temporary_post.updated
t = Time.now + 50

Timecop.freeze(t) do
@temporary_post.update!
@temporary_post.updated.should_not == old_update_time
@temporary_post.updated.to_i.should == t.to_i
@temporary_post.headers[:updated].to_i.should == t.to_i
end
end

it "calls save and writes out the new timestamp value, without a publish: now header" do
@temporary_post.should_receive(:save).once.and_call_original

t = Time.now + 50
Timecop.freeze(t) do
@temporary_post.update!

file_content = Redhead::String[File.read(@temporary_post.path)]
Time.parse(file_content.headers[:updated].value).to_i.should == t.to_i
file_content.headers[:publish].should be_nil
end
end

it "marks the post as no longer auto-updating" do
@temporary_post.autoupdate?.should be_false
@temporary_post.autoupdate = true
@temporary_post.autoupdate?.should be_true
@temporary_post.update!
@temporary_post.autoupdate?.should be_false
end
end
end
28 changes: 28 additions & 0 deletions test/site_generation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,34 @@
Dir[File.join(testing_dir("_site/drafts/sample-draft"), "*.html")].size.should == 1
end

context "for posts with an update: now header" do
around :each do |example|
begin
d = Serif::Draft.new(subject)
d.slug = "post-to-be-auto-updated"
d.title = "Testing title"
d.save("# some content")
d.publish!

@temporary_post = Serif::Post.from_slug(subject, d.slug)
@temporary_post.autoupdate = true
@temporary_post.save

example.run
ensure
FileUtils.rm(@temporary_post.path)
end
end

it "sets the updated header to the current time" do
t = Time.now + 30
Timecop.freeze(t) do
capture_stdout { subject.generate }
Serif::Post.from_slug(subject, @temporary_post.slug).updated.to_i.should == t.to_i
end
end
end

context "for drafts with a publish: now header" do
before :all do
@time = Time.utc(2012, 12, 21, 15, 30, 00)
Expand Down

0 comments on commit d9f6c89

Please sign in to comment.