baron 1.0.3 → 1.0.4
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/VERSION +1 -1
- data/baron.gemspec +32 -23
- data/lib/baron.rb +15 -13
- data/spec/baron_article_spec.rb +1 -1
- data/spec/baron_blog_engine_spec.rb +22 -20
- data/spec/baron_spec.rb +10 -22
- data/spec/sample_data/.gitignore +5 -0
- data/spec/sample_data/Gemfile +1 -1
- data/spec/sample_data/README.md +243 -0
- data/spec/sample_data/Rakefile +1 -1
- data/spec/sample_data/articles/favorites/1916-01-01-the-road-not-taken.txt +26 -0
- data/spec/sample_data/articles/north of boston/1914-01-01-the-pasture.txt +10 -0
- data/spec/sample_data/articles/north of boston/1914-01-02-mending-wall.txt +48 -0
- data/spec/sample_data/articles/north of boston/1914-01-03-the-death-of-the-hired-man.txt +211 -0
- data/spec/sample_data/articles/north of boston/1914-01-04-the-mountain.txt +121 -0
- data/spec/sample_data/articles/north of boston/1914-01-05-A-Hundred-callers.txt +196 -0
- data/spec/sample_data/articles/poems/1909-01-02-If.txt b/data/spec/sample_data/articles/other → authors/1909-01-02-If.txt +0 -0
- data/spec/sample_data/config.ru +63 -29
- data/spec/sample_data/images/robert-frost-small.png +0 -0
- data/spec/sample_data/images/robert-frost.png +0 -0
- data/spec/sample_data/pages/about.rhtml +9 -14
- data/spec/sample_data/resources/redirects.txt +1 -29
- data/spec/sample_data/resources/robots.txt +4 -1
- data/spec/sample_data/themes/typography/css/app.css +58 -0
- data/spec/sample_data/themes/{test → typography}/css/bootstrap-responsive.css +0 -0
- data/spec/sample_data/themes/{test → typography}/css/bootstrap-responsive.min.css +0 -0
- data/spec/sample_data/themes/{test → typography}/css/bootstrap.css +0 -0
- data/spec/sample_data/themes/{test → typography}/css/bootstrap.min.css +0 -0
- data/spec/sample_data/themes/typography/img/github.png +0 -0
- data/spec/sample_data/themes/{test → typography}/img/glyphicons-halflings-white.png +0 -0
- data/spec/sample_data/themes/{test → typography}/img/glyphicons-halflings.png +0 -0
- data/spec/sample_data/{images → themes/typography/img}/instagram.png +0 -0
- data/spec/sample_data/themes/typography/js/bootstrap.js +2159 -0
- data/spec/sample_data/themes/typography/js/bootstrap.min.js +6 -0
- data/spec/sample_data/themes/typography/js/image_alt.js +12 -0
- data/spec/sample_data/themes/typography/js/read_later.js +14 -0
- data/spec/sample_data/themes/typography/templates/archives.rhtml +18 -0
- data/spec/sample_data/themes/typography/templates/article.rhtml +14 -0
- data/spec/sample_data/themes/typography/templates/category.rhtml +17 -0
- data/spec/sample_data/themes/typography/templates/error.rhtml +3 -0
- data/spec/sample_data/themes/typography/templates/home.rhtml +28 -0
- data/spec/sample_data/themes/typography/templates/layout.rhtml +141 -0
- data/spec/spec_helper.rb +1 -1
- metadata +32 -23
- data/spec/sample_data/articles/2012-11-09-sample-post.txt +0 -11
- data/spec/sample_data/articles/poems/1916-01-01-the-road-not-taken.txt +0 -26
- data/spec/sample_data/images/import-csv-file-1.png +0 -0
- data/spec/sample_data/images/import-csv-file-2.png +0 -0
- data/spec/sample_data/images/import-csv-file-3.png +0 -0
- data/spec/sample_data/themes/test/css/app.css +0 -27
- data/spec/sample_data/themes/test/img/instagram.png +0 -0
- data/spec/sample_data/themes/test/templates/archives.rhtml +0 -14
- data/spec/sample_data/themes/test/templates/article.rhtml +0 -14
- data/spec/sample_data/themes/test/templates/category.rhtml +0 -15
- data/spec/sample_data/themes/test/templates/error.rhtml +0 -11
- data/spec/sample_data/themes/test/templates/home.rhtml +0 -26
- data/spec/sample_data/themes/test/templates/layout.rhtml +0 -90
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.4
|
data/baron.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "baron"
|
8
|
-
s.version = "1.0.
|
8
|
+
s.version = "1.0.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nathan Buggia"]
|
12
|
-
s.date = "2013-03-
|
12
|
+
s.date = "2013-03-18"
|
13
13
|
s.description = "What's in the box: (1) Publish to Heroku using Git (2) Author articles in markdown (3) Article categories (4) Multiple permalink formats supported (5) Redirects (6) Advanced SEO optimizations and Google Analytics/ Webmaster Tools support. Uses Rack, RSpec, Bootstrap, JQuery, Disqus, Thin"
|
14
14
|
s.email = "nbuggia@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,34 +25,43 @@ Gem::Specification.new do |s|
|
|
25
25
|
"spec/baron_article_spec.rb",
|
26
26
|
"spec/baron_blog_engine_spec.rb",
|
27
27
|
"spec/baron_spec.rb",
|
28
|
+
"spec/sample_data/.gitignore",
|
28
29
|
"spec/sample_data/Gemfile",
|
30
|
+
"spec/sample_data/README.md",
|
29
31
|
"spec/sample_data/Rakefile",
|
30
|
-
"spec/sample_data/articles/
|
31
|
-
"spec/sample_data/articles/
|
32
|
-
"spec/sample_data/articles/
|
32
|
+
"spec/sample_data/articles/favorites/1916-01-01-the-road-not-taken.txt",
|
33
|
+
"spec/sample_data/articles/north of boston/1914-01-01-the-pasture.txt",
|
34
|
+
"spec/sample_data/articles/north of boston/1914-01-02-mending-wall.txt",
|
35
|
+
"spec/sample_data/articles/north of boston/1914-01-03-the-death-of-the-hired-man.txt",
|
36
|
+
"spec/sample_data/articles/north of boston/1914-01-04-the-mountain.txt",
|
37
|
+
"spec/sample_data/articles/north of boston/1914-01-05-A-Hundred-callers.txt",
|
38
|
+
"spec/sample_data/articles/other authors/1909-01-02-If.txt",
|
33
39
|
"spec/sample_data/config.ru",
|
34
|
-
"spec/sample_data/images/
|
35
|
-
"spec/sample_data/images/
|
36
|
-
"spec/sample_data/images/import-csv-file-3.png",
|
37
|
-
"spec/sample_data/images/instagram.png",
|
40
|
+
"spec/sample_data/images/robert-frost-small.png",
|
41
|
+
"spec/sample_data/images/robert-frost.png",
|
38
42
|
"spec/sample_data/pages/about.rhtml",
|
39
43
|
"spec/sample_data/resources/feed.rss",
|
40
44
|
"spec/sample_data/resources/redirects.txt",
|
41
45
|
"spec/sample_data/resources/robots.txt",
|
42
|
-
"spec/sample_data/themes/
|
43
|
-
"spec/sample_data/themes/
|
44
|
-
"spec/sample_data/themes/
|
45
|
-
"spec/sample_data/themes/
|
46
|
-
"spec/sample_data/themes/
|
47
|
-
"spec/sample_data/themes/
|
48
|
-
"spec/sample_data/themes/
|
49
|
-
"spec/sample_data/themes/
|
50
|
-
"spec/sample_data/themes/
|
51
|
-
"spec/sample_data/themes/
|
52
|
-
"spec/sample_data/themes/
|
53
|
-
"spec/sample_data/themes/
|
54
|
-
"spec/sample_data/themes/
|
55
|
-
"spec/sample_data/themes/
|
46
|
+
"spec/sample_data/themes/typography/css/app.css",
|
47
|
+
"spec/sample_data/themes/typography/css/bootstrap-responsive.css",
|
48
|
+
"spec/sample_data/themes/typography/css/bootstrap-responsive.min.css",
|
49
|
+
"spec/sample_data/themes/typography/css/bootstrap.css",
|
50
|
+
"spec/sample_data/themes/typography/css/bootstrap.min.css",
|
51
|
+
"spec/sample_data/themes/typography/img/github.png",
|
52
|
+
"spec/sample_data/themes/typography/img/glyphicons-halflings-white.png",
|
53
|
+
"spec/sample_data/themes/typography/img/glyphicons-halflings.png",
|
54
|
+
"spec/sample_data/themes/typography/img/instagram.png",
|
55
|
+
"spec/sample_data/themes/typography/js/bootstrap.js",
|
56
|
+
"spec/sample_data/themes/typography/js/bootstrap.min.js",
|
57
|
+
"spec/sample_data/themes/typography/js/image_alt.js",
|
58
|
+
"spec/sample_data/themes/typography/js/read_later.js",
|
59
|
+
"spec/sample_data/themes/typography/templates/archives.rhtml",
|
60
|
+
"spec/sample_data/themes/typography/templates/article.rhtml",
|
61
|
+
"spec/sample_data/themes/typography/templates/category.rhtml",
|
62
|
+
"spec/sample_data/themes/typography/templates/error.rhtml",
|
63
|
+
"spec/sample_data/themes/typography/templates/home.rhtml",
|
64
|
+
"spec/sample_data/themes/typography/templates/layout.rhtml",
|
56
65
|
"spec/spec_helper.rb"
|
57
66
|
]
|
58
67
|
s.homepage = "https://github.com/nbuggia/baron-blog-engine-gem"
|
data/lib/baron.rb
CHANGED
@@ -99,8 +99,8 @@ module Baron
|
|
99
99
|
route = (path || '/').split('/').reject { |i| i.empty? }
|
100
100
|
route << @config[:root] if route.empty?
|
101
101
|
mime_type = (mime_type =~ /txt|rss|json/) ? mime_type.to_sym : :html
|
102
|
-
categories = get_all_categories
|
103
|
-
params = {:page_name => route.first}
|
102
|
+
categories = get_all_categories
|
103
|
+
params = {:page_name => route.first, :rss_feed => get_feed_path}
|
104
104
|
params[:page_title] = (route.first == @config[:root] ? '' : "#{route.first.capitalize} #{@config[:title_delimiter]} ") + "#{@config[:title]}"
|
105
105
|
|
106
106
|
begin
|
@@ -112,19 +112,20 @@ module Baron
|
|
112
112
|
|
113
113
|
# Robots... /robots.txt
|
114
114
|
elsif route.first == 'robots'
|
115
|
-
|
115
|
+
PageController.new(get_all_articles, categories, @config[:article_max], params, @config) .
|
116
|
+
render_rss(get_system_resource('robots.txt'))
|
116
117
|
|
117
118
|
# Home page... /
|
118
119
|
elsif route.first == @config[:root]
|
119
|
-
all_articles = get_all_articles
|
120
|
+
all_articles = get_all_articles
|
120
121
|
params[:page_forward] = '/page/2/' if @config[:article_max] < all_articles.count
|
121
122
|
PageController.new(all_articles, categories, @config[:article_max], params, @config) .
|
122
123
|
render_html(get_theme_template(route.first), get_theme_template('layout'))
|
123
124
|
|
124
125
|
# Pagination... /page/2, /page/2/
|
125
126
|
elsif route.first == 'page' && route.count == 2
|
126
|
-
page_num = route.last.to_i
|
127
|
-
all_articles = get_all_articles
|
127
|
+
page_num = route.last.to_i rescue page_num = -1
|
128
|
+
all_articles = get_all_articles
|
128
129
|
max_pages = (all_articles.count.to_f / @config[:article_max].to_f).ceil
|
129
130
|
raise(Errno::ENOENT, 'Page not found') if page_num < 1 or page_num > max_pages
|
130
131
|
|
@@ -142,18 +143,18 @@ module Baron
|
|
142
143
|
# System routes... /robots.txt, /archives
|
143
144
|
elsif route.first == 'archives' or route.first == 'robots'
|
144
145
|
max_articles = ('archives' == route.first) ? :all : @config[:article_max]
|
145
|
-
PageController.new(get_all_articles
|
146
|
+
PageController.new(get_all_articles, categories, max_articles, params, @config) .
|
146
147
|
render_html(get_theme_template(route.first), get_theme_template('layout'))
|
147
148
|
|
148
149
|
# Custom pages... /about, /contact-us
|
149
150
|
elsif is_route_custom_page? route.first
|
150
|
-
PageController.new(get_all_articles
|
151
|
+
PageController.new(get_all_articles, categories, @config[:article_max], params, @config) .
|
151
152
|
render_html(get_page_template(route.first), get_theme_template('layout'))
|
152
153
|
|
153
154
|
# Category home pages... /projects/, /photography/, /poems/, etc
|
154
155
|
elsif is_route_category_home? route.last
|
155
|
-
filtered_articles = get_all_articles
|
156
|
-
params[:page_name] = route.last.gsub('-', ' ').titleize
|
156
|
+
filtered_articles = get_all_articles.select { |h| h[:category] == route.last }
|
157
|
+
params[:page_name] = route.last.gsub('-', ' ').titleize
|
157
158
|
PageController.new(filtered_articles, categories, :all, params, @config) .
|
158
159
|
render_html(get_theme_template('category'), get_theme_template('layout'))
|
159
160
|
|
@@ -186,7 +187,7 @@ module Baron
|
|
186
187
|
end
|
187
188
|
|
188
189
|
def get_all_articles
|
189
|
-
get_all_category_folder_paths
|
190
|
+
get_all_category_folder_paths.map do |folder_name|
|
190
191
|
Dir["#{folder_name}/*"].map do |e|
|
191
192
|
if e.end_with? @config[:ext]
|
192
193
|
parts = e.split('/')
|
@@ -219,7 +220,7 @@ module Baron
|
|
219
220
|
end
|
220
221
|
|
221
222
|
def find_single_article article_slug
|
222
|
-
get_all_articles
|
223
|
+
get_all_articles.each { |fileparts| return fileparts if fileparts[:filename] == article_slug }
|
223
224
|
raise Errno::ENOENT, 'Article not found'
|
224
225
|
end
|
225
226
|
|
@@ -228,7 +229,7 @@ module Baron
|
|
228
229
|
end
|
229
230
|
|
230
231
|
def is_route_category_home? path_node
|
231
|
-
get_all_categories
|
232
|
+
get_all_categories.each { |h| return true if h[:node_name] == path_node }
|
232
233
|
return false
|
233
234
|
end
|
234
235
|
|
@@ -237,6 +238,7 @@ module Baron
|
|
237
238
|
def get_page_template(name) "#{@config[:sample_data_path]}pages/#{name}.rhtml" end
|
238
239
|
def get_theme_template(name) "#{@config[:sample_data_path]}themes/#{@config[:theme]}/templates/#{name}.rhtml" end
|
239
240
|
def get_system_resource(name) "#{@config[:sample_data_path]}resources/#{name}" end
|
241
|
+
def get_feed_path() "#{@config[:url]}/feed.rss" end
|
240
242
|
end
|
241
243
|
|
242
244
|
class Article < Hash
|
data/spec/baron_article_spec.rb
CHANGED
@@ -8,7 +8,7 @@ require 'spec_helper'
|
|
8
8
|
describe "Baron::ArticleModel" do
|
9
9
|
before :all do
|
10
10
|
@config = load_config()
|
11
|
-
@article_parts = {:filename_and_path => "#{SAMPLE_DATA_PATH}articles/
|
11
|
+
@article_parts = {:filename_and_path => "#{SAMPLE_DATA_PATH}articles/favorites/1916-01-01-the-road-not-taken.txt",
|
12
12
|
:date => '1916-01-01',
|
13
13
|
:filename => 'the-road-not-taken',
|
14
14
|
:category => 'poems'}
|
@@ -17,11 +17,11 @@ describe "Baron::BlogEngine" do
|
|
17
17
|
@blog_engine.get_pages_path.should == SAMPLE_DATA_PATH + 'pages/'
|
18
18
|
@blog_engine.get_articles_path.should == SAMPLE_DATA_PATH + 'articles'
|
19
19
|
@blog_engine.get_page_template('about').should == SAMPLE_DATA_PATH + 'pages/about.rhtml'
|
20
|
-
@blog_engine.get_theme_template('article').should == SAMPLE_DATA_PATH + 'themes/
|
21
|
-
@blog_engine.get_theme_template('category').should == SAMPLE_DATA_PATH + 'themes/
|
22
|
-
@blog_engine.get_theme_template('error').should == SAMPLE_DATA_PATH + 'themes/
|
23
|
-
@blog_engine.get_theme_template('home').should == SAMPLE_DATA_PATH + 'themes/
|
24
|
-
@blog_engine.get_theme_template('layout').should == SAMPLE_DATA_PATH + 'themes/
|
20
|
+
@blog_engine.get_theme_template('article').should == SAMPLE_DATA_PATH + 'themes/typography/templates/article.rhtml'
|
21
|
+
@blog_engine.get_theme_template('category').should == SAMPLE_DATA_PATH + 'themes/typography/templates/category.rhtml'
|
22
|
+
@blog_engine.get_theme_template('error').should == SAMPLE_DATA_PATH + 'themes/typography/templates/error.rhtml'
|
23
|
+
@blog_engine.get_theme_template('home').should == SAMPLE_DATA_PATH + 'themes/typography/templates/home.rhtml'
|
24
|
+
@blog_engine.get_theme_template('layout').should == SAMPLE_DATA_PATH + 'themes/typography/templates/layout.rhtml'
|
25
25
|
@blog_engine.get_system_resource('redirects.txt').should == SAMPLE_DATA_PATH + 'resources/redirects.txt'
|
26
26
|
@blog_engine.get_system_resource('robots.txt').should == SAMPLE_DATA_PATH + 'resources/robots.txt'
|
27
27
|
@blog_engine.get_system_resource('feeds.rss').should == SAMPLE_DATA_PATH + 'resources/feeds.rss'
|
@@ -29,26 +29,28 @@ describe "Baron::BlogEngine" do
|
|
29
29
|
|
30
30
|
it "finds all categories" do
|
31
31
|
categories = @blog_engine.get_all_categories
|
32
|
-
categories.count.should ==
|
33
|
-
categories.first[:name].should == '
|
34
|
-
categories.first[:path].should == '/
|
35
|
-
categories.first[:count].should ==
|
36
|
-
categories.last[:name].should == '
|
37
|
-
categories.last[:path].should == '/
|
38
|
-
categories.last[:count].should ==
|
32
|
+
categories.count.should == 4
|
33
|
+
categories.first[:name].should == 'Favorites'
|
34
|
+
categories.first[:path].should == '/favorites/'
|
35
|
+
categories.first[:count].should == 1
|
36
|
+
categories.last[:name].should == 'Other Authors'
|
37
|
+
categories.last[:path].should == '/other-authors/'
|
38
|
+
categories.last[:count].should == 1
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
|
+
# todo, refactor to break out testing that we're appropriately breaking the fileparts down
|
42
|
+
|
41
43
|
it "finds all articles" do
|
42
44
|
articles_fileparts = @blog_engine.get_all_articles()
|
43
|
-
articles_fileparts.count.should ==
|
44
|
-
articles_fileparts.first[:filename_and_path].should == SAMPLE_DATA_PATH + 'articles/
|
45
|
-
articles_fileparts.first[:date].should == '
|
46
|
-
articles_fileparts.first[:filename].should == '
|
47
|
-
articles_fileparts.first[:category].should == ''
|
48
|
-
articles_fileparts.last[:filename_and_path].should == SAMPLE_DATA_PATH + 'articles/
|
45
|
+
articles_fileparts.count.should == 7
|
46
|
+
articles_fileparts.first[:filename_and_path].should == SAMPLE_DATA_PATH + 'articles/favorites/1916-01-01-the-road-not-taken.txt'
|
47
|
+
articles_fileparts.first[:date].should == '1916-01-01'
|
48
|
+
articles_fileparts.first[:filename].should == 'the-road-not-taken'
|
49
|
+
articles_fileparts.first[:category].should == 'favorites'
|
50
|
+
articles_fileparts.last[:filename_and_path].should == SAMPLE_DATA_PATH + 'articles/other authors/1909-01-02-If.txt'
|
49
51
|
articles_fileparts.last[:date].should == '1909-01-02'
|
50
52
|
articles_fileparts.last[:filename].should == 'if'
|
51
|
-
articles_fileparts.last[:category].should == '
|
53
|
+
articles_fileparts.last[:category].should == 'other authors'
|
52
54
|
end
|
53
55
|
|
54
56
|
it "returns all article parts" do
|
data/spec/baron_spec.rb
CHANGED
@@ -65,7 +65,7 @@ describe "Baron" do
|
|
65
65
|
|
66
66
|
describe "GET category home page" do
|
67
67
|
before :all do
|
68
|
-
@response = @baron.get('/
|
68
|
+
@response = @baron.get('/north-of-boston/')
|
69
69
|
end
|
70
70
|
|
71
71
|
it_behaves_like "Server Response"
|
@@ -113,7 +113,6 @@ describe "Baron" do
|
|
113
113
|
response.body.should include('404')
|
114
114
|
# should not render in the layout.rhtml if <html is in the first 100 chars
|
115
115
|
response.body.should_not include("<meta name=\"description\" content="">")
|
116
|
-
response.body.should include ('Error')
|
117
116
|
end
|
118
117
|
|
119
118
|
end
|
@@ -128,11 +127,11 @@ describe "Baron" do
|
|
128
127
|
it "returns expected content" do
|
129
128
|
@response.body.should include(@config[:title])
|
130
129
|
@response.body.should include("#{@config[:url]}/feed.rss")
|
131
|
-
@response.body.scan(/<entry>/).count.should ==
|
132
|
-
@response.body.scan(/<\/entry>/).count.should ==
|
130
|
+
@response.body.scan(/<entry>/).count.should == 5
|
131
|
+
@response.body.scan(/<\/entry>/).count.should == 5
|
133
132
|
@response.body.should include('<feed')
|
134
133
|
@response.body.should include('</feed>')
|
135
|
-
end
|
134
|
+
end
|
136
135
|
end
|
137
136
|
|
138
137
|
describe "GET /robots.txt" do
|
@@ -140,19 +139,14 @@ describe "Baron" do
|
|
140
139
|
@response = @baron.get('/robots.txt')
|
141
140
|
end
|
142
141
|
|
143
|
-
it_behaves_like "Server Response"
|
142
|
+
it_behaves_like "Server Response"
|
143
|
+
|
144
|
+
it "renders expected parameters" do
|
145
|
+
@response.body.should include("#{@config[:url]}/feed.rss")
|
146
|
+
end
|
144
147
|
end
|
145
148
|
|
146
149
|
describe "Redirect URLs" do
|
147
|
-
it "should redirect with 301" do
|
148
|
-
@response = @baron.get('/foobar-test-1')
|
149
|
-
@response.status.should == 301
|
150
|
-
@response['Location'].should == '/'
|
151
|
-
@response = @baron.get('/foobar-test-2')
|
152
|
-
@response.status.should == 301
|
153
|
-
@response['Location'].should == '/archives'
|
154
|
-
end
|
155
|
-
|
156
150
|
it "should canonicalize pagination at page 1" do
|
157
151
|
@response = @baron.get('/page/1/')
|
158
152
|
@response.status.should == 301
|
@@ -160,13 +154,7 @@ describe "Baron" do
|
|
160
154
|
@response = @baron.get('/page/1')
|
161
155
|
@response.status.should == 301
|
162
156
|
@response['Location'].should == '/'
|
163
|
-
end
|
164
|
-
|
165
|
-
it "should redirect with 302" do
|
166
|
-
@response = @baron.get('/foobar-test-3')
|
167
|
-
@response.status.should == 302
|
168
|
-
@response['Location'].should == '/posts/poems/the-road-not-taken/'
|
169
|
-
end
|
157
|
+
end
|
170
158
|
end
|
171
159
|
|
172
160
|
describe "Helper Functions" do
|
data/spec/sample_data/Gemfile
CHANGED
@@ -0,0 +1,243 @@
|
|
1
|
+
#The Baron Blog
|
2
|
+
|
3
|
+
A full-featured, yet minimalist, blog engine for developers
|
4
|
+
|
5
|
+
I know what you're thinking, the world doesn't need another Ruby blog
|
6
|
+
engine. And, okay, you're right, however Baron is a little bit different from
|
7
|
+
all the others in that it is a lot more full-featured, and still only a scant
|
8
|
+
400 lines of easy-to-ready code.
|
9
|
+
|
10
|
+
**Features**
|
11
|
+
* Publish to heroku (or similar PaaS) using Git
|
12
|
+
* Author articles or custom pages in markdown, text or HTML
|
13
|
+
* Article categories supported by simply putting articles in a folder
|
14
|
+
* Many permalink formats are supported, including a custom prefix and several
|
15
|
+
date formats
|
16
|
+
* 301 or 302 redirects are support for easy porting from your current blog
|
17
|
+
* SEO optimized with built-in support for Robots.txt, Google Analytics, Google
|
18
|
+
web master tools
|
19
|
+
* Easy to customize the look & feel via a common site layout template
|
20
|
+
* Frameworks used: Rack, RSpec, Bootstrap, JQuery, Disqus, Thin
|
21
|
+
|
22
|
+
##Quick Start
|
23
|
+
|
24
|
+
Dependencies: <a href="http://git-scm.com/">git</a>,
|
25
|
+
<a href="https://devcenter.heroku.com/articles/quickstart">heroku</a>,
|
26
|
+
<a href="http://www.ruby-lang.org/en/downloads/">ruby</a>
|
27
|
+
|
28
|
+
$ git clone https://github.com/nbuggia/baron-blog.git
|
29
|
+
$ mv baron-blog my-blog
|
30
|
+
$ heroku create my-blog
|
31
|
+
$ TODO: heroku add local...????
|
32
|
+
$ git init
|
33
|
+
$ git add .
|
34
|
+
$ git commit -m 'First commit of my new blog!'
|
35
|
+
$ git push heroku master
|
36
|
+
$ heroku open
|
37
|
+
|
38
|
+
Yay! Now you probably are going to want to customize this thing, read on for
|
39
|
+
all the bells and whistles.
|
40
|
+
|
41
|
+
##Quick How To's
|
42
|
+
|
43
|
+
###Customize Your Blog
|
44
|
+
|
45
|
+
$ less config.ru
|
46
|
+
|
47
|
+
There are many bells and whistles available for your blog, most of them can be
|
48
|
+
set from inside <code>config.ru</code>. The file is well documented for all the
|
49
|
+
options.
|
50
|
+
|
51
|
+
The other big customization route is to hack the theme to either make your own,
|
52
|
+
or to just modify it the way you'd like. Check out the
|
53
|
+
**Creating Your Own Themes*** section for more information.
|
54
|
+
|
55
|
+
###Adding a Custom Domain Name (with Heroku)
|
56
|
+
|
57
|
+
Go to the config section of your blog app and then enter in one or more new
|
58
|
+
domain names. Next step is to redirect your DNS server to to heroku
|
59
|
+
|
60
|
+
**GoDaddy**
|
61
|
+
|
62
|
+
|
63
|
+
**FOOBAR**
|
64
|
+
|
65
|
+
|
66
|
+
If you have a different hoster, just ask google how to set it up.
|
67
|
+
|
68
|
+
Finally, you'll want to redirect the nude domain name to www, like this:
|
69
|
+
|
70
|
+
my-domain.com → www.my-domain.com
|
71
|
+
|
72
|
+
###Create New Post
|
73
|
+
|
74
|
+
$ rake new
|
75
|
+
Title: My Blog Title
|
76
|
+
|
77
|
+
This command creates a new blog post with the current date in your
|
78
|
+
<code>drafts/</code> folder.
|
79
|
+
|
80
|
+
To create a new post, you simply create a new text file in your favorite
|
81
|
+
editor. Simply save the file somewhere in your <code>articles/</code> folder in
|
82
|
+
the format of <code>YYYY-MM-DD-article-title.txt</code>. Where YYYY means the
|
83
|
+
year in 4 digits, MM means the month in two digits and DD means the day of the
|
84
|
+
month in 2 digits.
|
85
|
+
|
86
|
+
**Attributes**
|
87
|
+
|
88
|
+
The first few lines of the file are where you can place attribute value
|
89
|
+
pairs in YAML format (e.g. <code>my_attribute: 'attribute'</code>). Add two new
|
90
|
+
lines to start the article, and then you can write it using markdown, HTML,
|
91
|
+
plain text, or a combination of all 3.
|
92
|
+
|
93
|
+
* You can add additional attributes you want and then access them in the rhtml
|
94
|
+
template with <code>@article[:my_attribute]</code>.
|
95
|
+
* If you need to use ':' or other special characters in your value, wrap it in
|
96
|
+
quotes (e.g. <code>title: "My article: Lots & Lots of Smiles"</code>)
|
97
|
+
|
98
|
+
Notes
|
99
|
+
|
100
|
+
* Baron forces all folder names and file names to lower case for canonicalization
|
101
|
+
* You can't have periods in the file name
|
102
|
+
|
103
|
+
###Create A New Custom Page
|
104
|
+
|
105
|
+
use the directory structure
|
106
|
+
|
107
|
+
###Setup redirects
|
108
|
+
|
109
|
+
|
110
|
+
###SEO
|
111
|
+
|
112
|
+
###Explore the Project
|
113
|
+
|
114
|
+
Project structure:
|
115
|
+
|
116
|
+
├── Gemfile
|
117
|
+
├── Rakefile
|
118
|
+
├── articles/ place your published articles here
|
119
|
+
│ ├── 2012-11-09-sample-1.txt the date and URL slug are the filename
|
120
|
+
│ └── category/ creating folders puts these articles in a category
|
121
|
+
│ ├── another category/ spaces in folder names will be replaces with '-'s
|
122
|
+
├── config.ru configure features of the blog here
|
123
|
+
├── downloads/ files in here are publicly accessible
|
124
|
+
├── drafts/ place for your unfinished articles
|
125
|
+
├── images/ images in here are publicly accessible
|
126
|
+
├── pages/ you can create custom pages in here
|
127
|
+
│ └── about.rhtml
|
128
|
+
├── resources/
|
129
|
+
│ ├── feed.rss your rss feed's rendering template
|
130
|
+
│ ├── redirects.txt list of redirects the blog will process
|
131
|
+
│ └── robots.txt your robots.txt file
|
132
|
+
└── themes/
|
133
|
+
└── my-theme/ each theme has the same folder structure
|
134
|
+
├── css/
|
135
|
+
├── img/
|
136
|
+
├── js/
|
137
|
+
└── templates/ rhtml rendering templates for each page type
|
138
|
+
|
139
|
+
###Create a New Article
|
140
|
+
|
141
|
+
|
142
|
+
###Create a New Page
|
143
|
+
|
144
|
+
|
145
|
+
###Add a Custom Domain Name in Heroku (free!)
|
146
|
+
|
147
|
+
TODO
|
148
|
+
|
149
|
+
###Deploy to Heroku (free!)
|
150
|
+
|
151
|
+
TODO
|
152
|
+
|
153
|
+
###Domain Name Configuration
|
154
|
+
|
155
|
+
Then setup use the Forwarding feature in GoDaddy to send buggia.org --> www.buggia.org
|
156
|
+
|
157
|
+
http://stackoverflow.com/questions/11492563/heroku-godaddy-send-naked-domain-to-www
|
158
|
+
|
159
|
+
|
160
|
+
###Run Blog Locally
|
161
|
+
|
162
|
+
Uses Thin to run the blog
|
163
|
+
|
164
|
+
> cd my-blog
|
165
|
+
> sudo gem install thin
|
166
|
+
> thin start
|
167
|
+
|
168
|
+
If you make a change to config.ru, you will need to restart thin.
|
169
|
+
|
170
|
+
###Creating Your Own Themes
|
171
|
+
|
172
|
+
|
173
|
+
##Next Steps
|
174
|
+
|
175
|
+
I wrote this as an excuse to learn a handful of new technologies and approaches,
|
176
|
+
like Ruby and TDD. There are an ambitious set of features I'd like to add that
|
177
|
+
each align to something else I would like to learn:
|
178
|
+
|
179
|
+
* Themes - I'm designing 3-4 fancy, shmancy themes to try out this new 'flat'
|
180
|
+
and minimalist thing everyone's excited about. Also a good excuse to dig into
|
181
|
+
HTML5, CSS3, JQuery, Instagram's API and a few other things.
|
182
|
+
|
183
|
+
* Pre-rendering - the platform nerd in me doesn't understand why the whole
|
184
|
+
blog isn't pre-rendered at deploy time so heroku just serves static HTML and
|
185
|
+
assets (a la <a href="https://github.com/mojombo/jekyll">Jekyll</a>)
|
186
|
+
|
187
|
+
* JavaScript Comments - the blog engine currently uses Disqus for comments,
|
188
|
+
which is free and cool, but I hate letting other people own my data. I want
|
189
|
+
to build something similar to Disqus on top of
|
190
|
+
<a href="https://www.parse.com/">Parse</a> /
|
191
|
+
<a href="https://github.com/documentcloud/backbone">Backbone</a> and make it
|
192
|
+
really easy to use
|
193
|
+
|
194
|
+
* Simple Plugin Model - I've always wanted to write a plug-in model. I tried
|
195
|
+
to write one in C++ in college and was only able to do static linking (lame). I
|
196
|
+
think an interpreted language will make it much easier, right?
|
197
|
+
|
198
|
+
##Namesake
|
199
|
+
|
200
|
+
Pictures of the adorable baron von underbite
|
201
|
+
|
202
|
+
##Thanks
|
203
|
+
|
204
|
+
While writing this blog engine, I barrowed a lot of code and design approaches
|
205
|
+
from the Toto project by Cloudhead and the Scanty project by Adam Wiggins. The
|
206
|
+
primary purpose of this project was a learning one for me, and both of these
|
207
|
+
folks provided a lot of good code an examples. I'm not sure how much code or
|
208
|
+
design awesomeness one needs to use before they are obligated to include their
|
209
|
+
license, so I'm included a link to each of them just in case (and thank you
|
210
|
+
both for your awesomeness!)
|
211
|
+
|
212
|
+
Toto
|
213
|
+
- URL: https://github.com/cloudhead/toto
|
214
|
+
- Author: http://cloudhead.io/ (Alexis Sellier)
|
215
|
+
- License: https://github.com/cloudhead/toto/blob/master/LICENSE
|
216
|
+
|
217
|
+
Scanty
|
218
|
+
- URL: https://github.com/adamwiggins/scanty
|
219
|
+
- Author: http://about.adamwiggins.com/ (Adam Wiggins)
|
220
|
+
|
221
|
+
##License
|
222
|
+
|
223
|
+
This software is licensed under the MIT Software License
|
224
|
+
|
225
|
+
Copyright (c) 2013 Nathan Buggia
|
226
|
+
|
227
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
228
|
+
this software and associated documentation files (the "Software"), to deal in
|
229
|
+
the Software without restriction, including without limitation the rights to
|
230
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
231
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
232
|
+
so, subject to the following conditions:
|
233
|
+
|
234
|
+
The above copyright notice and this permission notice shall be included in all
|
235
|
+
copies or substantial portions of the Software.
|
236
|
+
|
237
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
238
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
239
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
240
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
241
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
242
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
243
|
+
SOFTWARE.
|