use IDs instead of slugs for posts

Drafts don’t have reliable slugs until they’re published so give them
UUIDs, and lookup posts by ID instead of slug.
This commit is contained in:
Sami Samhuri 2015-03-29 19:42:13 -07:00
parent d9731944c2
commit c905f5c414
3 changed files with 230 additions and 146 deletions

View file

@ -1,5 +1,6 @@
require 'fileutils' require 'fileutils'
require 'json' require 'json'
require 'securerandom'
require './web_title_finder' require './web_title_finder'
require './web_version_finder' require './web_version_finder'
@ -20,7 +21,7 @@ class HarpBlog
end end
class Post class Post
PERSISTENT_FIELDS = %w[author title date timestamp link url tags].map(&:to_sym) PERSISTENT_FIELDS = %w[id author title date timestamp link url tags].map(&:to_sym)
TRANSIENT_FIELDS = %w[time slug body draft].map(&:to_sym) TRANSIENT_FIELDS = %w[time slug body draft].map(&:to_sym)
FIELDS = PERSISTENT_FIELDS + TRANSIENT_FIELDS FIELDS = PERSISTENT_FIELDS + TRANSIENT_FIELDS
FIELDS.each { |f| attr_accessor f } FIELDS.each { |f| attr_accessor f }
@ -83,10 +84,19 @@ class HarpBlog
@timestamp = timestamp @timestamp = timestamp
end end
def id
@id ||=
if draft?
SecureRandom.uuid
else
slug
end
end
def url def url
@url ||= @url ||=
if draft? if draft?
"/posts/drafts/#{slug}" "/posts/drafts/#{id}"
else else
"/posts/#{time.year}/#{padded_month}/#{slug}" "/posts/#{time.year}/#{padded_month}/#{slug}"
end end
@ -94,7 +104,7 @@ class HarpBlog
def slug def slug
# TODO: be intelligent about unicode ... \p{Word} might help. negated char class with it? # TODO: be intelligent about unicode ... \p{Word} might help. negated char class with it?
if title if !draft? && title
@slug ||= title.downcase. @slug ||= title.downcase.
gsub(/'/, ''). gsub(/'/, '').
gsub(/[^[:alpha:]\d_]/, '-'). gsub(/[^[:alpha:]\d_]/, '-').
@ -184,12 +194,12 @@ class HarpBlog
read_posts('drafts', draft: true) read_posts('drafts', draft: true)
end end
def get_post(year, month, slug) def get_post(year, month, id)
read_post(File.join(year, month), slug) read_post(File.join(year, month), id)
end end
def get_draft(slug) def get_draft(id)
read_post('drafts', slug, draft: true) read_post('drafts', id, draft: true)
end end
def create_post(title, body, url, extra_fields = nil) def create_post(title, body, url, extra_fields = nil)
@ -208,15 +218,15 @@ class HarpBlog
post = Post.new(fields) post = Post.new(fields)
begin begin
existing_post = read_post(post.dir, post.slug, extra_fields) existing_post = read_post(post.dir, post.id, extra_fields)
rescue InvalidDataError => e rescue InvalidDataError => e
$stderr.puts "[HarpBlog#create_post] deleting post with invalid data: #{e.message}" $stderr.puts "[HarpBlog#create_post] deleting post with invalid data: #{e.message}"
delete_post_from_dir(post.dir, post.slug) delete_post_from_dir(post.dir, post.id)
existing_post = nil existing_post = nil
end end
if existing_post if existing_post
raise PostExistsError.new("post exists: #{post.dir}/#{post.slug}") raise PostExistsError.new("post exists: #{post.dir}/#{post.id}")
else else
save_post('create post', post) save_post('create post', post)
end end
@ -229,36 +239,36 @@ class HarpBlog
save_post('update post', post) save_post('update post', post)
end end
def delete_post(year, month, slug) def delete_post(year, month, id)
delete_post_from_dir(File.join(year, month), slug) delete_post_from_dir(File.join(year, month), id)
end end
def delete_draft(slug) def delete_draft(id)
delete_post_from_dir('drafts', slug) delete_post_from_dir('drafts', id)
end end
def publish_post(post) def publish_post(post)
if post.draft? if post.draft?
new_post = create_post(post.title, post.body, post.link) new_post = create_post(post.title, post.body, post.link)
delete_post_from_dir('drafts', post.slug) delete_post_from_dir('drafts', post.id)
new_post new_post
else else
raise PostAlreadyPublishedError.new("post is already published: #{post.dir}/#{post.slug}") raise PostAlreadyPublishedError.new("post is already published: #{post.dir}/#{post.id}")
end end
end end
def unpublish_post(post) def unpublish_post(post)
if post.draft? if post.draft?
raise PostNotPublishedError.new("post is not published: #{post.dir}/#{post.slug}") raise PostNotPublishedError.new("post is not published: #{post.dir}/#{post.id}")
else else
new_post = create_post(post.title, post.body, post.link, draft: true) new_post = create_post(post.title, post.body, post.link, draft: true)
delete_post_from_dir(post.dir, post.slug) delete_post_from_dir(post.dir, post.id)
new_post new_post
end end
end end
def publish(production = false) def publish(env)
target = production ? 'publish' : 'publish_beta' target = env.to_s == 'production' ? 'publish' : 'publish_beta'
run("make #{target}") run("make #{target}")
end end
@ -282,26 +292,40 @@ class HarpBlog
end end
def read_posts(post_dir, extra_fields = nil) def read_posts(post_dir, extra_fields = nil)
extra_fields ||= {}
post_data = read_post_data(post_path(post_dir)) post_data = read_post_data(post_path(post_dir))
post_data.sort_by do |k, v| post_data.sort_by do |k, v|
(v['timestamp'] || Time.now).to_i (v['timestamp'] || Time.now).to_i
end.map do |slug, fields| end.map do |id, fields|
Post.new(fields.merge(extra_fields || {}).merge(slug: slug)) fields[:id] = id
unless extra_fields[:draft]
fields[:slug] = id
end
post_filename = post_path(post_dir, "#{id}.md")
fields[:body] = File.read(post_filename)
Post.new(fields.merge(extra_fields))
end end
end end
def read_post(post_dir, slug, extra_fields = nil) def read_post(post_dir, id, extra_fields = nil)
post_filename = post_path(post_dir, "#{slug}.md") post_filename = post_path(post_dir, "#{id}.md")
post_data = read_post_data(post_path(post_dir)) post_data = read_post_data(post_path(post_dir))
if File.exist?(post_filename) && fields = post_data[slug] if File.exist?(post_filename) && fields = post_data[id]
fields[:body] = File.read(post_filename) fields[:body] = File.read(post_filename)
Post.new(fields.merge(extra_fields || {}).merge(slug: slug)) if extra_fields
fields.merge!(extra_fields)
end
fields[:id] = id
unless fields[:draft]
fields[:slug] = id
end
Post.new(fields)
elsif fields elsif fields
message = "missing post body for #{post_dir}/#{slug}: #{post_filename}" message = "missing post body for #{post_dir}/#{id}: #{post_filename}"
$stderr.puts "[HarpBlog#read_post] #{message}" $stderr.puts "[HarpBlog#read_post] #{message}"
raise InvalidDataError.new(message) raise InvalidDataError.new(message)
elsif File.exist?(post_filename) elsif File.exist?(post_filename)
message = "missing metadata for #{post_dir}/#{slug}: #{post_dir}/_data.json" message = "missing metadata for #{post_dir}/#{id}: #{post_dir}/_data.json"
$stderr.puts "[HarpBlog#read_post] #{message}" $stderr.puts "[HarpBlog#read_post] #{message}"
raise InvalidDataError.new(message) raise InvalidDataError.new(message)
end end
@ -330,43 +354,43 @@ class HarpBlog
unless post.draft? unless post.draft?
ensure_post_dir_exists(post_dir) ensure_post_dir_exists(post_dir)
end end
write_post_body(post_dir, post.slug, post.body) write_post_body(post_dir, post.id, post.body)
begin begin
write_post_index(post_dir, post.slug, post.persistent_fields) write_post_index(post_dir, post.id, post.persistent_fields)
rescue => e rescue => e
$stderr.puts "#{e.class}: #{e.message}" $stderr.puts "#{e.class}: #{e.message}"
$stderr.puts e.backtrace $stderr.puts e.backtrace
delete_post_body(post_dir, post.slug) delete_post_body(post_dir, post.id)
raise e raise e
end end
end end
def delete_post_from_dir(post_dir, slug) def delete_post_from_dir(post_dir, id)
post_dir = post_path(post_dir) post_dir = post_path(post_dir)
delete_post_body(post_dir, slug) delete_post_body(post_dir, id)
delete_post_index(post_dir, slug) delete_post_index(post_dir, id)
end end
def write_post_body(dir, slug, body) def write_post_body(dir, id, body)
post_filename = File.join(dir, "#{slug}.md") post_filename = File.join(dir, "#{id}.md")
write_file(post_filename, body) write_file(post_filename, body)
end end
def delete_post_body(dir, slug) def delete_post_body(dir, id)
post_filename = File.join(dir, "#{slug}.md") post_filename = File.join(dir, "#{id}.md")
delete_file(post_filename) delete_file(post_filename)
end end
def write_post_index(dir, slug, fields) def write_post_index(dir, id, fields)
post_data = read_post_data(dir) post_data = read_post_data(dir)
post_data[slug] = fields post_data[id] = fields
write_post_data(dir, post_data) write_post_data(dir, post_data)
end end
def delete_post_index(dir, slug) def delete_post_index(dir, id)
post_data = read_post_data(dir) post_data = read_post_data(dir)
if post_data[slug] if post_data[id]
post_data.delete(slug) post_data.delete(id)
write_post_data(dir, post_data) write_post_data(dir, post_data)
end end
end end

View file

@ -67,11 +67,36 @@ set :port, $config[:port]
blog = HarpBlog.new($config[:path], $config[:dry_run]) blog = HarpBlog.new($config[:path], $config[:dry_run])
before do
if request.body.size > 0
type = request['HTTP_CONTENT_TYPE']
@fields =
case
when type =~ /^application\/json\b/
request.body.rewind
JSON.parse(request.body.read)
else
params
end
end
end
# status # status
get '/status' do get '/status' do
status 200 status 200
headers 'Content-Type' => 'application/json' headers 'Content-Type' => 'application/json'
JSON.generate(blog.status) JSON.generate(status: blog.status)
end
# publish the site
post '/publish' do
unless authenticated?(request['Auth'])
status 403
return 'forbidden'
end
blog.publish(@fields['env'])
status 204
end end
# list years # list years
@ -102,6 +127,17 @@ get '/posts/:year/?:month?' do |year, month|
JSON.generate(posts: posts.map(&:fields)) JSON.generate(posts: posts.map(&:fields))
end end
# list all published posts
get '/posts' do
posts = blog.months.map do |year, month|
blog.posts_for_month(year, month)
end.flatten
status 200
headers 'Content-Type' => 'application/json'
JSON.generate(posts: posts.map(&:fields))
end
# list drafts # list drafts
get '/drafts' do get '/drafts' do
posts = blog.drafts posts = blog.drafts
@ -112,9 +148,9 @@ get '/drafts' do
end end
# get a post # get a post
get '/posts/:year/:month/:slug' do |year, month, slug| get '/posts/:year/:month/:id' do |year, month, id|
begin begin
post = blog.get_post(year, month, slug) post = blog.get_post(year, month, id)
rescue HarpBlog::InvalidDataError => e rescue HarpBlog::InvalidDataError => e
status 500 status 500
return "Failed to get post, invalid data on disk: #{e.message}" return "Failed to get post, invalid data on disk: #{e.message}"
@ -140,9 +176,9 @@ get '/posts/:year/:month/:slug' do |year, month, slug|
end end
# get a draft # get a draft
get '/drafts/:slug' do |slug| get '/drafts/:id' do |id|
begin begin
post = blog.get_draft(slug) post = blog.get_draft(id)
rescue HarpBlog::InvalidDataError => e rescue HarpBlog::InvalidDataError => e
status 500 status 500
return "Failed to get draft, invalid data on disk: #{e.message}" return "Failed to get draft, invalid data on disk: #{e.message}"
@ -174,16 +210,25 @@ post '/drafts' do
return 'forbidden' return 'forbidden'
end end
id, title, body, link = @fields.values_at('id', 'title', 'body', 'link')
begin begin
post = blog.create_post(params[:title], params[:body], params[:link], draft: true) if post = blog.create_post(title, body, link, id: id, draft: true)
url = url_for(post.url)
status 201
headers 'Location' => url, 'Content-Type' => 'application/json'
JSON.generate(post: post.fields)
else
status 500
'failed to create post'
end
rescue HarpBlog::PostExistsError => e rescue HarpBlog::PostExistsError => e
post = HarpBlog::Post.new({ post = HarpBlog::Post.new({
title: params[:title], title: title,
body: params[:body], body: body,
link: params[:link], link: link,
}) })
status 409 status 409
return "refusing to clobber existing draft, update it instead: #{post.url}" "refusing to clobber existing draft, update it instead: #{post.url}"
rescue HarpBlog::PostSaveError => e rescue HarpBlog::PostSaveError => e
status 500 status 500
if orig_err = e.original_error if orig_err = e.original_error
@ -192,28 +237,19 @@ post '/drafts' do
"Failed to create draft: #{e.message}" "Failed to create draft: #{e.message}"
end end
end end
if post
url = url_for(post.url)
status 201
headers 'Location' => url, 'Content-Type' => 'application/json'
JSON.generate(post: post.fields)
else
status 500
'failed to create post'
end
end end
# update a post # update a post
put '/posts/:year/:month/:slug' do |year, month, slug| put '/posts/:year/:month/:id' do |year, month, id|
unless authenticated?(request['Auth']) unless authenticated?(request['Auth'])
status 403 status 403
return 'forbidden' return 'forbidden'
end end
title, body, link = @field.values_at('title', 'body', 'link')
begin begin
if post = blog.get_post(year, month, slug) if post = blog.get_post(year, month, id)
blog.update_post(post, params[:title], params[:body], params[:link]) blog.update_post(post, title, body, link)
status 204 status 204
else else
status 404 status 404
@ -233,15 +269,16 @@ put '/posts/:year/:month/:slug' do |year, month, slug|
end end
# update a draft # update a draft
put '/drafts/:slug' do |slug| put '/drafts/:id' do |id|
unless authenticated?(request['Auth']) unless authenticated?(request['Auth'])
status 403 status 403
return 'forbidden' return 'forbidden'
end end
title, body, link = @field.values_at('title', 'body', 'link')
begin begin
if post = blog.get_draft(slug) if post = blog.get_draft(id)
blog.update_post(post, params[:title], params[:body], params[:link]) blog.update_post(post, title, body, link)
status 204 status 204
else else
status 404 status 404
@ -261,35 +298,35 @@ put '/drafts/:slug' do |slug|
end end
# delete a post # delete a post
delete '/posts/:year/:month/:slug' do |year, month, slug| delete '/posts/:year/:month/:id' do |year, month, id|
unless authenticated?(request['Auth']) unless authenticated?(request['Auth'])
status 403 status 403
return 'forbidden' return 'forbidden'
end end
blog.delete_post(year, month, slug) blog.delete_post(year, month, id)
status 204 status 204
end end
# delete a draft # delete a draft
delete '/drafts/:slug' do |slug| delete '/drafts/:id' do |id|
unless authenticated?(request['Auth']) unless authenticated?(request['Auth'])
status 403 status 403
return 'forbidden' return 'forbidden'
end end
blog.delete_draft(slug) blog.delete_draft(id)
status 204 status 204
end end
# publish a post # publish a post
post '/drafts/:slug/publish' do |slug| post '/drafts/:id/publish' do |id|
unless authenticated?(request['Auth']) unless authenticated?(request['Auth'])
status 403 status 403
return 'forbidden' return 'forbidden'
end end
if post = blog.get_draft(slug) if post = blog.get_draft(id)
new_post = blog.publish_post(post) new_post = blog.publish_post(post)
status 201 status 201
headers 'Location' => url_for(new_post.url), 'Content-Type' => 'application/json' headers 'Location' => url_for(new_post.url), 'Content-Type' => 'application/json'
@ -301,13 +338,13 @@ post '/drafts/:slug/publish' do |slug|
end end
# unpublish a post # unpublish a post
post '/posts/:year/:month/:slug/unpublish' do |year, month, slug| post '/posts/:year/:month/:id/unpublish' do |year, month, id|
unless authenticated?(request['Auth']) unless authenticated?(request['Auth'])
status 403 status 403
return 'forbidden' return 'forbidden'
end end
if post = blog.get_post(year, month, slug) if post = blog.get_post(year, month, id)
new_post = blog.unpublish_post(post) new_post = blog.unpublish_post(post)
status 201 status 201
headers 'Location' => url_for(new_post.url), 'Content-Type' => 'application/json' headers 'Location' => url_for(new_post.url), 'Content-Type' => 'application/json'
@ -317,15 +354,3 @@ post '/posts/:year/:month/:slug/unpublish' do |year, month, slug|
'not found' 'not found'
end end
end end
# publish the site
post '/publish' do
unless authenticated?(request['Auth'])
status 403
return 'forbidden'
end
production = params[:env] == 'production'
blog.publish(production)
status 204
end

View file

@ -11,21 +11,28 @@ end
RSpec.describe HarpBlog::Post do RSpec.describe HarpBlog::Post do
# Persistent fields: author, title, date, timestamp, link, url, tags # Persistent fields: id, author, title, date, timestamp, link, url, tags
# Transient fields: time, slug, body # Transient fields: time, slug, body
before :all do before :all do
@default_fields = { @post_fields = {
title: 'samhuri.net', title: 'samhuri.net',
link: 'http://samhuri.net', link: 'http://samhuri.net',
body: 'this site is sick', body: 'this site is sick',
} }
@default_slug = 'samhuri-net' @post_slug = 'samhuri-net'
@draft_fields = {
title: 'reddit.com',
link: 'http://reddit.com',
body: 'hi reddit',
draft: true,
id: 'dummy-draft-id',
}
end end
describe '#new' do describe '#new' do
it "takes a Hash of fields" do it "takes a Hash of fields" do
fields = @default_fields fields = @post_fields
post = HarpBlog::Post.new(fields) post = HarpBlog::Post.new(fields)
expect(post.title).to eq(fields[:title]) expect(post.title).to eq(fields[:title])
expect(post.link).to eq(fields[:link]) expect(post.link).to eq(fields[:link])
@ -61,7 +68,7 @@ RSpec.describe HarpBlog::Post do
describe '#link?' do describe '#link?' do
it "returns true for link posts" do it "returns true for link posts" do
post = HarpBlog::Post.new(link: @default_fields[:link]) post = HarpBlog::Post.new(link: @post_fields[:link])
expect(post.link?).to be_truthy expect(post.link?).to be_truthy
end end
@ -101,18 +108,38 @@ RSpec.describe HarpBlog::Post do
describe '#url' do describe '#url' do
it "should be derived from the time and slug if necessary" do it "should be derived from the time and slug if necessary" do
post = HarpBlog::Post.new(@default_fields) post = HarpBlog::Post.new(@post_fields)
year = post.time.year.to_s year = post.time.year.to_s
month = post.time.month month = post.time.month
padded_month = month < 10 ? " #{month}" : "#{month}" padded_month = month < 10 ? "0#{month}" : "#{month}"
expect(post.url).to eq("/posts/#{year}/#{padded_month}/#{@default_slug}") expect(post.url).to eq("/posts/#{year}/#{padded_month}/#{@post_slug}")
end
end
describe '#id' do
it "should be generated for drafts if necessary" do
draft = HarpBlog::Post.new(@draft_fields)
expect(draft.id).to eq(@draft_fields[:id])
draft = HarpBlog::Post.new(@draft_fields.merge(id: nil))
expect(draft.id).to_not eq(@draft_fields[:id])
end
it "should be the slug for posts" do
post = HarpBlog::Post.new(@post_fields)
expect(post.id).to eq(post.slug)
end end
end end
describe '#slug' do describe '#slug' do
it "should be derived from the title if necessary" do it "should be derived from the title if necessary" do
post = HarpBlog::Post.new(@default_fields) post = HarpBlog::Post.new(@post_fields)
expect(post.slug).to eq(@default_slug) expect(post.slug).to eq(@post_slug)
end
it "should be nil for drafts" do
draft = HarpBlog::Post.new(@draft_fields)
expect(draft.slug).to be_nil
end end
it "should strip apostrophes" do it "should strip apostrophes" do
@ -303,20 +330,21 @@ RSpec.describe HarpBlog do
describe '#get_draft' do describe '#get_draft' do
it "should return complete posts" do it "should return complete posts" do
id = 'some-draft-id'
title = 'new draft' title = 'new draft'
body = "blah blah blah\n" body = "blah blah blah\n"
@blog.create_post(title, body, nil, draft: true) @blog.create_post(title, body, nil, id: id, draft: true)
post = @blog.get_draft('new-draft') draft = @blog.get_draft(id)
expect(post).to be_truthy expect(draft).to be_truthy
expect(post.title).to eq(title) expect(draft.title).to eq(title)
expect(post.url).to eq('/posts/drafts/new-draft') expect(draft.url).to eq("/posts/drafts/#{id}")
expect(post.draft?).to be_truthy expect(draft.draft?).to be_truthy
expect(post.body).to eq(body) expect(draft.body).to eq(body)
end end
it "should return nil if the post does not exist" do it "should return nil if the post does not exist" do
post = @blog.get_draft('does-not-exist') draft = @blog.get_draft('does-not-exist')
expect(post).to be(nil) expect(draft).to be(nil)
end end
end end
@ -369,13 +397,14 @@ RSpec.describe HarpBlog do
end end
it "should create a draft that can be fetched immediately" do it "should create a draft that can be fetched immediately" do
id = 'another-draft-id'
title = 'fetch now' title = 'fetch now'
body = 'blah blah blah' body = 'blah blah blah'
post = @blog.create_post(title, body, nil, draft: true) draft = @blog.create_post(title, body, nil, id: id, draft: true)
expect(post).to be_truthy expect(draft).to be_truthy
fetched_post = @blog.get_draft(post.slug) fetched_draft = @blog.get_draft(draft.id)
expect(post.url).to eq(fetched_post.url) expect(draft.url).to eq(fetched_draft.url)
end end
it "should fetch titles if necessary" do it "should fetch titles if necessary" do
@ -434,49 +463,54 @@ RSpec.describe HarpBlog do
describe '#delete_draft' do describe '#delete_draft' do
it "should delete existing drafts" do it "should delete existing drafts" do
id = 'bunk-draft-id'
title = 'new draft' title = 'new draft'
body = 'blah blah blah' body = 'blah blah blah'
existing_post = @blog.create_post(title, body, nil, draft: true) existing_draft = @blog.create_post(title, body, nil, id: id, draft: true)
post = @blog.get_draft(existing_post.slug) draft = @blog.get_draft(existing_draft.id)
expect(post).to be_truthy expect(draft).to be_truthy
@blog.delete_draft(post.slug) @blog.delete_draft(draft.id)
post = @blog.get_draft(post.slug) draft = @blog.get_draft(draft.id)
expect(post).to eq(nil) expect(draft).to eq(nil)
end end
it "should do nothing for non-existent posts" do it "should do nothing for non-existent posts" do
id = 'missing-draft-id'
title = 'new draft' title = 'new draft'
body = 'blah blah blah' body = 'blah blah blah'
existing_post = @blog.create_post(title, body, nil, draft: true) existing_draft = @blog.create_post(title, body, nil, id: id, draft: true)
post = @blog.get_draft(existing_post.slug) draft = @blog.get_draft(existing_draft.id)
expect(post).to be_truthy expect(draft).to be_truthy
@blog.delete_draft(post.slug) @blog.delete_draft(draft.id)
@blog.delete_draft(post.slug) expect(@blog.get_draft(existing_draft.id)).to be_nil
@blog.delete_draft(draft.id)
end end
end end
describe '#publish_post' do describe '#publish_post' do
it "should publish drafts" do it "should publish drafts" do
id = 'this-draft-is-a-keeper'
title = 'a-shiny-new-post' title = 'a-shiny-new-post'
body = 'blah blah blah' body = 'blah blah blah'
link = 'http://samhuri.net' link = 'http://samhuri.net'
post = @blog.create_post(title, body, link, draft: true) draft = @blog.create_post(title, body, link, id: id, draft: true)
new_post = @blog.publish_post(post) post = @blog.publish_post(draft)
expect(new_post).to be_truthy
expect(new_post.draft?).to be_falsy
expect(new_post.title).to eq(title)
expect(new_post.body).to eq(body)
expect(new_post.link).to eq(link)
draft = @blog.get_draft(post.slug)
expect(draft).to eq(nil)
post = @blog.get_post(post.time.year.to_s, post.padded_month, post.slug)
expect(post).to be_truthy expect(post).to be_truthy
expect(post.id).to eq(post.slug)
expect(post.draft?).to be_falsy
expect(post.title).to eq(title)
expect(post.body).to eq(body)
expect(post.link).to eq(link)
missing_draft = @blog.get_draft(draft.id)
expect(missing_draft).to eq(nil)
fetched_post = @blog.get_post(post.time.year.to_s, post.padded_month, post.slug)
expect(fetched_post).to be_truthy
end end
it "should raise an error for published posts" do it "should raise an error for published posts" do
@ -488,18 +522,19 @@ RSpec.describe HarpBlog do
describe '#unpublish_post' do describe '#unpublish_post' do
it "should unpublish posts" do it "should unpublish posts" do
post = @blog.get_post('2006', '02', 'first-post') post = @blog.get_post('2006', '02', 'first-post')
new_post = @blog.unpublish_post(post) draft = @blog.unpublish_post(post)
expect(new_post).to be_truthy
expect(new_post.draft?).to be_truthy
expect(new_post.title).to eq(post.title)
expect(new_post.body).to eq(post.body)
expect(new_post.link).to eq(post.link)
post = @blog.get_post(post.time.year.to_s, post.padded_month, post.slug)
expect(post).to eq(nil)
draft = @blog.get_draft(new_post.slug)
expect(draft).to be_truthy expect(draft).to be_truthy
expect(draft.id).to be_truthy
expect(draft.draft?).to be_truthy
expect(draft.title).to eq(post.title)
expect(draft.body).to eq(post.body)
expect(draft.link).to eq(post.link)
missing_post = @blog.get_post(post.time.year.to_s, post.padded_month, post.slug)
expect(missing_post).to eq(nil)
fetched_draft = @blog.get_draft(draft.id)
expect(fetched_draft).to be_truthy
end end
it "should raise an error for drafts" do it "should raise an error for drafts" do