shinmun 0.1

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.
Files changed (74) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +18 -0
  3. data/README.md +249 -0
  4. data/Rakefile +60 -0
  5. data/bin/shinmun +12 -0
  6. data/example/posts/2008/9/example.md +19 -0
  7. data/example/posts/blog.yml +10 -0
  8. data/example/posts/uuid.state +3 -0
  9. data/example/public/controllers/comments.php +56 -0
  10. data/example/public/images/loading.gif +0 -0
  11. data/example/public/javascripts/comments.js +60 -0
  12. data/example/public/javascripts/highlight.js +505 -0
  13. data/example/public/javascripts/images/bg-fill.png +0 -0
  14. data/example/public/javascripts/images/bg.png +0 -0
  15. data/example/public/javascripts/images/blockquote.png +0 -0
  16. data/example/public/javascripts/images/bold.png +0 -0
  17. data/example/public/javascripts/images/code.png +0 -0
  18. data/example/public/javascripts/images/h1.png +0 -0
  19. data/example/public/javascripts/images/hr.png +0 -0
  20. data/example/public/javascripts/images/img.png +0 -0
  21. data/example/public/javascripts/images/italic.png +0 -0
  22. data/example/public/javascripts/images/link.png +0 -0
  23. data/example/public/javascripts/images/ol.png +0 -0
  24. data/example/public/javascripts/images/redo.png +0 -0
  25. data/example/public/javascripts/images/separator.png +0 -0
  26. data/example/public/javascripts/images/ul.png +0 -0
  27. data/example/public/javascripts/images/undo.png +0 -0
  28. data/example/public/javascripts/images/wmd-on.png +0 -0
  29. data/example/public/javascripts/images/wmd.png +0 -0
  30. data/example/public/javascripts/jquery-form.js +869 -0
  31. data/example/public/javascripts/jquery.js +3383 -0
  32. data/example/public/javascripts/languages/1c.js +82 -0
  33. data/example/public/javascripts/languages/axapta.js +52 -0
  34. data/example/public/javascripts/languages/bash.js +80 -0
  35. data/example/public/javascripts/languages/diff.js +64 -0
  36. data/example/public/javascripts/languages/dos.js +33 -0
  37. data/example/public/javascripts/languages/dynamic.js +460 -0
  38. data/example/public/javascripts/languages/ini.js +36 -0
  39. data/example/public/javascripts/languages/javascript.js +38 -0
  40. data/example/public/javascripts/languages/lisp.js +86 -0
  41. data/example/public/javascripts/languages/mel.js +50 -0
  42. data/example/public/javascripts/languages/profile.js +50 -0
  43. data/example/public/javascripts/languages/renderman.js +71 -0
  44. data/example/public/javascripts/languages/smalltalk.js +53 -0
  45. data/example/public/javascripts/languages/sql.js +50 -0
  46. data/example/public/javascripts/languages/static.js +175 -0
  47. data/example/public/javascripts/languages/vbscript.js +25 -0
  48. data/example/public/javascripts/languages/www.js +245 -0
  49. data/example/public/javascripts/prettyDate.js +36 -0
  50. data/example/public/javascripts/showdown.js +421 -0
  51. data/example/public/javascripts/template.js +165 -0
  52. data/example/public/javascripts/wmd-base.js +1799 -0
  53. data/example/public/javascripts/wmd-plus.js +311 -0
  54. data/example/public/javascripts/wmd.js +73 -0
  55. data/example/public/stylesheets/grid.css +243 -0
  56. data/example/public/stylesheets/grid.png +0 -0
  57. data/example/public/stylesheets/highlight/ascetic.css +38 -0
  58. data/example/public/stylesheets/highlight/dark.css +96 -0
  59. data/example/public/stylesheets/highlight/default.css +91 -0
  60. data/example/public/stylesheets/highlight/far.css +95 -0
  61. data/example/public/stylesheets/highlight/idea.css +75 -0
  62. data/example/public/stylesheets/highlight/sunburst.css +112 -0
  63. data/example/public/stylesheets/highlight/zenburn.css +108 -0
  64. data/example/public/stylesheets/print.css +76 -0
  65. data/example/public/stylesheets/reset.css +45 -0
  66. data/example/public/stylesheets/style.css +141 -0
  67. data/example/public/stylesheets/typography.css +59 -0
  68. data/example/templates/feed.rxml +21 -0
  69. data/example/templates/layout.rhtml +54 -0
  70. data/example/templates/page.rhtml +4 -0
  71. data/example/templates/post.rhtml +57 -0
  72. data/example/templates/posts.rhtml +10 -0
  73. data/lib/shinmun.rb +420 -0
  74. metadata +151 -0
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *~
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2008 Matthias Georgi <http://www.matthias-georgi.de>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,249 @@
1
+ Shinmun, a small and beautiful blog engine
2
+ ==========================================
3
+
4
+ ### Intro
5
+
6
+ Shinmun is a **minimalist blog engine**. You just write posts as text files,
7
+ render them to static files and push your blog to your server.
8
+
9
+ This allows you to write posts in your favorite editor like Emacs or
10
+ VI and use a VCS like git.
11
+
12
+ Your layout can be customized by set of *ERB templates*. These
13
+ templates have access to `Post` objects and *helper methods* so that
14
+ anybody who knows *Rails* should feel comfortable with it.
15
+
16
+ Shinmun has some common features of blog engines like:
17
+
18
+ * Index summary page
19
+ * Category summary page
20
+ * Archive pages for each month
21
+ * RSS feeds for index and category pages
22
+ * AJAX comment system with PHP JSON file storage
23
+ * Integration of the WMD-Markdown Editor for comments
24
+
25
+ ### Quickstart
26
+
27
+ Install the gem by typing:
28
+ gem install shinmun
29
+
30
+ Issue the following commands and the output will go to the public
31
+ folder:
32
+
33
+ cd example
34
+ ../bin/shinmun
35
+
36
+ ### Writing Posts
37
+
38
+ Posts can be created by using the `shinmun` command inside your blog folder:
39
+
40
+ shinmun new 'The title of the post'
41
+
42
+ Shinmun will then create a post file in the right place, for example
43
+ in `posts/2008/9/the-title-of-the-post.md`. After creating you will
44
+ probably open the file, set the category and start writing your new
45
+ article.
46
+
47
+ After finishing your post, you may just run `shinmun` without arguments
48
+ and the output will be rendered to the *public* folder.
49
+
50
+
51
+ ### Post Format
52
+
53
+ Each blog post is just a text file with an optional header section and
54
+ a markup body, which are separated by a newline.
55
+
56
+ The **first line** of the header should start with 3 dashes as usual
57
+ for a YAML document.
58
+
59
+ The **first and the second line** of the body becomes the title of the
60
+ post.
61
+
62
+ The header may have following attributes:
63
+
64
+ * `date`: post will show up in blog page and archive pages
65
+ * `category`: post will show up in the defined category page
66
+ * `guid`: will be set automatically by Shinmun
67
+
68
+ Posts without a date are by definition static pages.
69
+
70
+ Example post:
71
+
72
+ <pre>
73
+
74
+ ---
75
+ category: Ruby
76
+ date: 2008-09-05
77
+ guid: 7ad04f10-5dd6-012b-b53c-001a92975b89
78
+
79
+ BlueCloth, a Markdown library
80
+ =============================
81
+
82
+ This is the summary, which is by definition the first paragraph of the
83
+ article. The summary shows up in category listings or the index listing.
84
+
85
+ </pre>
86
+
87
+ The guid should never change, as it will be you used for identifying
88
+ posts for comments.
89
+
90
+
91
+ ### Directory layout
92
+
93
+ * All your **posts** reside in the `posts` folder sorted by year/month.
94
+
95
+ * All the **output** will be rendered to the `public` folder.
96
+
97
+ * **Template** files are in the `templates` folder.
98
+
99
+ * The **properties of your blog** defined in `posts/blog.yml`
100
+
101
+ * **Static files** should be put into the directories `public/images`,
102
+ `public/stylesheets`, `public/javascripts`.
103
+
104
+ * Archive pages will be rendered to files like `public/2008/9/index.html`.
105
+
106
+ * Category pages will be rendered to files like `public/categories/ruby.html`.
107
+
108
+ * The *home page* of your blog will go to `public/index.html`.
109
+
110
+ An example tree:
111
+
112
+ + posts
113
+ + blog.yml
114
+ + about.md
115
+ + 2007
116
+ + 2008
117
+ + 9
118
+ + my-article.md
119
+
120
+ + public
121
+ + index.html
122
+ + about.html
123
+ + categories
124
+ + emacs.html
125
+ + ruby.html
126
+ + 2007
127
+ + 2008
128
+ + 9
129
+ + my-article.html
130
+ + images
131
+ + stylesheets
132
+ + javascripts
133
+
134
+ + templates
135
+ + feed.rxml
136
+ + layout.rhtml
137
+ + page.rhtml
138
+ + post.rhtml
139
+ + posts.rhtml
140
+
141
+
142
+ ### Layout
143
+
144
+ Layout and templates are rendered by *ERB*. The layout is defined in
145
+ `layout.rhtml`. The content will be provided in the variable
146
+ `@content`. A minimal example:
147
+
148
+ <html>
149
+ <head>
150
+ <title><%= @blog_title %></title>
151
+ <%= stylesheet_link_tag 'style' %>
152
+ </head>
153
+ <body>
154
+ <%= @content %>
155
+ </body>
156
+ </html>
157
+
158
+
159
+ ### Helpers
160
+
161
+ There are also helper methods, which work the same way like the *Rails*
162
+ helpers. The most important ones are these:
163
+
164
+ * `stylesheet_link_tag(*names)` links a stylesheet with a timestamp
165
+
166
+ * `javascript_tag(*names)` includes a javascript with a timestamp
167
+
168
+ * `image_tag(src, options = {})` renders an image tag
169
+
170
+ * `link_to(text, path, options = {})` renders a link
171
+
172
+ Stylesheets, javascripts and images should be included by using theses
173
+ helpers. The helper methods will include a timestamp of the
174
+ modification time as `querystring`, so that the browser will fetch the
175
+ new resource if it has been changed.
176
+
177
+
178
+ ### Post Template
179
+
180
+ The attributes of a post are accessible as instance variables in a template:
181
+
182
+ <div class="article">
183
+
184
+ <div class="date">
185
+ <%= date @date %>
186
+ </div>
187
+
188
+ <h2><%= @title %></h2>
189
+
190
+ <%= @body %>
191
+
192
+ <h3>Comments</h3>
193
+
194
+ <!-- Here you may put my commenting system -->
195
+ </div>
196
+
197
+
198
+
199
+ ### RSS Feeds
200
+
201
+ Feeds will be rendered by one *ERB template*. Some of the variables
202
+ have been read from the `blog.yml`, like `@blog_title`, other variables
203
+ have been determined by the engine like `@posts` and `@category`.
204
+
205
+ <?xml version="1.0" encoding="utf-8"?>
206
+ <rss version="2.0">
207
+ <channel>
208
+ <title><%= @category ? @blog_title + ' - ' + @category : @blog_title %></title>
209
+ <link><%= @blog_url %></link>
210
+ <description><%= @category ? 'Category ' + @category : @blog_description %></description>
211
+ <language><%= @blog_language %></language>
212
+ <copyright><%= @blog_author %></copyright>
213
+ <pubDate><%= rfc822 Time.now %></pubDate>
214
+ <% for post in @posts %>
215
+ <item>
216
+ <title><%= post.title %></title>
217
+ <description><%= post.text_summary %></description>
218
+ <link><%= post.link %></link>
219
+ <author><%= @blog_author %></author>
220
+ <guid><%= post.guid %></guid>
221
+ <pubDate><%= rfc822 post.date %></pubDate>
222
+ </item>
223
+ <% end %>
224
+ </channel>
225
+ </rss>
226
+
227
+ ### Commenting System
228
+
229
+ As I am not willing to build up a whole Rails stack for a single blog,
230
+ I was looking for a simple storage for comments. I really like the
231
+ JSON format. It works seamlessly with Javascript libraries and can be
232
+ serialized and deserialized from almost any language.
233
+
234
+ Read about my [lightweight commenting system][2].
235
+
236
+
237
+ ### Download
238
+
239
+ Simply install the gem:
240
+
241
+ gem install shinmun
242
+
243
+
244
+ Download or fork the package at my [github repository][1]
245
+
246
+
247
+
248
+ [1]: http://github.com/georgi/shinmun/tree/master
249
+ [2]: commenting-system-with-lightweight-json-store.html
data/Rakefile ADDED
@@ -0,0 +1,60 @@
1
+ require 'rubygems'
2
+
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/rdoctask'
6
+ require 'rake/packagetask'
7
+ require 'rake/gempackagetask'
8
+ require 'fileutils'
9
+
10
+ spec = Gem::Specification.new do |s|
11
+ s.name = "shinmun"
12
+ s.version = `git describe`.strip.sub(/-.*/, '')
13
+ s.platform = Gem::Platform::RUBY
14
+ s.summary = "a small blog engine"
15
+
16
+ s.description = <<-EOF
17
+ Shinmun is a blog engine, which renders text files using a markup
18
+ language like Markdown and a set of templates into static web
19
+ pages. It supports Categories, Archives and RSS Feeds. Commenting can
20
+ be done with some Javascript, PHP and a flat file JSON store.
21
+ EOF
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.bindir = 'bin'
25
+ s.executables << 'shinmun'
26
+ s.require_path = 'lib'
27
+ s.add_dependency 'uuid', '>=2.0.0'
28
+ s.add_dependency 'BlueCloth', '>=1.0.0'
29
+ s.add_dependency 'rubypants', '>=0.2.0'
30
+ s.has_rdoc = true
31
+ s.extra_rdoc_files = ['README.md']
32
+
33
+ s.author = 'Matthias Georgi'
34
+ s.email = 'matti.georgi@gmail.com'
35
+ s.homepage = 'http://shinmun.rubyforge.org'
36
+ s.rubyforge_project = 'shinmun'
37
+ end
38
+
39
+ Rake::GemPackageTask.new(spec) do |p|
40
+ p.gem_spec = spec
41
+ p.need_tar = false
42
+ p.need_zip = false
43
+ end
44
+
45
+
46
+ desc "Generate RDoc documentation"
47
+ Rake::RDocTask.new(:rdoc) do |rdoc|
48
+ rdoc.options << '--line-numbers' << '--inline-source' <<
49
+ '--main' << 'README' <<
50
+ '--title' << 'Shinmun Documentation' <<
51
+ '--charset' << 'utf-8'
52
+ rdoc.rdoc_dir = "doc"
53
+ rdoc.rdoc_files.include 'README.md'
54
+ rdoc.rdoc_files.include('lib/shinmun.rb')
55
+ end
56
+
57
+
58
+ task :push => [:rdoc] do
59
+ sh "rsync -avz doc/ mgeorgi@rack.rubyforge.org:/var/www/gforge-projects/shinmun"
60
+ end
data/bin/shinmun ADDED
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'shinmun'
4
+
5
+ blog = Shinmun::Blog.new
6
+
7
+ case ARGV[0]
8
+ when 'new'
9
+ blog.create_post(ARGV[1])
10
+ else
11
+ blog.write_all
12
+ end
@@ -0,0 +1,19 @@
1
+ ---
2
+ category: Ruby
3
+ guid: 72ece880-5e32-012b-362f-001a92975b89
4
+ date: 2008-09-01
5
+
6
+ Example post
7
+ ============
8
+
9
+ This is the first paragraph. Below is a code example:
10
+
11
+ # Return the first paragraph of rendered html.
12
+ def summary
13
+ body.split("\n\n")[0]
14
+ end
15
+
16
+ # Return the first paragraph of source text.
17
+ def text_summary
18
+ src.split("\n\n")[1]
19
+ end
@@ -0,0 +1,10 @@
1
+ blog_title: The title of the blog
2
+ blog_description: Blog description
3
+ blog_language: en
4
+ blog_author: The author
5
+ blog_url: http://www.example.com
6
+ categories:
7
+ - Ruby
8
+ - Emacs
9
+
10
+
@@ -0,0 +1,3 @@
1
+ mac_addr: "00:1a:92:97:5b:89"
2
+ sequence: "0x3630"
3
+ last_clock: "0x2b5e3272ece3d8"
@@ -0,0 +1,56 @@
1
+ <?php
2
+
3
+ $guid_pattern = "/^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$/";
4
+ $req = $_REQUEST;
5
+ $guid = $req['guid'];
6
+
7
+ preg_match($guid_pattern, $guid) or die("invalid guid");
8
+
9
+ $file = 'comments/' . $guid;
10
+
11
+ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
12
+
13
+ // create a comment record
14
+ $record = array(date('Y-m-d H:i:s'),
15
+ strip_tags(stripslashes($req['name'])),
16
+ strip_tags(stripslashes($req['website'])),
17
+ strip_tags(stripslashes($req['text'])));
18
+
19
+ // encode as json string
20
+ $json = json_encode($record) . "\n";
21
+
22
+ // open the comment file for appending
23
+ $fp = fopen($file, "a");
24
+
25
+ // acquire a write lock
26
+ flock($fp, LOCK_EX);
27
+
28
+ // append the json line
29
+ fwrite($fp, $json);
30
+
31
+ // release lock
32
+ flock($fp, LOCK_UN);
33
+
34
+ // close file and release lock
35
+ fclose($fp);
36
+ }
37
+
38
+ if (file_exists($file)) {
39
+
40
+ // open the comment file for reading
41
+ $fp = fopen($file, "r");
42
+
43
+ // acquire a read lock
44
+ flock($fp, LOCK_SH);
45
+
46
+ // read whole file and print it out
47
+ echo fread($fp, filesize($file));
48
+
49
+ // release lock
50
+ flock($fp, LOCK_UN);
51
+
52
+ // close file
53
+ fclose($fp);
54
+ }
55
+
56
+ ?>
Binary file
@@ -0,0 +1,60 @@
1
+ $('.comment-form form').ajaxForm({
2
+ url: Blog.root + 'controllers/comments.php',
3
+ type: 'POST',
4
+ resetForm: true,
5
+ beforeSubmit: function(values) {
6
+ if (values[1].value && values[3].value) {
7
+ $('.comment-form-loading').show();
8
+ return true;
9
+ }
10
+ else {
11
+ alert('Please enter name and text!');
12
+ return false;
13
+ }
14
+ },
15
+ success: function(data) {
16
+ $('.comment-form-loading').hide();
17
+ renderComments(data);
18
+ }
19
+ });
20
+
21
+ function renderComments(data) {
22
+ var converter = new Showdown.converter();
23
+ var lines = data.split("\n");
24
+ var comments = [];
25
+ for (var i = 0; i < lines.length; i++) {
26
+ var row = eval(lines[i]);
27
+ if (row) {
28
+ comments.push({
29
+ time: row[0],
30
+ name: row[1],
31
+ website: row[2],
32
+ text: converter.makeHtml(row[3])
33
+ });
34
+ }
35
+ }
36
+ $('.comments-loading').hide();
37
+ $('.comments').expand(Template.comments, { comment: comments });
38
+ $('.comments a').prettyDate();
39
+ }
40
+
41
+ function loadComments() {
42
+ $('.comments-loading').show();
43
+ $.ajax({
44
+ type: "GET",
45
+ url: Blog.root + 'controllers/comments.php',
46
+ data: {
47
+ guid: Blog.guid
48
+ },
49
+ complete: function(response) {
50
+ renderComments(response.responseText);
51
+ }
52
+ });
53
+ }
54
+
55
+ $(function() {
56
+ if ($('.comments').length > 0) {
57
+ Template.comments = new Template('comments-template');
58
+ loadComments();
59
+ }
60
+ });