shinmun 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ });