baron 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|