{"componentChunkName":"component---src-templates-blog-post-js","path":"/blog/draftsman-gem-draft-state-ruby-on-rails-sinatra-relational-models/","result":{"data":{"site":{"siteMetadata":{"title":"Chris Peters","siteUrl":"https://www.chrisdpeters.com"}},"markdownRemark":{"id":"f32e7965-1062-549e-8c9b-66e3df53c26e","html":"<p><a href=\"https://github.com/liveeditor/draftsman\">Draftsman</a> is a Ruby gem that lets you create draft versions of your database records. If you’re developing a system in need of simple drafts or a publishing approval queue, then Draftsman just might be what you need.</p>\n<p>I built Draftsman to handle the draft logic in <a href=\"http://www.liveeditorcms.com/\">Live Editor</a>. Because draft logic gets complicated pretty quickly, I felt that this warranted its own gem with its own set of tests. Plus this was a great opportunity to share a piece of Live Editor with the open source community!</p>\n<p>Live Editor uses the excellent <a href=\"https://github.com/airblade/paper_trail\">PaperTrail gem</a> for publication revision tracking, so I built the API behind Draftsman to work much like PaperTrail.</p>\n<h2>A taste of how it works</h2>\n<p>Have a read of the <a href=\"https://github.com/liveeditor/draftsman\">README</a> to see more examples of how to work with the Draftsman gem.</p>\n<h3>Setup</h3>\n<p>To get up and running with Draftsman, you generally have to do a few things:</p>\n<ul>\n<li>Add <code class=\"language-text\">draftsman</code> to your <code class=\"language-text\">Gemfile</code>.</li>\n<li>Add the <code class=\"language-text\">drafts</code> table to your database with the <code class=\"language-text\">rails g draftsman:install</code> command (or copy the migration file into your application if you’re using Sinatra).</li>\n<li>Add <code class=\"language-text\">draft_id</code>, <code class=\"language-text\">published_at</code>, and <code class=\"language-text\">trashed_at</code> columns to the database tables you want to have drafts on.</li>\n<li>Add <code class=\"language-text\">has_drafts</code> to the models you want to have drafts on.</li>\n</ul>\n<p>More specific installation instructions are available at the <a href=\"https://github.com/liveeditor/draftsman\">GitHub project</a>.</p>\n<h3>Persistence of draft data</h3>\n<p>The Draftsman API requires you to be explicit about when you want to save data as a draft. It provides these persistence methods on your models containing <code class=\"language-text\">has_drafts</code>:</p>\n<ul>\n<li><code class=\"language-text\">draft_creation</code></li>\n<li><code class=\"language-text\">draft_update</code></li>\n<li><code class=\"language-text\">draft_destroy</code></li>\n</ul>\n<p><code class=\"language-text\">draft_creation</code> and <code class=\"language-text\">draft_update</code> work like ActiveRecord’s <code class=\"language-text\">save</code> and <code class=\"language-text\">update</code> methods: they validate data in the model, run callbacks, and return <code class=\"language-text\">true</code> or <code class=\"language-text\">false</code> if everything saved successfully.</p>\n<h3>Publishing and reverting drafts</h3>\n<p>By calling <code class=\"language-text\">draft_creation</code>, <code class=\"language-text\">draft_update</code>, and <code class=\"language-text\">draft_destroy</code>, you’re effectively storing a copy of the record with drafted changes in the <code class=\"language-text\">drafts</code> table.</p>\n<p>You can access these drafts through a query directly on the <code class=\"language-text\">Draftsman::Draft</code> class or through an instance method called <code class=\"language-text\">draft</code> on your model objects.</p>\n<p><div id=\"gist13582820\" class=\"gist\">\n    <div class=\"gist-file\" translate=\"no\">\n      <div class=\"gist-data\">\n        <div class=\"js-gist-file-update-container js-task-list-container file-box\">\n  <div id=\"file-accessing_drafts-rb\" class=\"file my-2\">\n    \n  <div itemprop=\"text\" class=\"Box-body p-0 blob-wrapper data type-ruby  \">\n\n      \n<div class=\"js-check-bidi js-blob-code-container blob-code-content\">\n\n  <template class=\"js-file-alert-template\">\n  <div data-view-component=\"true\" class=\"flash flash-warn flash-full d-flex flex-items-center\">\n  <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path fill-rule=\"evenodd\" d=\"M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z\"></path>\n</svg>\n  \n    <span>\n      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a href=\"https://github.co/hiddenchars\" target=\"_blank\">Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\"true\" class=\"flash-action\">        <a href=\"{{ revealButtonHref }}\" data-view-component=\"true\" class=\"btn-sm btn\">  Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\"js-line-alert-template\">\n  <span aria-label=\"This line has hidden Unicode characters\" data-view-component=\"true\" class=\"line-alert tooltipped tooltipped-e\">\n    <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path fill-rule=\"evenodd\" d=\"M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z\"></path>\n</svg>\n</span></template>\n\n  <table class=\"highlight tab-size js-file-line-container js-code-nav-container js-tagsearch-file\" data-tab-size=\"8\" data-paste-markdown-skip data-tagsearch-lang=\"Ruby\" data-tagsearch-path=\"accessing_drafts.rb\">\n        <tr>\n          <td id=\"file-accessing_drafts-rb-L1\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"1\"></td>\n          <td id=\"file-accessing_drafts-rb-LC1\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-c># Accessing drafts through the `Draftsman::Draft` class</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-accessing_drafts-rb-L2\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"2\"></td>\n          <td id=\"file-accessing_drafts-rb-LC2\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-c1>@drafts</span> <span class=pl-c1>=</span> <span class=pl-v>Draftsman</span>::<span class=pl-v>Draft</span><span class=pl-kos>.</span><span class=pl-en>order</span><span class=pl-kos>(</span><span class=pl-pds>:created_at</span><span class=pl-kos>)</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-accessing_drafts-rb-L3\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"3\"></td>\n          <td id=\"file-accessing_drafts-rb-LC3\" class=\"blob-code blob-code-inner js-file-line\">\n</td>\n        </tr>\n        <tr>\n          <td id=\"file-accessing_drafts-rb-L4\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"4\"></td>\n          <td id=\"file-accessing_drafts-rb-LC4\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-c># Accessing a draft through a model</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-accessing_drafts-rb-L5\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"5\"></td>\n          <td id=\"file-accessing_drafts-rb-LC5\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-c1>@post</span> <span class=pl-c1>=</span> <span class=pl-v>Post</span><span class=pl-kos>.</span><span class=pl-en>find</span><span class=pl-kos>(</span><span class=pl-en>params</span><span class=pl-kos>[</span><span class=pl-pds>:id</span><span class=pl-kos>]</span><span class=pl-kos>)</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-accessing_drafts-rb-L6\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"6\"></td>\n          <td id=\"file-accessing_drafts-rb-LC6\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-c1>@draft</span> <span class=pl-c1>=</span> <span class=pl-c1>@post</span><span class=pl-kos>.</span><span class=pl-en>draft</span> <span class=pl-k>if</span> <span class=pl-c1>@post</span><span class=pl-kos>.</span><span class=pl-en>draft?</span></td>\n        </tr>\n  </table>\n</div>\n\n\n  </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\"gist-meta\">\n        <a href=\"https://gist.github.com/chrisdpeters/8324486e91eaf9e1b1ed/raw/0e84387a2ec5cc7a6b5dc6d743dc837a76cb93e2/accessing_drafts.rb\" style=\"float:right\">view raw</a>\n        <a href=\"https://gist.github.com/chrisdpeters/8324486e91eaf9e1b1ed#file-accessing_drafts-rb\">\n          accessing_drafts.rb\n        </a>\n        hosted with &#10084; by <a href=\"https://github.com\">GitHub</a>\n      </div>\n    </div>\n</div></p>\n<p>Once you have a draft record, you can call <code class=\"language-text\">revert!</code> on it to undo the draft or <code class=\"language-text\">publish!</code> to publish the changes to the main record.</p>\n<h3>Query scopes</h3>\n<p>Once you’re living in the brave new world of drafts, you’ll want to query your model data differently depending on where you are in the application.</p>\n<p>Here is a quick listing of scopes that Draftsman adds to your model when you add <code class=\"language-text\">has_drafts</code> to it:</p>\n<p><div id=\"gist13582820\" class=\"gist\">\n    <div class=\"gist-file\" translate=\"no\">\n      <div class=\"gist-data\">\n        <div class=\"js-gist-file-update-container js-task-list-container file-box\">\n  <div id=\"file-scopes-rb\" class=\"file my-2\">\n    \n  <div itemprop=\"text\" class=\"Box-body p-0 blob-wrapper data type-ruby  \">\n\n      \n<div class=\"js-check-bidi js-blob-code-container blob-code-content\">\n\n  <template class=\"js-file-alert-template\">\n  <div data-view-component=\"true\" class=\"flash flash-warn flash-full d-flex flex-items-center\">\n  <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path fill-rule=\"evenodd\" d=\"M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z\"></path>\n</svg>\n  \n    <span>\n      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a href=\"https://github.co/hiddenchars\" target=\"_blank\">Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\"true\" class=\"flash-action\">        <a href=\"{{ revealButtonHref }}\" data-view-component=\"true\" class=\"btn-sm btn\">  Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\"js-line-alert-template\">\n  <span aria-label=\"This line has hidden Unicode characters\" data-view-component=\"true\" class=\"line-alert tooltipped tooltipped-e\">\n    <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path fill-rule=\"evenodd\" d=\"M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z\"></path>\n</svg>\n</span></template>\n\n  <table class=\"highlight tab-size js-file-line-container js-code-nav-container js-tagsearch-file\" data-tab-size=\"8\" data-paste-markdown-skip data-tagsearch-lang=\"Ruby\" data-tagsearch-path=\"scopes.rb\">\n        <tr>\n          <td id=\"file-scopes-rb-L1\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"1\"></td>\n          <td id=\"file-scopes-rb-LC1\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-v>Widget</span><span class=pl-kos>.</span><span class=pl-en>drafted</span>    <span class=pl-c># Limits to items that have drafts. Best used in an &quot;admin&quot; area in your application.</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-scopes-rb-L2\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"2\"></td>\n          <td id=\"file-scopes-rb-LC2\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-v>Widget</span><span class=pl-kos>.</span><span class=pl-en>published</span>  <span class=pl-c># Limits to items that have been published at some point in their lifecycles. Best used in a &quot;public&quot; area in your application.</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-scopes-rb-L3\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"3\"></td>\n          <td id=\"file-scopes-rb-LC3\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-v>Widget</span><span class=pl-kos>.</span><span class=pl-en>trashed</span>    <span class=pl-c># Limits to items that have been drafted for deletion (but not fully committed for deletion). Best used in an &quot;admin&quot; area in your application.</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-scopes-rb-L4\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"4\"></td>\n          <td id=\"file-scopes-rb-LC4\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-v>Widget</span><span class=pl-kos>.</span><span class=pl-en>live</span>       <span class=pl-c># Limits to items that have not been drafted for deletion. Best used in an &quot;admin&quot; area in your application.</span></td>\n        </tr>\n  </table>\n</div>\n\n\n  </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\"gist-meta\">\n        <a href=\"https://gist.github.com/chrisdpeters/8324486e91eaf9e1b1ed/raw/0e84387a2ec5cc7a6b5dc6d743dc837a76cb93e2/scopes.rb\" style=\"float:right\">view raw</a>\n        <a href=\"https://gist.github.com/chrisdpeters/8324486e91eaf9e1b1ed#file-scopes-rb\">\n          scopes.rb\n        </a>\n        hosted with &#10084; by <a href=\"https://github.com\">GitHub</a>\n      </div>\n    </div>\n</div></p>\n<p>Typically, the <code class=\"language-text\">live</code> scope is used in the “admin” section of your application because it limits the query to records that have not been put in the trash (or <code class=\"language-text\">draft_destroy</code>ed, to use Draftsman terminology).</p>\n<p>When you want to show only published data (usually in the public or non-admin area of your application), you should use the <code class=\"language-text\">published</code> scope to query your data.</p>\n<h3>Loading drafted data</h3>\n<p>In the admin area of your application, you’ll most likely want to load your data in a mixed state:</p>\n<ul>\n<li>If a record has a draft, show that data</li>\n<li>If a record doesn’t have a draft, show that data</li>\n<li>Hide records that have been drafted for destruction (AKA put in the trash)</li>\n</ul>\n<p>The way to accomplish this is through the <code class=\"language-text\">reify</code> method on each record’s <code class=\"language-text\">draft</code>. Take a look at these sample <code class=\"language-text\">index</code> and <code class=\"language-text\">show</code> actions for a <code class=\"language-text\">posts</code> admin area:</p>\n<p><div id=\"gist13582820\" class=\"gist\">\n    <div class=\"gist-file\" translate=\"no\">\n      <div class=\"gist-data\">\n        <div class=\"js-gist-file-update-container js-task-list-container file-box\">\n  <div id=\"file-posts_controller-rb\" class=\"file my-2\">\n    \n  <div itemprop=\"text\" class=\"Box-body p-0 blob-wrapper data type-ruby  \">\n\n      \n<div class=\"js-check-bidi js-blob-code-container blob-code-content\">\n\n  <template class=\"js-file-alert-template\">\n  <div data-view-component=\"true\" class=\"flash flash-warn flash-full d-flex flex-items-center\">\n  <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path fill-rule=\"evenodd\" d=\"M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z\"></path>\n</svg>\n  \n    <span>\n      This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.\n      <a href=\"https://github.co/hiddenchars\" target=\"_blank\">Learn more about bidirectional Unicode characters</a>\n    </span>\n\n\n  <div data-view-component=\"true\" class=\"flash-action\">        <a href=\"{{ revealButtonHref }}\" data-view-component=\"true\" class=\"btn-sm btn\">  Show hidden characters\n</a>\n</div>\n</div></template>\n<template class=\"js-line-alert-template\">\n  <span aria-label=\"This line has hidden Unicode characters\" data-view-component=\"true\" class=\"line-alert tooltipped tooltipped-e\">\n    <svg aria-hidden=\"true\" height=\"16\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" data-view-component=\"true\" class=\"octicon octicon-alert\">\n    <path fill-rule=\"evenodd\" d=\"M8.22 1.754a.25.25 0 00-.44 0L1.698 13.132a.25.25 0 00.22.368h12.164a.25.25 0 00.22-.368L8.22 1.754zm-1.763-.707c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0114.082 15H1.918a1.75 1.75 0 01-1.543-2.575L6.457 1.047zM9 11a1 1 0 11-2 0 1 1 0 012 0zm-.25-5.25a.75.75 0 00-1.5 0v2.5a.75.75 0 001.5 0v-2.5z\"></path>\n</svg>\n</span></template>\n\n  <table class=\"highlight tab-size js-file-line-container js-code-nav-container js-tagsearch-file\" data-tab-size=\"8\" data-paste-markdown-skip data-tagsearch-lang=\"Ruby\" data-tagsearch-path=\"posts_controller.rb\">\n        <tr>\n          <td id=\"file-posts_controller-rb-L1\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"1\"></td>\n          <td id=\"file-posts_controller-rb-LC1\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-k>class</span> <span class=pl-v>Admin</span>::<span class=pl-v>PostsController</span> &lt; <span class=pl-v>Admin</span>::<span class=pl-v>BaseController</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L2\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"2\"></td>\n          <td id=\"file-posts_controller-rb-LC2\" class=\"blob-code blob-code-inner js-file-line\">  <span class=pl-k>def</span> <span class=pl-en>index</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L3\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"3\"></td>\n          <td id=\"file-posts_controller-rb-LC3\" class=\"blob-code blob-code-inner js-file-line\">    <span class=pl-c1>@posts</span> <span class=pl-c1>=</span> <span class=pl-v>Post</span><span class=pl-kos>.</span><span class=pl-en>live</span><span class=pl-kos>.</span><span class=pl-en>includes</span><span class=pl-kos>(</span><span class=pl-pds>:draft</span><span class=pl-kos>)</span><span class=pl-kos>.</span><span class=pl-en>to_a</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L4\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"4\"></td>\n          <td id=\"file-posts_controller-rb-LC4\" class=\"blob-code blob-code-inner js-file-line\">    <span class=pl-c1>@posts</span><span class=pl-kos>.</span><span class=pl-en>map!</span> <span class=pl-kos>{</span> |<span class=pl-s1>post</span>| <span class=pl-s1>post</span><span class=pl-kos>.</span><span class=pl-en>draft?</span> ? <span class=pl-s1>post</span><span class=pl-kos>.</span><span class=pl-en>draft</span><span class=pl-kos>.</span><span class=pl-en>reify</span> : <span class=pl-s1>post</span> <span class=pl-kos>}</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L5\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"5\"></td>\n          <td id=\"file-posts_controller-rb-LC5\" class=\"blob-code blob-code-inner js-file-line\">  <span class=pl-k>end</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L6\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"6\"></td>\n          <td id=\"file-posts_controller-rb-LC6\" class=\"blob-code blob-code-inner js-file-line\">  </td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L7\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"7\"></td>\n          <td id=\"file-posts_controller-rb-LC7\" class=\"blob-code blob-code-inner js-file-line\">  <span class=pl-k>def</span> <span class=pl-en>show</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L8\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"8\"></td>\n          <td id=\"file-posts_controller-rb-LC8\" class=\"blob-code blob-code-inner js-file-line\">    <span class=pl-c1>@post</span> <span class=pl-c1>=</span> <span class=pl-v>Post</span><span class=pl-kos>.</span><span class=pl-en>live</span><span class=pl-kos>.</span><span class=pl-en>find</span><span class=pl-kos>(</span><span class=pl-en>params</span><span class=pl-kos>[</span><span class=pl-pds>:id</span><span class=pl-kos>]</span><span class=pl-kos>)</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L9\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"9\"></td>\n          <td id=\"file-posts_controller-rb-LC9\" class=\"blob-code blob-code-inner js-file-line\">    <span class=pl-c1>@post</span> <span class=pl-c1>=</span> <span class=pl-c1>@post</span><span class=pl-kos>.</span><span class=pl-en>draft</span><span class=pl-kos>.</span><span class=pl-en>reify</span> <span class=pl-k>if</span> <span class=pl-c1>@post</span><span class=pl-kos>.</span><span class=pl-en>draft?</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L10\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"10\"></td>\n          <td id=\"file-posts_controller-rb-LC10\" class=\"blob-code blob-code-inner js-file-line\">  <span class=pl-k>end</span></td>\n        </tr>\n        <tr>\n          <td id=\"file-posts_controller-rb-L11\" class=\"blob-num js-line-number js-code-nav-line-number\" data-line-number=\"11\"></td>\n          <td id=\"file-posts_controller-rb-LC11\" class=\"blob-code blob-code-inner js-file-line\"><span class=pl-k>end</span></td>\n        </tr>\n  </table>\n</div>\n\n\n  </div>\n\n  </div>\n</div>\n\n      </div>\n      <div class=\"gist-meta\">\n        <a href=\"https://gist.github.com/chrisdpeters/8324486e91eaf9e1b1ed/raw/0e84387a2ec5cc7a6b5dc6d743dc837a76cb93e2/posts_controller.rb\" style=\"float:right\">view raw</a>\n        <a href=\"https://gist.github.com/chrisdpeters/8324486e91eaf9e1b1ed#file-posts_controller-rb\">\n          posts_controller.rb\n        </a>\n        hosted with &#10084; by <a href=\"https://github.com\">GitHub</a>\n      </div>\n    </div>\n</div></p>\n<p>Here, <code class=\"language-text\">reify</code> returns a <code class=\"language-text\">Post</code> object with the drafted data loaded in. If any <code class=\"language-text\">Post</code> record is not a draft, then nothing is done.</p>\n<h2>All drafts in one spot</h2>\n<p>Live Editor, using Draftsman under the hood, allows admins to browse to a <em>Drafts</em> section that lists all drafts polymorphically, no matter if the drafts are of content, files, or design elements.</p>\n<p><img src=\"https://files.liveeditorcms.com/files/resources/1561/drafts-screenshot.png\"></p>\n<p>That way, you can review everything that’s in progress and publish or revert multiple drafts at once, no matter the type of content.</p>\n<p>Try doing that in WordPress!</p>\n<h2>Other features of Draftsman</h2>\n<p>There is more to this gem that I won’t cover in this introduction. Some features that I didn’t mention are as follows:</p>\n<ul>\n<li>Does not store drafts for updates that don’t change anything.</li>\n<li>Allows you to specify attributes (by inclusion or exclusion) that must change for a draft to be stored.</li>\n<li><code class=\"language-text\">publish!</code> and <code class=\"language-text\">revert!</code> methods handle any dependent drafts so you don’t end up with orphaned records.</li>\n<li>Allows you to get at every draft, even if the schema has since changed.</li>\n<li>Automatically records who was responsible via your controller. Draftsman calls <code class=\"language-text\">current_user</code> by default if it exists, but you can have it call any method you like.</li>\n<li>Allows you to store arbitrary model-level metadata with each draft (useful for filtering).</li>\n<li>Allows you to store arbitrary controller-level information with each draft (e.g., remote IP, current account ID).</li>\n<li>Stores everything in a single database table by default (generates migration for you), or you can use separate tables for separate models.</li>\n<li>Supports custom draft classes so different models’ drafts can have different behavior.</li>\n<li>Supports custom name for <code class=\"language-text\">draft</code> association.</li>\n<li>Threadsafe.</li>\n</ul>\n<p>For more detailed information, examples, bug reports, etc., visit the <a href=\"https://github.com/liveeditor/draftsman\">Draftsman GitHub repo</a>. Give it a try!</p>","frontmatter":{"title":"Draftsman gem: Add a draft state to your Ruby on Rails and Sinatra relational models","date":"August 18, 2014","description":"Draftsman is my new Ruby gem that lets you create draft versions of your database records. If you're developing a system in need of simple drafts or a publishing approval queue, then Draftsman just might be what you need.\n"},"fields":{"slug":"/draftsman-gem-draft-state-ruby-on-rails-sinatra-relational-models/"}}},"pageContext":{"slug":"/draftsman-gem-draft-state-ruby-on-rails-sinatra-relational-models/","previous":{"fields":{"slug":"/effective-us-page-tips-best-practices-guidelines/"},"frontmatter":{"title":"Effective \"About us\" page tips, best practices, and guidelines"}},"next":{"fields":{"slug":"/simplistic-vs-simple-product-design/"},"frontmatter":{"title":"Simplistic vs. simple in product design"}}}},"staticQueryHashes":["3678401200","63159454"]}