baron 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/VERSION +1 -1
  2. data/baron.gemspec +32 -23
  3. data/lib/baron.rb +15 -13
  4. data/spec/baron_article_spec.rb +1 -1
  5. data/spec/baron_blog_engine_spec.rb +22 -20
  6. data/spec/baron_spec.rb +10 -22
  7. data/spec/sample_data/.gitignore +5 -0
  8. data/spec/sample_data/Gemfile +1 -1
  9. data/spec/sample_data/README.md +243 -0
  10. data/spec/sample_data/Rakefile +1 -1
  11. data/spec/sample_data/articles/favorites/1916-01-01-the-road-not-taken.txt +26 -0
  12. data/spec/sample_data/articles/north of boston/1914-01-01-the-pasture.txt +10 -0
  13. data/spec/sample_data/articles/north of boston/1914-01-02-mending-wall.txt +48 -0
  14. data/spec/sample_data/articles/north of boston/1914-01-03-the-death-of-the-hired-man.txt +211 -0
  15. data/spec/sample_data/articles/north of boston/1914-01-04-the-mountain.txt +121 -0
  16. data/spec/sample_data/articles/north of boston/1914-01-05-A-Hundred-callers.txt +196 -0
  17. 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
  18. data/spec/sample_data/config.ru +63 -29
  19. data/spec/sample_data/images/robert-frost-small.png +0 -0
  20. data/spec/sample_data/images/robert-frost.png +0 -0
  21. data/spec/sample_data/pages/about.rhtml +9 -14
  22. data/spec/sample_data/resources/redirects.txt +1 -29
  23. data/spec/sample_data/resources/robots.txt +4 -1
  24. data/spec/sample_data/themes/typography/css/app.css +58 -0
  25. data/spec/sample_data/themes/{test → typography}/css/bootstrap-responsive.css +0 -0
  26. data/spec/sample_data/themes/{test → typography}/css/bootstrap-responsive.min.css +0 -0
  27. data/spec/sample_data/themes/{test → typography}/css/bootstrap.css +0 -0
  28. data/spec/sample_data/themes/{test → typography}/css/bootstrap.min.css +0 -0
  29. data/spec/sample_data/themes/typography/img/github.png +0 -0
  30. data/spec/sample_data/themes/{test → typography}/img/glyphicons-halflings-white.png +0 -0
  31. data/spec/sample_data/themes/{test → typography}/img/glyphicons-halflings.png +0 -0
  32. data/spec/sample_data/{images → themes/typography/img}/instagram.png +0 -0
  33. data/spec/sample_data/themes/typography/js/bootstrap.js +2159 -0
  34. data/spec/sample_data/themes/typography/js/bootstrap.min.js +6 -0
  35. data/spec/sample_data/themes/typography/js/image_alt.js +12 -0
  36. data/spec/sample_data/themes/typography/js/read_later.js +14 -0
  37. data/spec/sample_data/themes/typography/templates/archives.rhtml +18 -0
  38. data/spec/sample_data/themes/typography/templates/article.rhtml +14 -0
  39. data/spec/sample_data/themes/typography/templates/category.rhtml +17 -0
  40. data/spec/sample_data/themes/typography/templates/error.rhtml +3 -0
  41. data/spec/sample_data/themes/typography/templates/home.rhtml +28 -0
  42. data/spec/sample_data/themes/typography/templates/layout.rhtml +141 -0
  43. data/spec/spec_helper.rb +1 -1
  44. metadata +32 -23
  45. data/spec/sample_data/articles/2012-11-09-sample-post.txt +0 -11
  46. data/spec/sample_data/articles/poems/1916-01-01-the-road-not-taken.txt +0 -26
  47. data/spec/sample_data/images/import-csv-file-1.png +0 -0
  48. data/spec/sample_data/images/import-csv-file-2.png +0 -0
  49. data/spec/sample_data/images/import-csv-file-3.png +0 -0
  50. data/spec/sample_data/themes/test/css/app.css +0 -27
  51. data/spec/sample_data/themes/test/img/instagram.png +0 -0
  52. data/spec/sample_data/themes/test/templates/archives.rhtml +0 -14
  53. data/spec/sample_data/themes/test/templates/article.rhtml +0 -14
  54. data/spec/sample_data/themes/test/templates/category.rhtml +0 -15
  55. data/spec/sample_data/themes/test/templates/error.rhtml +0 -11
  56. data/spec/sample_data/themes/test/templates/home.rhtml +0 -26
  57. data/spec/sample_data/themes/test/templates/layout.rhtml +0 -90
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.3
1
+ 1.0.4
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "baron"
8
- s.version = "1.0.3"
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-13"
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/2012-11-09-sample-post.txt",
31
- "spec/sample_data/articles/poems/1909-01-02-If.txt",
32
- "spec/sample_data/articles/poems/1916-01-01-the-road-not-taken.txt",
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/import-csv-file-1.png",
35
- "spec/sample_data/images/import-csv-file-2.png",
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/test/css/app.css",
43
- "spec/sample_data/themes/test/css/bootstrap-responsive.css",
44
- "spec/sample_data/themes/test/css/bootstrap-responsive.min.css",
45
- "spec/sample_data/themes/test/css/bootstrap.css",
46
- "spec/sample_data/themes/test/css/bootstrap.min.css",
47
- "spec/sample_data/themes/test/img/glyphicons-halflings-white.png",
48
- "spec/sample_data/themes/test/img/glyphicons-halflings.png",
49
- "spec/sample_data/themes/test/img/instagram.png",
50
- "spec/sample_data/themes/test/templates/archives.rhtml",
51
- "spec/sample_data/themes/test/templates/article.rhtml",
52
- "spec/sample_data/themes/test/templates/category.rhtml",
53
- "spec/sample_data/themes/test/templates/error.rhtml",
54
- "spec/sample_data/themes/test/templates/home.rhtml",
55
- "spec/sample_data/themes/test/templates/layout.rhtml",
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"
@@ -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
- File.read(get_system_resource('robots.txt')) rescue raise(Errno::ENOENT, 'Page not found')
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() rescue page_num = -1
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(), categories, max_articles, params, @config) .
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(), categories, @config[:article_max], params, @config) .
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().select { |h| h[:category] == route.last }
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().map do |folder_name|
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().each { |fileparts| return fileparts if fileparts[:filename] == article_slug }
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().each { |h| return true if h[:node_name] == path_node }
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
@@ -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/poems/1916-01-01-the-road-not-taken.txt",
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/test/templates/article.rhtml'
21
- @blog_engine.get_theme_template('category').should == SAMPLE_DATA_PATH + 'themes/test/templates/category.rhtml'
22
- @blog_engine.get_theme_template('error').should == SAMPLE_DATA_PATH + 'themes/test/templates/error.rhtml'
23
- @blog_engine.get_theme_template('home').should == SAMPLE_DATA_PATH + 'themes/test/templates/home.rhtml'
24
- @blog_engine.get_theme_template('layout').should == SAMPLE_DATA_PATH + 'themes/test/templates/layout.rhtml'
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 == 2
33
- categories.first[:name].should == 'Code Projects'
34
- categories.first[:path].should == '/code-projects/'
35
- categories.first[:count].should == 0
36
- categories.last[:name].should == 'Poems'
37
- categories.last[:path].should == '/poems/'
38
- categories.last[:count].should == 2
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 == 3
44
- articles_fileparts.first[:filename_and_path].should == SAMPLE_DATA_PATH + 'articles/2012-11-09-sample-post.txt'
45
- articles_fileparts.first[:date].should == '2012-11-09'
46
- articles_fileparts.first[:filename].should == 'sample-post'
47
- articles_fileparts.first[:category].should == ''
48
- articles_fileparts.last[:filename_and_path].should == SAMPLE_DATA_PATH + 'articles/poems/1909-01-02-If.txt'
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 == 'poems'
53
+ articles_fileparts.last[:category].should == 'other authors'
52
54
  end
53
55
 
54
56
  it "returns all article parts" do
@@ -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('/poems/')
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 == 3
132
- @response.body.scan(/<\/entry>/).count.should == 3
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
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
@@ -3,4 +3,4 @@ source 'https://rubygems.org'
3
3
  ruby '1.9.3'
4
4
  gem 'baron'
5
5
  gem 'rack'
6
- gem 'rdiscount'
6
+ gem 'rdiscount'
@@ -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 &rarr; 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.