georgi-shinmun 0.4.1 → 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +23 -31
- data/assets/print.css +76 -0
- data/assets/styles.css +91 -0
- data/bin/shinmun +16 -8
- data/config.ru +16 -0
- data/lib/shinmun.rb +1 -4
- data/lib/shinmun/blog.rb +110 -100
- data/lib/shinmun/comment.rb +1 -1
- data/lib/shinmun/handlers.rb +19 -0
- data/lib/shinmun/helpers.rb +1 -1
- data/lib/shinmun/routes.rb +25 -36
- data/templates/404.rhtml +4 -0
- data/templates/_comment_form.rhtml +21 -0
- data/{test/templates → templates}/_comments.rhtml +0 -0
- data/{example/templates → templates}/archive.rhtml +0 -1
- data/{example/templates → templates}/category.rhtml +1 -2
- data/{example/templates → templates}/category.rxml +0 -0
- data/{example/templates → templates}/index.rhtml +0 -1
- data/{example/templates → templates}/index.rxml +0 -0
- data/templates/layout.rhtml +44 -0
- data/{example/templates → templates}/page.rhtml +0 -2
- data/{example/templates → templates}/post.rhtml +6 -21
- data/test/blog_spec.rb +48 -81
- metadata +17 -44
- data/example/Rakefile +0 -41
- data/example/assets/images/favicon.ico +0 -0
- data/example/assets/images/loading.gif +0 -0
- data/example/assets/javascripts/1-jquery.min.js +0 -32
- data/example/assets/javascripts/2-jquery-form.min.js +0 -5
- data/example/assets/javascripts/3-comments.js +0 -45
- data/example/assets/javascripts/4-coderay.js +0 -13
- data/example/assets/print.css +0 -76
- data/example/assets/stylesheets/1-reset.css +0 -23
- data/example/assets/stylesheets/2-typo.css +0 -40
- data/example/assets/stylesheets/3-table.css +0 -23
- data/example/assets/stylesheets/4-article.css +0 -15
- data/example/assets/stylesheets/5-comments.css +0 -20
- data/example/assets/stylesheets/6-diff.css +0 -25
- data/example/assets/stylesheets/7-blog.css +0 -33
- data/example/config.ru +0 -6
- data/example/config/blog.yml +0 -10
- data/example/pages/about.md +0 -7
- data/example/templates/_comment_form.rhtml +0 -90
- data/example/templates/_comments.rhtml +0 -11
- data/example/templates/_pagination.rhtml +0 -10
- data/example/templates/comments.rhtml +0 -1
- data/example/templates/layout.rhtml +0 -82
- data/lib/shinmun/aggregations/delicious.rb +0 -57
- data/lib/shinmun/aggregations/flickr.rb +0 -81
- data/lib/shinmun/post_handler.rb +0 -16
- data/test/templates/archive.rhtml +0 -6
- data/test/templates/category.rhtml +0 -6
- data/test/templates/category.rxml +0 -20
- data/test/templates/index.rhtml +0 -4
- data/test/templates/index.rxml +0 -21
- data/test/templates/layout.rhtml +0 -9
- data/test/templates/page.rhtml +0 -2
- data/test/templates/post.rhtml +0 -3
    
        data/README.md
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            Shinmun - a git-based blog engine
         | 
| 2 | 
            -
             | 
| 2 | 
            +
            =================================
         | 
| 3 3 |  | 
| 4 4 | 
             
            Shinmun is a small git-based blog engine. Write posts in your favorite
         | 
| 5 5 | 
             
            editor, git-push it and serve your blog straight from a repository.
         | 
| @@ -11,7 +11,6 @@ editor, git-push it and serve your blog straight from a repository. | |
| 11 11 | 
             
            * Deploy via [git-push][11]
         | 
| 12 12 | 
             
            * Index, category and archive listings
         | 
| 13 13 | 
             
            * RSS feeds
         | 
| 14 | 
            -
            * Flickr and Delicious aggregations
         | 
| 15 14 | 
             
            * Syntax highlighting provided by [CodeRay][4]
         | 
| 16 15 | 
             
            * AJAX comment system with Markdown preview
         | 
| 17 16 |  | 
| @@ -21,9 +20,9 @@ editor, git-push it and serve your blog straight from a repository. | |
| 21 20 | 
             
            Install the gems:
         | 
| 22 21 |  | 
| 23 22 | 
             
                $ gem sources -a http://gems.github.com
         | 
| 24 | 
            -
                $ gem install rack BlueCloth rubypants coderay  | 
| 23 | 
            +
                $ gem install rack BlueCloth rubypants coderay georgi-git_store georgi-kontrol georgi-shinmun
         | 
| 25 24 |  | 
| 26 | 
            -
            Create a sample blog | 
| 25 | 
            +
            Create a sample blog:
         | 
| 27 26 |  | 
| 28 27 | 
             
                $ shinmun init myblog
         | 
| 29 28 |  | 
| @@ -48,9 +47,7 @@ folder: | |
| 48 47 | 
             
                shinmun post 'The title of the post'
         | 
| 49 48 |  | 
| 50 49 | 
             
            Shinmun will then create a post file in the right place, for example
         | 
| 51 | 
            -
            in `posts/2008/9/the-title-of-the-post.md | 
| 52 | 
            -
            probably open the file, set the category and tags and start writing
         | 
| 53 | 
            -
            your new article.
         | 
| 50 | 
            +
            in `posts/2008/9/the-title-of-the-post.md` and open it with $EDITOR.
         | 
| 54 51 |  | 
| 55 52 |  | 
| 56 53 | 
             
            ### Post Format
         | 
| @@ -141,23 +138,15 @@ An example tree: | |
| 141 138 |  | 
| 142 139 | 
             
            ### Blog configuation
         | 
| 143 140 |  | 
| 144 | 
            -
            Inside `config | 
| 141 | 
            +
            Inside `config.ru` you can set the properties of your blog:
         | 
| 145 142 |  | 
| 146 | 
            -
             | 
| 147 | 
            -
             | 
| 148 | 
            -
             | 
| 149 | 
            -
             | 
| 150 | 
            -
             | 
| 151 | 
            -
             | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
            ### Assets
         | 
| 155 | 
            -
             | 
| 156 | 
            -
            Shinmun serves asset files from your assets directory. Files in the
         | 
| 157 | 
            -
            directories `assets/stylesheets` and `assets/javascripts` will be
         | 
| 158 | 
            -
            served as one file each under the URLs `assets/stylesheets.css` and
         | 
| 159 | 
            -
            `assets/javascripts.css`. You have to name them accordingly like
         | 
| 160 | 
            -
            `1-reset.css` and `2-typo.css` to define the order.
         | 
| 143 | 
            +
                blog.config = {
         | 
| 144 | 
            +
                  :language => 'en',
         | 
| 145 | 
            +
                  :title => "Blog Title",
         | 
| 146 | 
            +
                  :author => "The Author",
         | 
| 147 | 
            +
                  :categories => ["Ruby", "Javascript"],
         | 
| 148 | 
            +
                  :description => "Blog description"
         | 
| 149 | 
            +
                }
         | 
| 161 150 |  | 
| 162 151 |  | 
| 163 152 | 
             
            ### Templates
         | 
| @@ -176,17 +165,20 @@ Layout and templates are rendered by *ERB*.  The layout is defined in | |
| 176 165 | 
             
                  </body>
         | 
| 177 166 | 
             
                 </html>
         | 
| 178 167 |  | 
| 179 | 
            -
            The attributes of a post are accessible  | 
| 180 | 
            -
            template:
         | 
| 168 | 
            +
            The attributes of a post are accessible via the @post variable:
         | 
| 181 169 |  | 
| 182 | 
            -
                <div class="article"> | 
| 170 | 
            +
                <div class="article">
         | 
| 171 | 
            +
                 
         | 
| 172 | 
            +
                  <h1><%= @post.title %></h1>
         | 
| 173 | 
            +
                 
         | 
| 183 174 | 
             
                  <div class="date">
         | 
| 184 | 
            -
                    <%=  | 
| 175 | 
            +
                    <%= human_date @post.date %>
         | 
| 185 176 | 
             
                  </div>
         | 
| 186 | 
            -
             | 
| 187 | 
            -
                  <%= @ | 
| 188 | 
            -
             | 
| 189 | 
            -
                   | 
| 177 | 
            +
                 
         | 
| 178 | 
            +
                  <%= @post.body_html %>
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  ...      
         | 
| 181 | 
            +
             | 
| 190 182 | 
             
                </div>
         | 
| 191 183 |  | 
| 192 184 |  | 
    
        data/assets/print.css
    ADDED
    
    | @@ -0,0 +1,76 @@ | |
| 1 | 
            +
            body {
         | 
| 2 | 
            +
                line-height:1.5;
         | 
| 3 | 
            +
                font-family:"Helvetica Neue", "Lucida Grande", Arial, Verdana, sans-serif;
         | 
| 4 | 
            +
                color:#000;
         | 
| 5 | 
            +
                background:none;
         | 
| 6 | 
            +
                font-size:10pt;
         | 
| 7 | 
            +
            }
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            .container {
         | 
| 10 | 
            +
                background:none;
         | 
| 11 | 
            +
            }
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            h1,h2,h3,h4,h5,h6 {
         | 
| 14 | 
            +
                font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;
         | 
| 15 | 
            +
            }
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            code {
         | 
| 18 | 
            +
                font:.9em "Courier New", Monaco, Courier, monospace;
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            img {
         | 
| 22 | 
            +
                float:left;
         | 
| 23 | 
            +
                margin:1.5em 1.5em 1.5em 0;
         | 
| 24 | 
            +
            }
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            a img {
         | 
| 27 | 
            +
                border:none;
         | 
| 28 | 
            +
            }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            p img.top {
         | 
| 31 | 
            +
                margin-top:0;
         | 
| 32 | 
            +
            }
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            hr {
         | 
| 35 | 
            +
                background:#ccc;
         | 
| 36 | 
            +
                color:#ccc;
         | 
| 37 | 
            +
                width:100%;
         | 
| 38 | 
            +
                height:2px;
         | 
| 39 | 
            +
                border:none;
         | 
| 40 | 
            +
                margin:2em 0;
         | 
| 41 | 
            +
                padding:0;
         | 
| 42 | 
            +
            }
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            blockquote {
         | 
| 45 | 
            +
                font-style:italic;
         | 
| 46 | 
            +
                font-size:.9em;
         | 
| 47 | 
            +
                margin:1.5em;
         | 
| 48 | 
            +
                padding:1em;
         | 
| 49 | 
            +
            }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            .small {
         | 
| 52 | 
            +
                font-size:.9em;
         | 
| 53 | 
            +
            }
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            .large {
         | 
| 56 | 
            +
                font-size:1.1em;
         | 
| 57 | 
            +
            }
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            .quiet {
         | 
| 60 | 
            +
                color:#999;
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            .hide {
         | 
| 64 | 
            +
                display:none;
         | 
| 65 | 
            +
            }
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            a:link,a:visited {
         | 
| 68 | 
            +
                background:transparent;
         | 
| 69 | 
            +
                font-weight:700;
         | 
| 70 | 
            +
                text-decoration:underline;
         | 
| 71 | 
            +
            }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            a:link:after,a:visited:after {
         | 
| 74 | 
            +
                content:" (" attr(href) ") ";
         | 
| 75 | 
            +
                font-size:90%;
         | 
| 76 | 
            +
            }
         | 
    
        data/assets/styles.css
    ADDED
    
    | @@ -0,0 +1,91 @@ | |
| 1 | 
            +
            html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p,
         | 
| 2 | 
            +
            blockquote, pre, a, abbr, acronym, address, del, dfn, img,
         | 
| 3 | 
            +
            q, fieldset, form, label, legend, table,
         | 
| 4 | 
            +
            caption, tbody, tfoot, thead, tr, th, td {
         | 
| 5 | 
            +
                margin: 0; 
         | 
| 6 | 
            +
                padding: 0; 
         | 
| 7 | 
            +
                border: 0; 
         | 
| 8 | 
            +
                font-weight: inherit;
         | 
| 9 | 
            +
                font-style: inherit;
         | 
| 10 | 
            +
                font-size: 100%; 
         | 
| 11 | 
            +
                font-family: inherit;
         | 
| 12 | 
            +
                vertical-align: baseline;
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            body {
         | 
| 16 | 
            +
                line-height: 1.5;
         | 
| 17 | 
            +
                background: #fff;
         | 
| 18 | 
            +
                margin:0.5em 0;
         | 
| 19 | 
            +
                font-size: 80%;
         | 
| 20 | 
            +
                color: #222;
         | 
| 21 | 
            +
                font-family: Arial, sans-serif;
         | 
| 22 | 
            +
            }
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            p {
         | 
| 25 | 
            +
                margin-bottom: 1em;
         | 
| 26 | 
            +
            }
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            a {
         | 
| 29 | 
            +
                color: #444;
         | 
| 30 | 
            +
            }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            a:visited {
         | 
| 33 | 
            +
                color: #444;
         | 
| 34 | 
            +
            }
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
         | 
| 37 | 
            +
                text-decoration: none;
         | 
| 38 | 
            +
            }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            h1 { font-size: 2em; line-height: 1; margin-top: 1em; margin-bottom: 1em; }
         | 
| 41 | 
            +
            h2 { font-size: 1.5em; margin-top: 0.75em; margin-bottom: 0.75em; }
         | 
| 42 | 
            +
            h3 { font-size: 1.3em; line-height: 1; margin-top: 1.7em; margin-bottom: 1em; }
         | 
| 43 | 
            +
            h4 { font-size: 1.2em; line-height: 1.25; margin-top: 1.25em; margin-bottom: 1.25em; }
         | 
| 44 | 
            +
            h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; }
         | 
| 45 | 
            +
            h6 { font-size: 1em; font-weight: bold; }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
            hr { 
         | 
| 48 | 
            +
                height: 1px;
         | 
| 49 | 
            +
                color: #ccc;
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            .container {
         | 
| 53 | 
            +
                width: 600px;
         | 
| 54 | 
            +
                margin: 0 auto;
         | 
| 55 | 
            +
            }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            .article {
         | 
| 58 | 
            +
            }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
            .article h2 {
         | 
| 61 | 
            +
                margin-top:0px;
         | 
| 62 | 
            +
            }
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            .article .date {
         | 
| 65 | 
            +
                color: #666;
         | 
| 66 | 
            +
            }
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            .article .tags a {
         | 
| 69 | 
            +
                color: #69c;
         | 
| 70 | 
            +
                text-decoration: none;
         | 
| 71 | 
            +
            }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            .comments {
         | 
| 74 | 
            +
                margin-bottom: 2em;
         | 
| 75 | 
            +
            }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            .comments .comment, .preview .comment {
         | 
| 78 | 
            +
                margin-top: 2em;
         | 
| 79 | 
            +
                border: 1px solid #ccc;
         | 
| 80 | 
            +
            }
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            .comments .comment .top, .preview .comment .top  {
         | 
| 83 | 
            +
                background: #F0F0F0;
         | 
| 84 | 
            +
                color: #333;
         | 
| 85 | 
            +
                padding: 3px 5px;
         | 
| 86 | 
            +
            }
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            .comments .comment .body, .preview .comment .body {
         | 
| 89 | 
            +
                background: #F8F8F8;
         | 
| 90 | 
            +
                padding: 3px 5px;
         | 
| 91 | 
            +
            }
         | 
    
        data/bin/shinmun
    CHANGED
    
    | @@ -2,21 +2,29 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            require 'shinmun'
         | 
| 4 4 |  | 
| 5 | 
            +
            ENV['RACK_ENV'] = 'production'
         | 
| 6 | 
            +
             | 
| 5 7 | 
             
            case ARGV[0]
         | 
| 6 8 | 
             
            when 'init'
         | 
| 7 9 | 
             
              Shinmun::Blog.init ARGV[1]
         | 
| 8 10 |  | 
| 9 11 | 
             
            when 'post'
         | 
| 10 | 
            -
               | 
| 11 | 
            -
               | 
| 12 | 
            -
               | 
| 13 | 
            -
               | 
| 12 | 
            +
              blog = Shinmun::Blog.new('.')
         | 
| 13 | 
            +
              post = blog.create_post(:title => ARGV[1], :date => Date.today)
         | 
| 14 | 
            +
              path = blog.post_file(post)
         | 
| 15 | 
            +
              
         | 
| 16 | 
            +
              `git checkout master posts`
         | 
| 17 | 
            +
              
         | 
| 18 | 
            +
              exec "#{ENV['EDITOR']} #{path}"
         | 
| 14 19 |  | 
| 15 20 | 
             
            when 'page'
         | 
| 16 | 
            -
               | 
| 17 | 
            -
               | 
| 18 | 
            -
               | 
| 19 | 
            -
             | 
| 21 | 
            +
              blog = Shinmun::Blog.new('.')
         | 
| 22 | 
            +
              post = blog.create_page(:title => ARGV[1])
         | 
| 23 | 
            +
              path = blog.post_file(post)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              `git checkout master pages`
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              exec "#{ENV['EDITOR']} #{path}"
         | 
| 20 28 |  | 
| 21 29 | 
             
            else
         | 
| 22 30 | 
             
              puts "Usage:"
         | 
    
        data/config.ru
    ADDED
    
    | @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            require 'shinmun'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            use Rack::Session::Cookie
         | 
| 4 | 
            +
            use Rack::Reloader
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            blog = Shinmun::Blog.new(File.dirname(__FILE__))
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            blog.config = {
         | 
| 9 | 
            +
              :language => 'en',
         | 
| 10 | 
            +
              :title => "Blog Title",
         | 
| 11 | 
            +
              :author => "The Author",
         | 
| 12 | 
            +
              :categories => ["Ruby", "Javascript"],
         | 
| 13 | 
            +
              :description => "Blog description"
         | 
| 14 | 
            +
            }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            run blog
         | 
    
        data/lib/shinmun.rb
    CHANGED
    
    | @@ -10,11 +10,8 @@ begin; require 'redcloth'; rescue LoadError; end | |
| 10 10 |  | 
| 11 11 | 
             
            require 'shinmun/bluecloth_coderay'
         | 
| 12 12 | 
             
            require 'shinmun/helpers'
         | 
| 13 | 
            +
            require 'shinmun/handlers'
         | 
| 13 14 | 
             
            require 'shinmun/blog'
         | 
| 14 15 | 
             
            require 'shinmun/routes'
         | 
| 15 16 | 
             
            require 'shinmun/post'
         | 
| 16 17 | 
             
            require 'shinmun/comment'
         | 
| 17 | 
            -
            require 'shinmun/post_handler'
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            require 'shinmun/aggregations/delicious'
         | 
| 20 | 
            -
            require 'shinmun/aggregations/flickr'
         | 
    
        data/lib/shinmun/blog.rb
    CHANGED
    
    | @@ -1,104 +1,76 @@ | |
| 1 1 | 
             
            module Shinmun
         | 
| 2 | 
            +
              ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
         | 
| 2 3 |  | 
| 3 4 | 
             
              class Blog < Kontrol::Application
         | 
| 4 | 
            -
                
         | 
| 5 | 
            -
                EXAMPLE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../example')
         | 
| 6 | 
            -
             | 
| 7 5 | 
             
                include Helpers
         | 
| 8 6 |  | 
| 9 | 
            -
                 | 
| 10 | 
            -
             | 
| 11 | 
            -
                %w[ assets comments config posts pages templates ].each do |name|
         | 
| 12 | 
            -
                  define_method(name) { store.root.tree(name) }
         | 
| 13 | 
            -
                end
         | 
| 7 | 
            +
                attr_accessor :config, :store, :posts, :pages
         | 
| 14 8 |  | 
| 15 | 
            -
                %w[ title description language author  | 
| 16 | 
            -
                  define_method(name) { config[ | 
| 9 | 
            +
                %w[ base_path title description language author categories ].each do |name|
         | 
| 10 | 
            +
                  define_method(name) { @config[name.to_sym] }
         | 
| 17 11 | 
             
                end
         | 
| 18 12 |  | 
| 19 13 | 
             
                # Initialize the blog
         | 
| 20 14 | 
             
                def initialize(path)
         | 
| 21 15 | 
             
                  super
         | 
| 22 16 |  | 
| 23 | 
            -
                  @ | 
| 24 | 
            -
             | 
| 25 | 
            -
                   | 
| 26 | 
            -
             | 
| 27 | 
            -
                   | 
| 28 | 
            -
                    @store = GitStore::FileStore.new(path)
         | 
| 29 | 
            -
                  end
         | 
| 30 | 
            -
                  
         | 
| 31 | 
            -
                  @store.load
         | 
| 32 | 
            -
                  
         | 
| 33 | 
            -
                  @repo = Grit::Repo.new(path) if defined?(Grit)
         | 
| 34 | 
            -
                  
         | 
| 35 | 
            -
                  Thread.start do
         | 
| 36 | 
            -
                    loop do
         | 
| 37 | 
            -
                      load_aggregations
         | 
| 38 | 
            -
                      sleep 300
         | 
| 39 | 
            -
                    end
         | 
| 40 | 
            -
                  end
         | 
| 17 | 
            +
                  @config = {}
         | 
| 18 | 
            +
                  @store = GitStore.new(path)
         | 
| 19 | 
            +
                  @store.handler['md'] = PostHandler.new
         | 
| 20 | 
            +
                  @store.handler['rhtml'] = ERBHandler.new
         | 
| 21 | 
            +
                  @store.handler['rxml'] = ERBHandler.new
         | 
| 41 22 | 
             
                end
         | 
| 42 23 |  | 
| 43 | 
            -
                def self.init( | 
| 44 | 
            -
                   | 
| 45 | 
            -
                  Dir. | 
| 46 | 
            -
                  FileUtils.cp_r EXAMPLE_DIR + '/.', '.'
         | 
| 47 | 
            -
                  `git init`
         | 
| 48 | 
            -
                  `git add .`
         | 
| 49 | 
            -
                  `git commit -m 'init'`
         | 
| 50 | 
            -
                end
         | 
| 24 | 
            +
                def self.init(path)
         | 
| 25 | 
            +
                  path = File.expand_path(path)
         | 
| 26 | 
            +
                  Dir.mkdir(path)
         | 
| 51 27 |  | 
| 52 | 
            -
             | 
| 53 | 
            -
                   | 
| 54 | 
            -
             | 
| 28 | 
            +
                  FileUtils.cp_r "#{ROOT}/assets", path
         | 
| 29 | 
            +
                  FileUtils.cp_r "#{ROOT}/templates", path
         | 
| 30 | 
            +
                  FileUtils.cp "#{ROOT}/config.ru", path
         | 
| 55 31 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
                   | 
| 58 | 
            -
             | 
| 32 | 
            +
                  Dir.mkdir("#{path}/posts")
         | 
| 33 | 
            +
                  Dir.mkdir("#{path}/pages")
         | 
| 34 | 
            +
                  Dir.mkdir("#{path}/comments")
         | 
| 35 | 
            +
                  Dir.mkdir("#{path}/public")
         | 
| 59 36 |  | 
| 60 | 
            -
             | 
| 61 | 
            -
                  store.refresh!      
         | 
| 62 | 
            -
                  super
         | 
| 63 | 
            -
                end
         | 
| 37 | 
            +
                  FileUtils.ln_s("../assets", "#{path}/public/assets")
         | 
| 64 38 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
                     | 
| 39 | 
            +
                  Dir.chdir(path) do
         | 
| 40 | 
            +
                    `git init`
         | 
| 41 | 
            +
                    `git add .`
         | 
| 42 | 
            +
                    `git commit -m 'init'`
         | 
| 68 43 | 
             
                  end
         | 
| 69 44 | 
             
                end
         | 
| 70 45 |  | 
| 71 | 
            -
                def  | 
| 72 | 
            -
                   | 
| 46 | 
            +
                def load_template(file)
         | 
| 47 | 
            +
                  store['templates/' + file]
         | 
| 73 48 | 
             
                end
         | 
| 74 49 |  | 
| 75 | 
            -
                def  | 
| 76 | 
            -
                   | 
| 50 | 
            +
                def render(name, vars = {})
         | 
| 51 | 
            +
                  super(name, vars.merge(:blog => self))
         | 
| 77 52 | 
             
                end
         | 
| 78 53 |  | 
| 79 | 
            -
                 | 
| 80 | 
            -
             | 
| 81 | 
            -
                  posts_by_date.select { |p| p.year == year and p.month == month }
         | 
| 54 | 
            +
                def pages      
         | 
| 55 | 
            +
                  store.tree('pages').values
         | 
| 82 56 | 
             
                end
         | 
| 83 57 |  | 
| 84 | 
            -
                 | 
| 85 | 
            -
             | 
| 86 | 
            -
                  return [] if tags.nil? or tags.empty?
         | 
| 87 | 
            -
                  tags = tags.split(',').map { |t| t.strip } if tags.is_a?(String)
         | 
| 88 | 
            -
                  posts.select do |post|
         | 
| 89 | 
            -
                    tags.any? do |tag| 
         | 
| 90 | 
            -
                      post.tag_list.include?(tag)
         | 
| 91 | 
            -
                    end
         | 
| 92 | 
            -
                  end
         | 
| 58 | 
            +
                def posts
         | 
| 59 | 
            +
                  store.tree('posts').values.sort_by { |post| post.date.to_s }.reverse
         | 
| 93 60 | 
             
                end
         | 
| 94 61 |  | 
| 95 | 
            -
                 | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 62 | 
            +
                def call(env)
         | 
| 63 | 
            +
                  if ENV['RACK_ENV'] == 'production'
         | 
| 64 | 
            +
                    store.load if store.changed?
         | 
| 65 | 
            +
                  else
         | 
| 66 | 
            +
                    store.load(true)
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                  
         | 
| 69 | 
            +
                  super
         | 
| 98 70 | 
             
                end
         | 
| 99 71 |  | 
| 100 | 
            -
                def  | 
| 101 | 
            -
                   | 
| 72 | 
            +
                def url
         | 
| 73 | 
            +
                  "http://#{request.host}"
         | 
| 102 74 | 
             
                end
         | 
| 103 75 |  | 
| 104 76 | 
             
                def symbolize_keys(hash)      
         | 
| @@ -112,36 +84,54 @@ module Shinmun | |
| 112 84 | 
             
                  store.transaction(message, &block)
         | 
| 113 85 | 
             
                end
         | 
| 114 86 |  | 
| 115 | 
            -
                 | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
                   | 
| 87 | 
            +
                def post_file(post)
         | 
| 88 | 
            +
                  'posts' + post_path(post) + '.' + post.type
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def page_file(post)
         | 
| 92 | 
            +
                  'pages' + page_path(post) + '.' + post.type
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def comment_file(post)
         | 
| 96 | 
            +
                  'comments/' + post_path(post) + '.yml'
         | 
| 121 97 | 
             
                end
         | 
| 122 98 |  | 
| 123 | 
            -
                def  | 
| 124 | 
            -
                   | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 99 | 
            +
                def create_post(attr)
         | 
| 100 | 
            +
                  post = Post.new(attr)
         | 
| 101 | 
            +
                  path = post_file(post)
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                  transaction "create post `#{post.title}'" do
         | 
| 104 | 
            +
                    store[path] = post
         | 
| 128 105 | 
             
                  end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  post
         | 
| 129 108 | 
             
                end
         | 
| 130 109 |  | 
| 131 | 
            -
                def  | 
| 132 | 
            -
                   | 
| 133 | 
            -
             | 
| 110 | 
            +
                def create_page(attr)
         | 
| 111 | 
            +
                  post = Post.new(attr)
         | 
| 112 | 
            +
                  path = page_file(post)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  transaction "create page `#{post.title}'" do
         | 
| 115 | 
            +
                    store[path] = post
         | 
| 134 116 | 
             
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  post
         | 
| 135 119 | 
             
                end
         | 
| 136 120 |  | 
| 137 | 
            -
                def comments_for( | 
| 138 | 
            -
                   | 
| 121 | 
            +
                def comments_for(post)
         | 
| 122 | 
            +
                  store[comment_file post] || []
         | 
| 139 123 | 
             
                end
         | 
| 140 124 |  | 
| 141 | 
            -
                def  | 
| 142 | 
            -
                   | 
| 143 | 
            -
             | 
| 125 | 
            +
                def create_comment(post, params)
         | 
| 126 | 
            +
                  path = comment_file(post)
         | 
| 127 | 
            +
                  comments = comments_for(post)
         | 
| 128 | 
            +
                  comment = Comment.new(params)
         | 
| 129 | 
            +
                  
         | 
| 130 | 
            +
                  transaction "new comment for `#{post.title}'" do
         | 
| 131 | 
            +
                    store[path] = comments + [comment]
         | 
| 144 132 | 
             
                  end
         | 
| 133 | 
            +
                  
         | 
| 134 | 
            +
                  comment
         | 
| 145 135 | 
             
                end
         | 
| 146 136 |  | 
| 147 137 | 
             
                def find_page(name)
         | 
| @@ -149,23 +139,43 @@ module Shinmun | |
| 149 139 | 
             
                end
         | 
| 150 140 |  | 
| 151 141 | 
             
                def find_post(year, month, name)
         | 
| 152 | 
            -
                   | 
| 142 | 
            +
                  posts.find { |p| p.year == year and p.month == month and p.name == name }
         | 
| 153 143 | 
             
                end
         | 
| 154 144 |  | 
| 155 145 | 
             
                def find_category(permalink)
         | 
| 156 | 
            -
                  name = categories.find { |name| urlify(name) == permalink } | 
| 157 | 
            -
                   | 
| 158 | 
            -
                  { :name => name, | 
| 146 | 
            +
                  name = categories.find { |name| urlify(name) == permalink }
         | 
| 147 | 
            +
                  
         | 
| 148 | 
            +
                  { :name => name,
         | 
| 149 | 
            +
                    :posts => posts.select { |p| p.category == name },
         | 
| 150 | 
            +
                    :permalink => permalink }
         | 
| 151 | 
            +
                end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                def recent_posts
         | 
| 154 | 
            +
                  posts[0, 20]
         | 
| 159 155 | 
             
                end
         | 
| 160 156 |  | 
| 161 | 
            -
                 | 
| 162 | 
            -
             | 
| 163 | 
            -
                   | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 157 | 
            +
                # Return all posts for a given month.
         | 
| 158 | 
            +
                def posts_for_month(year, month)
         | 
| 159 | 
            +
                  posts.select { |p| p.year == year and p.month == month }
         | 
| 160 | 
            +
                end
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                # Return all posts with any of given tags.
         | 
| 163 | 
            +
                def posts_with_tags(tags)
         | 
| 164 | 
            +
                  return [] if tags.nil? or tags.empty?
         | 
| 165 | 
            +
                  tags = tags.split(',').map { |t| t.strip } if tags.is_a?(String)
         | 
| 166 | 
            +
                  
         | 
| 167 | 
            +
                  posts.select do |post|
         | 
| 168 | 
            +
                    tags.any? do |tag| 
         | 
| 169 | 
            +
                      post.tag_list.include?(tag)
         | 
| 170 | 
            +
                    end
         | 
| 166 171 | 
             
                  end
         | 
| 167 172 | 
             
                end
         | 
| 168 | 
            -
             | 
| 169 | 
            -
             | 
| 173 | 
            +
             | 
| 174 | 
            +
                # Return all archives as tuples of [year, month].
         | 
| 175 | 
            +
                def archives
         | 
| 176 | 
            +
                  posts.map { |p| [p.year, p.month] }.uniq.sort
         | 
| 177 | 
            +
                end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
              end
         | 
| 170 180 |  | 
| 171 181 | 
             
            end
         |