baron 1.0.14 → 1.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +2 -23
- data/Readme.md +20 -84
- data/VERSION +1 -1
- data/baron.gemspec +6 -6
- data/lib/baron/blog_engine.rb +29 -25
- data/lib/baron/config.rb +25 -21
- data/lib/baron/models/article.rb +12 -6
- data/spec/baron_article_spec.rb +32 -27
- data/spec/baron_blog_engine_spec.rb +29 -29
- data/spec/baron_spec.rb +36 -43
- data/spec/baron_theme_spec.rb +12 -12
- metadata +12 -19
- data/lib/baron/utils.rb +0 -31
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1b7cb1562629316f255989b7b77ba359065d0e6a
|
4
|
+
data.tar.gz: 7c3e65e6abe83d721d871acdcd94e912f04a88a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 30665503b10b8974bca7d8457067722caa7cd7a09c2ce09ec3a474296bbb8a89a43dbc3c66153a101303ba253e3ed7f72d262295a5974b99ed486f2129021116
|
7
|
+
data.tar.gz: cec6fd94927beb807a0dd5ece4c16834a075fc540c95e048748c3f0074a104d4c4df9048b092eef83658c44da162b38e780cc4d5988c20334c6b644cc5f31650
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
This software is licensed under the MIT Software License
|
2
2
|
|
3
|
-
Copyright (c)
|
3
|
+
Copyright (c) 2016 Nathan Buggia
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
@@ -18,25 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
-
SOFTWARE.
|
22
|
-
|
23
|
-
---
|
24
|
-
|
25
|
-
While writing this blog engine, I barrowed some code and design approches
|
26
|
-
from many existing blog engines. I'm not sure how much one must borrow before
|
27
|
-
they are obligated to include the original license, so I'm including all of
|
28
|
-
them below just in case.
|
29
|
-
|
30
|
-
Toto
|
31
|
-
- URL: https://github.com/cloudhead/toto
|
32
|
-
- Author: http://cloudhead.io/ (Alexis Sellier)
|
33
|
-
- License: https://github.com/cloudhead/toto/blob/master/LICENSE
|
34
|
-
|
35
|
-
Jekyll
|
36
|
-
- URL: https://github.com/mojombo/jekyll
|
37
|
-
- Author: http://tom.preston-werner.com/ (Tom Preston-Werner)
|
38
|
-
- License: https://github.com/mojombo/jekyll/blob/master/LICENSE
|
39
|
-
|
40
|
-
Scanty
|
41
|
-
- URL: https://github.com/adamwiggins/scanty
|
42
|
-
- Author: http://about.adamwiggins.com/ (Adam Wiggins)
|
21
|
+
SOFTWARE.
|
data/Readme.md
CHANGED
@@ -1,95 +1,31 @@
|
|
1
1
|
#Baron Blog Engine Gem
|
2
2
|
|
3
|
-
|
3
|
+
This is my first project in Ruby. I wanted to create the simplest blog engine I
|
4
|
+
could that included all the features I was looking for in my person blog.
|
5
|
+
There are two parts, the [Baron Blog Engine Gem](https://rubygems.org/gems/baron)
|
6
|
+
and the [Baron Blog](https://github.com/nbuggia/baron-blog) project. The gem
|
7
|
+
contains the code for routing, HTTP, and the MVC components of a simple blog.
|
8
|
+
The blog project instantiates the gem and includes the blog UI templates and
|
9
|
+
content.
|
4
10
|
|
5
|
-
|
6
|
-
blog, you should use this project, which includes the client:
|
11
|
+
---
|
7
12
|
|
8
|
-
|
9
|
-
|
10
|
-
##How Does it Work?
|
11
|
-
|
12
|
-
Here's a quick overview of how the whole thing comes together.
|
13
|
-
|
14
|
-
There are two parts to this blog, the **Baron Blog Engine Gem** and the
|
15
|
-
**Baron Blog** project. The blog engine gem contains all the code for the data
|
16
|
-
model and the view controllers, which is conveniently packaged up into a gem
|
17
|
-
for easy distribution (I might change that in the future to make it easier to
|
18
|
-
hack). The Baron Blog project contains all of the views, and the assets (CSS,
|
19
|
-
images, articles, etc). It references the blog engine gem.
|
20
|
-
|
21
|
-
**Baron Blog Engine Gem**
|
22
|
-
|
23
|
-
All of the source code for this is in a single code file (./lib/baron.rb).
|
24
|
-
|
25
|
-
Project structure:
|
26
|
-
|
27
|
-
├── LICENSE
|
28
|
-
├── Rakefile rake test, rake install
|
29
|
-
├── Readme.md you are reading this document now...
|
30
|
-
├── VERSION
|
31
|
-
├── lib/
|
32
|
-
│ └── baron.rb all the source code for the gem
|
33
|
-
├── pkg/
|
34
|
-
│ └── baron-1.0.0.gem byte-code compiled gem (I think)
|
35
|
-
└── spec/ unit tests using RSpec
|
36
|
-
├── baron_article_spec.rb article data model tests
|
37
|
-
├── baron_blog_engine_spec.rb BlogEngine class tests
|
38
|
-
├── baron_spec.rb end-to-end tests
|
39
|
-
├── sample_data/ sample data for testing
|
40
|
-
└── spec_helper.rb
|
41
|
-
|
42
|
-
* Baron::BlogEngine - handles the main application loop. It handles building the
|
43
|
-
right page for every given route. It also contains all the logic for where all
|
44
|
-
the files are stored.
|
45
|
-
|
46
|
-
* Baron::PageController - uses Ruby's ERB library to render the template pages
|
47
|
-
with the variable's from the Baron::BlogEngine. Most pages get rendered twice,
|
48
|
-
the first time we render the partial page (e.g. an article data model into the
|
49
|
-
article rhtml template) and the second time we render the article.rhtml results
|
50
|
-
into the site layout template (./themes/theme/templates/layout.rhtml)
|
51
|
-
|
52
|
-
* Baron::Article - the data model for a single article.
|
53
|
-
|
54
|
-
##Thanks
|
55
|
-
|
56
|
-
While writing this blog engine, I barrowed a lot of code and design approaches
|
57
|
-
from the Toto project by Cloudhead and the Scanty project by Adam Wiggins. The
|
58
|
-
primary purpose of this project was a learning one for me, and both of these
|
59
|
-
folks provided a lot of good code an examples. I'm not sure how much code or
|
60
|
-
design awesomeness one needs to use before they are obligated to include their
|
61
|
-
license, so I'm included a link to each of them just in case (and thank you
|
62
|
-
both for your awesomeness!)
|
13
|
+
While writing this blog engine, I borrowed some code and design approaches
|
14
|
+
from many existing blog engines. Following were the most useful.
|
63
15
|
|
64
16
|
Toto
|
65
|
-
- URL: https://github.com/cloudhead/toto
|
66
|
-
- Author: http://cloudhead.io/ (Alexis Sellier)
|
67
|
-
- License: https://github.com/cloudhead/toto/blob/master/LICENSE
|
68
17
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
##License
|
18
|
+
* URL: https://github.com/cloudhead/toto
|
19
|
+
* Author: http://cloudhead.io/ (Alexis Sellier)
|
20
|
+
* License: https://github.com/cloudhead/toto/blob/master/LICENSE
|
74
21
|
|
75
|
-
|
22
|
+
Jekyll
|
76
23
|
|
77
|
-
|
24
|
+
* URL: https://github.com/mojombo/jekyll
|
25
|
+
* Author: http://tom.preston-werner.com/ (Tom Preston-Werner)
|
26
|
+
* License: https://github.com/mojombo/jekyll/blob/master/LICENSE
|
78
27
|
|
79
|
-
|
80
|
-
this software and associated documentation files (the "Software"), to deal in
|
81
|
-
the Software without restriction, including without limitation the rights to
|
82
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
83
|
-
of the Software, and to permit persons to whom the Software is furnished to do
|
84
|
-
so, subject to the following conditions:
|
85
|
-
|
86
|
-
The above copyright notice and this permission notice shall be included in all
|
87
|
-
copies or substantial portions of the Software.
|
28
|
+
Scanty
|
88
29
|
|
89
|
-
|
90
|
-
|
91
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
92
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
93
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
94
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
95
|
-
SOFTWARE.
|
30
|
+
* URL: https://github.com/adamwiggins/scanty
|
31
|
+
* Author: http://about.adamwiggins.com/ (Adam Wiggins)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.17
|
data/baron.gemspec
CHANGED
@@ -2,14 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: baron 1.0.17 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
8
|
s.name = "baron"
|
8
|
-
s.version = "1.0.
|
9
|
+
s.version = "1.0.17"
|
9
10
|
|
10
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
11
13
|
s.authors = ["Nathan Buggia"]
|
12
|
-
s.date = "
|
14
|
+
s.date = "2016-07-07"
|
13
15
|
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
16
|
s.email = "nbuggia@gmail.com"
|
15
17
|
s.extra_rdoc_files = [
|
@@ -27,7 +29,6 @@ Gem::Specification.new do |s|
|
|
27
29
|
"lib/baron/models/article.rb",
|
28
30
|
"lib/baron/models/theme.rb",
|
29
31
|
"lib/baron/page_controller.rb",
|
30
|
-
"lib/baron/utils.rb",
|
31
32
|
"spec/baron_article_spec.rb",
|
32
33
|
"spec/baron_blog_engine_spec.rb",
|
33
34
|
"spec/baron_spec.rb",
|
@@ -74,12 +75,11 @@ Gem::Specification.new do |s|
|
|
74
75
|
"spec/spec_helper.rb"
|
75
76
|
]
|
76
77
|
s.homepage = "https://github.com/nbuggia/baron-blog-engine-gem"
|
77
|
-
s.
|
78
|
-
s.rubygems_version = "1.8.25"
|
78
|
+
s.rubygems_version = "2.5.1"
|
79
79
|
s.summary = "Minimalist, yet full-featured, blog engine."
|
80
80
|
|
81
81
|
if s.respond_to? :specification_version then
|
82
|
-
s.specification_version =
|
82
|
+
s.specification_version = 4
|
83
83
|
|
84
84
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
85
85
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
data/lib/baron/blog_engine.rb
CHANGED
@@ -26,25 +26,24 @@ module Baron
|
|
26
26
|
theme.load_config
|
27
27
|
|
28
28
|
begin
|
29
|
-
|
30
|
-
# Atom feed... /feed.atom
|
29
|
+
# Atom feed /feed.atom
|
31
30
|
body = if route.first == 'feed.atom'
|
32
31
|
PageController.new(get_all_articles, categories, @config[:article_max], params, theme, @config) .
|
33
32
|
render(get_system_resource('feed.atom'))
|
34
33
|
|
35
|
-
# Robots
|
34
|
+
# Robots /robots.txt
|
36
35
|
elsif route.first == 'robots.txt'
|
37
36
|
PageController.new(get_all_articles, categories, @config[:article_max], params, theme, @config) .
|
38
37
|
render(get_system_resource('robots.txt'))
|
39
38
|
|
40
|
-
# Home page
|
39
|
+
# Home page /
|
41
40
|
elsif route.first == @config[:root]
|
42
41
|
all_articles = get_all_articles
|
43
42
|
params[:page_forward] = '/page/2/' if @config[:article_max] < all_articles.count
|
44
43
|
PageController.new(all_articles, categories, @config[:article_max], params, theme, @config) .
|
45
44
|
render_html(theme.get_template(route.first), theme.get_template('layout'))
|
46
45
|
|
47
|
-
# Pagination
|
46
|
+
# Pagination /page/2, /page/2/
|
48
47
|
elsif route.first == 'page' && route.count == 2
|
49
48
|
page_num = route.last.to_i rescue page_num = -1
|
50
49
|
all_articles = get_all_articles
|
@@ -62,43 +61,41 @@ module Baron
|
|
62
61
|
PageController.new(articles_on_this_page, categories, @config[:article_max], params, theme, @config) .
|
63
62
|
render_html(theme.get_template('home'), theme.get_template('layout'))
|
64
63
|
|
65
|
-
# System routes
|
64
|
+
# System routes /robots.txt, /archives
|
66
65
|
elsif route.first == 'archives' or route.first == 'robots'
|
67
66
|
max_articles = ('archives' == route.first) ? :all : @config[:article_max]
|
68
67
|
PageController.new(get_all_articles, categories, max_articles, params, theme, @config) .
|
69
68
|
render_html(theme.get_template(route.first), theme.get_template('layout'))
|
70
69
|
|
71
|
-
# Custom pages
|
70
|
+
# Custom pages /about, /contact-us
|
72
71
|
elsif is_route_custom_page? route.first
|
73
72
|
PageController.new(get_all_articles, categories, @config[:article_max], params, theme, @config) .
|
74
73
|
render_html(get_page_template(route.first), theme.get_template('layout'))
|
75
74
|
|
76
|
-
# Category home pages
|
75
|
+
# Category home pages /projects/, /photography/, /poems/, etc
|
77
76
|
elsif is_route_category_home? route.last
|
78
77
|
filtered_articles = get_all_articles.select { |h| h[:category] == route.last }
|
79
|
-
params[:page_name] = route.last.gsub('-', ' ')
|
78
|
+
params[:page_name] = titlecase(route.last.gsub('-', ' '))
|
80
79
|
PageController.new(filtered_articles, categories, :all, params, theme, @config) .
|
81
80
|
render_html(theme.get_template('category'), theme.get_template('layout'))
|
82
81
|
|
83
|
-
# Articles
|
82
|
+
# Articles /posts/2013/01/18/my-article-title, /posts/category/2013/my-article-title, etc
|
84
83
|
else
|
85
84
|
article = [ find_single_article(route.last) ]
|
86
|
-
params[:page_title] = "#{article.first[:filename].gsub('-',' ')
|
85
|
+
params[:page_title] = "#{titlecase(article.first[:filename].gsub('-',' '))} #{@config[:title_delimiter]} #{@config[:title]}"
|
87
86
|
PageController.new(article, categories, 1, params, theme, @config) .
|
88
87
|
render_html(theme.get_template('article'), theme.get_template('layout'))
|
89
88
|
end
|
90
89
|
|
91
|
-
return :
|
90
|
+
return { body: body, status: 200 }
|
92
91
|
|
93
|
-
rescue Errno::ENOENT => e
|
94
|
-
|
92
|
+
rescue Errno::ENOENT => e
|
95
93
|
# 404 Page Not Found
|
96
94
|
params[:error_message] = 'Page not found'
|
97
95
|
params[:error_code] = '404'
|
98
96
|
body = PageController.new([], categories, 0, params, theme, @config) .
|
99
97
|
render_html(theme.get_template('error'), theme.get_template('layout'))
|
100
|
-
|
101
|
-
return :body => body, :status => 404
|
98
|
+
return { body: body, status: 404 }
|
102
99
|
end
|
103
100
|
end
|
104
101
|
|
@@ -116,11 +113,11 @@ module Baron
|
|
116
113
|
if e.end_with? @config[:ext]
|
117
114
|
parts = e.split('/')
|
118
115
|
{
|
119
|
-
:
|
120
|
-
:
|
116
|
+
filename_and_path: e,
|
117
|
+
date: parts.last[0..9],
|
121
118
|
# trims date and extention
|
122
|
-
:
|
123
|
-
:
|
119
|
+
filename: parts.last[11..(-1 * (@config[:ext].length + 2))].downcase,
|
120
|
+
category: parts[parts.count-2] == 'articles' ? '' : parts[parts.count-2]
|
124
121
|
}
|
125
122
|
end
|
126
123
|
end
|
@@ -135,10 +132,10 @@ module Baron
|
|
135
132
|
Dir["#{get_articles_path}/*/"].map do |a|
|
136
133
|
folder_name = File.basename(a)
|
137
134
|
{
|
138
|
-
:
|
139
|
-
:
|
140
|
-
:
|
141
|
-
:
|
135
|
+
name: titlecase(folder_name),
|
136
|
+
node_name: folder_name.gsub(' ', '-'),
|
137
|
+
path: "/#{@config[:permalink_prefix]}/#{folder_name.gsub(' ', '-')}/".squeeze('/'),
|
138
|
+
count: Dir["#{get_articles_path}/#{folder_name}/*"].count
|
142
139
|
}
|
143
140
|
end .
|
144
141
|
sort_by { |hash| hash[:name] }
|
@@ -175,6 +172,13 @@ module Baron
|
|
175
172
|
def get_system_resource name
|
176
173
|
"#{@config[:sample_data_path]}resources/#{name}".squeeze('/')
|
177
174
|
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
# Capitalize the first letter of each word in a string
|
179
|
+
def titlecase str
|
180
|
+
str.split(/(\W)/).map(&:capitalize).join
|
181
|
+
end
|
178
182
|
end
|
179
|
-
|
183
|
+
|
180
184
|
end
|
data/lib/baron/config.rb
CHANGED
@@ -5,61 +5,65 @@ module Baron
|
|
5
5
|
Defaults = {
|
6
6
|
|
7
7
|
# cache duration (seconds)
|
8
|
-
:
|
8
|
+
cache: 28800,
|
9
9
|
|
10
10
|
# token to represent root in the app
|
11
|
-
:
|
11
|
+
root: 'home',
|
12
12
|
|
13
13
|
# used by the RSpec tests to show where the sample data is stored
|
14
|
-
:
|
14
|
+
sample_data_path: '',
|
15
15
|
|
16
16
|
# default name to use for the blog's author
|
17
|
-
:
|
17
|
+
author: ENV['USER'],
|
18
18
|
|
19
19
|
# default title for the site
|
20
|
-
:
|
20
|
+
title: Dir.pwd.split('/').last,
|
21
21
|
|
22
22
|
# used to divide the different elements of the page title
|
23
|
-
:
|
23
|
+
title_delimiter: "›",
|
24
24
|
|
25
|
-
# symbol used to represent trucated text (article summary)
|
26
|
-
:truncation_marker => '…',
|
27
|
-
|
28
25
|
# root URL of the site
|
29
|
-
:
|
26
|
+
url: 'http://localhost:3000/',
|
30
27
|
|
31
28
|
# date function block
|
32
|
-
:
|
29
|
+
date: lambda {|now| now.strftime("%d/%m/%Y") },
|
33
30
|
|
34
31
|
# use markdown
|
35
|
-
:
|
32
|
+
markdown: :smart,
|
33
|
+
|
34
|
+
# symbol used to represent trucated text (article summary)
|
35
|
+
truncation_marker: '…',
|
36
36
|
|
37
37
|
# length of summary and delimiter
|
38
|
-
|
38
|
+
summary:
|
39
|
+
{
|
40
|
+
max: 150,
|
41
|
+
delim: /~\n/
|
42
|
+
},
|
39
43
|
|
40
44
|
# extension for article files
|
41
|
-
:
|
45
|
+
ext: 'txt',
|
42
46
|
|
43
47
|
# common path prefix for article permalinks
|
44
|
-
:
|
48
|
+
permalink_prefix: '',
|
45
49
|
|
46
50
|
# :year_date, :year_month_date, :year_month_day_date, :no_date
|
47
|
-
:
|
51
|
+
permalink_date_format: :year_month_day_date,
|
48
52
|
|
49
53
|
# number of most recent articles to return to custom pages
|
50
|
-
:
|
54
|
+
article_max: 5,
|
51
55
|
|
52
56
|
# name of the theme to use
|
53
|
-
:
|
57
|
+
theme: 'default',
|
54
58
|
|
55
59
|
# account id for google analytics account
|
56
|
-
:
|
60
|
+
google_analytics: '',
|
57
61
|
|
58
62
|
# HTML Meta Tag verification code for google webmaster account
|
59
|
-
:
|
63
|
+
google_webmaster: '',
|
60
64
|
|
61
65
|
# account name for your disqus account www.disqus.com
|
62
|
-
:
|
66
|
+
disqus_shortname: false
|
63
67
|
}
|
64
68
|
|
65
69
|
def get_feed_permalink
|
data/lib/baron/models/article.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
module Baron
|
2
2
|
|
3
3
|
class Article < Hash
|
4
|
-
|
4
|
+
|
5
|
+
def self.create_slug title, date
|
6
|
+
slug = title.empty? ? "Untitled" : title.strip.downcase
|
7
|
+
slug_encoded = slug.gsub(/[^a-z0-9]/, '-')
|
8
|
+
"#{date.strftime("%Y-%m-%d-") + slug_encoded}"
|
9
|
+
end
|
10
|
+
|
5
11
|
def initialize file_parts, config = {}
|
6
12
|
@config = config
|
7
13
|
self[:filename_and_path] = file_parts[:filename_and_path]
|
@@ -29,10 +35,10 @@ module Baron
|
|
29
35
|
permalink_prefix = prefix.empty? ? @config[:permalink_prefix] : prefix
|
30
36
|
permalink_date_format = date_format.empty? ? @config[:permalink_date_format] : date_format
|
31
37
|
date_path = case permalink_date_format
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
38
|
+
when :year_date; self[:date].strftime("/%Y")
|
39
|
+
when :year_month_date; self[:date].strftime("/%Y/%m")
|
40
|
+
when :year_month_day_date; self[:date].strftime("/%Y/%m/%d")
|
41
|
+
else ''
|
36
42
|
end
|
37
43
|
|
38
44
|
"/#{permalink_prefix}/#{self[:category]}#{date_path}/#{slug}/".squeeze('/')
|
@@ -66,7 +72,7 @@ module Baron
|
|
66
72
|
def slug
|
67
73
|
self[:slug]
|
68
74
|
end
|
69
|
-
|
75
|
+
|
70
76
|
protected
|
71
77
|
|
72
78
|
def load_article filename_and_path
|
data/spec/baron_article_spec.rb
CHANGED
@@ -17,38 +17,43 @@ describe "Baron::Article" do
|
|
17
17
|
|
18
18
|
describe "Provides access to properties" do
|
19
19
|
it "should return input parameters" do
|
20
|
-
@article[:filename_and_path].
|
21
|
-
@article[:date].
|
22
|
-
@article[:category].
|
20
|
+
expect(@article[:filename_and_path]).to eq(@article_parts[:filename_and_path])
|
21
|
+
expect(@article[:date]).to be_instance_of(Date)
|
22
|
+
expect(@article[:category]).to eq(@article_parts[:category])
|
23
23
|
end
|
24
24
|
|
25
25
|
it "should return article data correctly" do
|
26
|
-
@article[:title].
|
27
|
-
@article.title.
|
28
|
-
@article.date.
|
29
|
-
@article.date_iso8601.
|
30
|
-
@article[:author].
|
31
|
-
@article.author.
|
32
|
-
@article[:body].length.
|
33
|
-
@article[:category].
|
34
|
-
@article.category.
|
35
|
-
@article[:slug].
|
36
|
-
@article.slug.
|
26
|
+
expect(@article[:title]).to eq('The Road Not Taken')
|
27
|
+
expect(@article.title).to eq('The Road Not Taken')
|
28
|
+
expect(@article.date).to eq('01/01/1916')
|
29
|
+
expect(@article.date_iso8601).to eq('1916-01-01T00:00:00+00:00')
|
30
|
+
expect(@article[:author]).to eq('Robert Frost')
|
31
|
+
expect(@article.author).to eq('Robert Frost')
|
32
|
+
expect(@article[:body].length).to be > 0
|
33
|
+
expect(@article[:category]).to eq('poems')
|
34
|
+
expect(@article.category).to eq('poems')
|
35
|
+
expect(@article[:slug]).to eq('the-road-not-taken')
|
36
|
+
expect(@article.slug).to eq('the-road-not-taken')
|
37
37
|
end
|
38
38
|
|
39
39
|
it "should handle different path configuations" do
|
40
|
-
@article.path('', :no_date).
|
41
|
-
@article.path('', :year_month_day_date).
|
42
|
-
@article.path('', :year_month_date).
|
43
|
-
@article.path('', :year_date).
|
44
|
-
@article.path('posts', :no_date).
|
45
|
-
@article.path('posts', :year_month_day_date).
|
46
|
-
@article.path('posts', :year_month_date).
|
47
|
-
@article.path('posts', :year_date).
|
48
|
-
@article.path('/foo/bar/', :no_date).
|
49
|
-
@article.path('/foo/bar/', :year_month_day_date).
|
50
|
-
@article.path('/foo/bar/', :year_month_date).
|
51
|
-
@article.path('/foo/bar/', :year_date).
|
40
|
+
expect(@article.path('', :no_date)).to eq('/poems/the-road-not-taken/')
|
41
|
+
expect(@article.path('', :year_month_day_date)).to eq('/poems/1916/01/01/the-road-not-taken/')
|
42
|
+
expect(@article.path('', :year_month_date)).to eq('/poems/1916/01/the-road-not-taken/')
|
43
|
+
expect(@article.path('', :year_date)).to eq('/poems/1916/the-road-not-taken/')
|
44
|
+
expect(@article.path('posts', :no_date)).to eq('/posts/poems/the-road-not-taken/')
|
45
|
+
expect(@article.path('posts', :year_month_day_date)).to eq('/posts/poems/1916/01/01/the-road-not-taken/')
|
46
|
+
expect(@article.path('posts', :year_month_date)).to eq('/posts/poems/1916/01/the-road-not-taken/')
|
47
|
+
expect(@article.path('posts', :year_date)).to eq('/posts/poems/1916/the-road-not-taken/')
|
48
|
+
expect(@article.path('/foo/bar/', :no_date)).to eq('/foo/bar/poems/the-road-not-taken/')
|
49
|
+
expect(@article.path('/foo/bar/', :year_month_day_date)).to eq('/foo/bar/poems/1916/01/01/the-road-not-taken/')
|
50
|
+
expect(@article.path('/foo/bar/', :year_month_date)).to eq('/foo/bar/poems/1916/01/the-road-not-taken/')
|
51
|
+
expect(@article.path('/foo/bar/', :year_date)).to eq('/foo/bar/poems/1916/the-road-not-taken/')
|
52
52
|
end
|
53
|
+
|
54
|
+
it "should create values correctly" do
|
55
|
+
expect(Baron::Article.create_slug "B's Cookies & Cream 2,3,4", Date.new(2016,6,15)).to eq("2016-06-15-b-s-cookies---cream-2-3-4")
|
56
|
+
end
|
57
|
+
|
53
58
|
end
|
54
|
-
end
|
59
|
+
end
|
@@ -14,53 +14,53 @@ describe "Baron::BlogEngine" do
|
|
14
14
|
|
15
15
|
describe "Access all content" do
|
16
16
|
it "finds application paths" do
|
17
|
-
@blog_engine.get_pages_path.
|
18
|
-
@blog_engine.get_articles_path.
|
19
|
-
@blog_engine.get_page_template('about').
|
20
|
-
@blog_engine.get_system_resource('redirects.txt').
|
21
|
-
@blog_engine.get_system_resource('robots.txt').
|
22
|
-
@blog_engine.get_system_resource('feeds.atom').
|
17
|
+
expect(@blog_engine.get_pages_path).to eq(SAMPLE_DATA_PATH + 'pages/')
|
18
|
+
expect(@blog_engine.get_articles_path).to eq(SAMPLE_DATA_PATH + 'articles')
|
19
|
+
expect(@blog_engine.get_page_template('about')).to eq(SAMPLE_DATA_PATH + 'pages/about.rhtml')
|
20
|
+
expect(@blog_engine.get_system_resource('redirects.txt')).to eq(SAMPLE_DATA_PATH + 'resources/redirects.txt')
|
21
|
+
expect(@blog_engine.get_system_resource('robots.txt')).to eq(SAMPLE_DATA_PATH + 'resources/robots.txt')
|
22
|
+
expect(@blog_engine.get_system_resource('feeds.atom')).to eq(SAMPLE_DATA_PATH + 'resources/feeds.atom')
|
23
23
|
end
|
24
24
|
|
25
25
|
it "finds all categories" do
|
26
26
|
categories = @blog_engine.get_all_categories
|
27
|
-
categories.count.
|
28
|
-
categories.first[:name].
|
29
|
-
categories.first[:path].
|
30
|
-
categories.first[:count].
|
31
|
-
categories.last[:name].
|
32
|
-
categories.last[:path].
|
33
|
-
categories.last[:count].
|
27
|
+
expect(categories.count).to eq(3)
|
28
|
+
expect(categories.first[:name]).to eq('Favorites')
|
29
|
+
expect(categories.first[:path]).to eq('/favorites/')
|
30
|
+
expect(categories.first[:count]).to eq(1)
|
31
|
+
expect(categories.last[:name]).to eq('Other Authors')
|
32
|
+
expect(categories.last[:path]).to eq('/other-authors/')
|
33
|
+
expect(categories.last[:count]).to eq(1)
|
34
34
|
end
|
35
35
|
|
36
36
|
# todo, refactor to break out testing that we're appropriately breaking the fileparts down
|
37
37
|
|
38
38
|
it "finds all articles" do
|
39
39
|
articles_fileparts = @blog_engine.get_all_articles()
|
40
|
-
articles_fileparts.count.
|
40
|
+
expect(articles_fileparts.count).to eq(7)
|
41
41
|
end
|
42
42
|
|
43
43
|
it "parses all article fileparts" do
|
44
44
|
articles_fileparts = @blog_engine.get_all_articles()
|
45
|
-
articles_fileparts[0][:filename_and_path].
|
46
|
-
articles_fileparts[0][:date].
|
47
|
-
articles_fileparts[0][:filename].
|
48
|
-
articles_fileparts[0][:category].
|
49
|
-
articles_fileparts[1][:filename_and_path].
|
50
|
-
articles_fileparts[1][:date].
|
51
|
-
articles_fileparts[1][:filename].
|
52
|
-
articles_fileparts[1][:category].
|
53
|
-
articles_fileparts.last[:filename_and_path].
|
54
|
-
articles_fileparts.last[:date].
|
55
|
-
articles_fileparts.last[:filename].
|
56
|
-
articles_fileparts.last[:category].
|
45
|
+
expect(articles_fileparts[0][:filename_and_path]).to eq(SAMPLE_DATA_PATH + 'articles/favorites/1916-01-01-the-road-not-taken.txt')
|
46
|
+
expect(articles_fileparts[0][:date]).to eq('1916-01-01')
|
47
|
+
expect(articles_fileparts[0][:filename]).to eq('the-road-not-taken')
|
48
|
+
expect(articles_fileparts[0][:category]).to eq('favorites')
|
49
|
+
expect(articles_fileparts[1][:filename_and_path]).to eq(SAMPLE_DATA_PATH + 'articles/north of boston/1914-01-05-A-Hundred-callers.txt')
|
50
|
+
expect(articles_fileparts[1][:date]).to eq('1914-01-05')
|
51
|
+
expect(articles_fileparts[1][:filename]).to eq('a-hundred-callers')
|
52
|
+
expect(articles_fileparts[1][:category]).to eq('north of boston')
|
53
|
+
expect(articles_fileparts.last[:filename_and_path]).to eq(SAMPLE_DATA_PATH + 'articles/other authors/1909-01-02-If.txt')
|
54
|
+
expect(articles_fileparts.last[:date]).to eq('1909-01-02')
|
55
|
+
expect(articles_fileparts.last[:filename]).to eq('if')
|
56
|
+
expect(articles_fileparts.last[:category]).to eq('other authors')
|
57
57
|
end
|
58
58
|
|
59
59
|
it "returns all article parts" do
|
60
60
|
@blog_engine.get_all_articles().each do |article_parts|
|
61
|
-
article_parts[:filename_and_path].
|
62
|
-
article_parts[:date].
|
63
|
-
article_parts[:filename].
|
61
|
+
expect(article_parts[:filename_and_path]).not_to eq(nil)
|
62
|
+
expect(article_parts[:date]).not_to eq(nil)
|
63
|
+
expect(article_parts[:filename]).not_to eq(nil)
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end # Access all content
|
data/spec/baron_spec.rb
CHANGED
@@ -6,27 +6,27 @@ require 'spec_helper'
|
|
6
6
|
|
7
7
|
shared_examples_for "Server Response" do
|
8
8
|
it "should be valid" do
|
9
|
-
@response.status.
|
10
|
-
@response.body.length.
|
9
|
+
expect(@response.status).to eq(200)
|
10
|
+
expect(@response.body.length).to be > 0
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
14
|
shared_examples_for "Server HTML Response" do
|
15
15
|
it "should be instrumented" do
|
16
|
-
@response.body.
|
16
|
+
expect(@response.body).to include(GOOGLE_ANALYTICS) unless GOOGLE_ANALYTICS.empty?
|
17
17
|
end
|
18
18
|
|
19
19
|
it "returns HTML content type" do
|
20
|
-
@response['Content-Type'].
|
20
|
+
expect(@response['Content-Type']).to eq('text/html')
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should generate valid HTML" do
|
24
|
-
@response.body.scan(/<html/).count.
|
25
|
-
@response.body.scan(/<\/html>/).count.
|
26
|
-
@response.body.scan(/<head>/).count.
|
27
|
-
@response.body.scan(/<\/head>/).count.
|
28
|
-
@response.body.scan(/<body/).count.
|
29
|
-
@response.body.scan(/<\/body>/).count.
|
24
|
+
expect(@response.body.scan(/<html/).count).to eq(1)
|
25
|
+
expect(@response.body.scan(/<\/html>/).count).to eq(1)
|
26
|
+
expect(@response.body.scan(/<head>/).count).to eq(1)
|
27
|
+
expect(@response.body.scan(/<\/head>/).count).to eq(1)
|
28
|
+
expect(@response.body.scan(/<body/).count).to eq(1)
|
29
|
+
expect(@response.body.scan(/<\/body>/).count).to eq(1)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -45,7 +45,7 @@ describe "Baron" do
|
|
45
45
|
it_behaves_like "Server HTML Response"
|
46
46
|
|
47
47
|
it "is instrumented for Google Webmaster Tools" do
|
48
|
-
@response.body.
|
48
|
+
expect(@response.body).to include(GOOGLE_WEBMASTER) unless GOOGLE_WEBMASTER.empty?
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -93,30 +93,30 @@ describe "Baron" do
|
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should not render a page out of bounds" do
|
96
|
-
@baron.get('/page/0/').status.
|
97
|
-
@baron.get('/page/100/').status.
|
96
|
+
expect(@baron.get('/page/0/').status).to eq(404)
|
97
|
+
expect(@baron.get('/page/100/').status).to eq(404)
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should not render a mal-formed URL" do
|
101
|
-
@baron.get('/page/foobar/').status.
|
101
|
+
expect(@baron.get('/page/foobar/').status).to eq(404)
|
102
102
|
end
|
103
103
|
|
104
104
|
it "should render a valid request" do
|
105
105
|
response = @baron.get('/page/2/')
|
106
|
-
response.status.
|
107
|
-
response.body.length.
|
108
|
-
response.body.
|
106
|
+
expect(response.status).to eq(200)
|
107
|
+
expect(response.body.length).to be > 0
|
108
|
+
expect(response.body).to include(GOOGLE_WEBMASTER) unless GOOGLE_WEBMASTER.empty?
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
112
|
describe "GET error page" do
|
113
113
|
it "returns proper error data" do
|
114
114
|
response = @baron.get('/fake-url-of-impossible-page-give-me-your-404-error')
|
115
|
-
response.status.
|
116
|
-
response.body.
|
117
|
-
response.body.
|
118
|
-
#
|
119
|
-
response.body.
|
115
|
+
expect(response.status).to eq(404)
|
116
|
+
expect(response.body).to include('Page not found')
|
117
|
+
expect(response.body).to include('404')
|
118
|
+
# Should not render in the layout.rhtml if <html is in the first 100 chars
|
119
|
+
expect(response.body).to_not include("<meta name=\"description\" content="">")
|
120
120
|
end
|
121
121
|
|
122
122
|
end
|
@@ -129,16 +129,16 @@ describe "Baron" do
|
|
129
129
|
it_behaves_like "Server Response"
|
130
130
|
|
131
131
|
it "returns expected content" do
|
132
|
-
@response.body.
|
133
|
-
@response.body.
|
134
|
-
@response.body.scan(/<entry>/).count.
|
135
|
-
@response.body.scan(/<\/entry>/).count.
|
136
|
-
@response.body.
|
137
|
-
@response.body.
|
132
|
+
expect(@response.body).to include(@config[:title])
|
133
|
+
expect(@response.body).to include("http://localhost/feed.atom")
|
134
|
+
expect(@response.body.scan(/<entry>/).count).to eq(5)
|
135
|
+
expect(@response.body.scan(/<\/entry>/).count).to eq(5)
|
136
|
+
expect(@response.body).to include('<feed')
|
137
|
+
expect(@response.body).to include('</feed>')
|
138
138
|
end
|
139
139
|
|
140
140
|
it "returns atom content type" do
|
141
|
-
@response['Content-Type'].
|
141
|
+
expect(@response['Content-Type']).to eq('application/atom+xml')
|
142
142
|
end
|
143
143
|
end
|
144
144
|
|
@@ -150,27 +150,20 @@ describe "Baron" do
|
|
150
150
|
it_behaves_like "Server Response"
|
151
151
|
|
152
152
|
it "returns text content type" do
|
153
|
-
@response['Content-Type'].
|
153
|
+
expect(@response['Content-Type']).to eq('text/plain')
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
157
|
describe "Redirect URLs" do
|
158
158
|
it "should canonicalize pagination at page 1" do
|
159
159
|
@response = @baron.get('/page/1/')
|
160
|
-
@response.status.
|
161
|
-
@response['Location'].
|
160
|
+
expect(@response.status).to eq(301)
|
161
|
+
expect(@response['Location']).to eq('/')
|
162
|
+
|
162
163
|
@response = @baron.get('/page/1')
|
163
|
-
@response.status.
|
164
|
-
@response['Location'].
|
164
|
+
expect(@response.status).to eq(301)
|
165
|
+
expect(@response['Location']).to eq('/')
|
165
166
|
end
|
166
167
|
end
|
167
|
-
|
168
|
-
# add a method to test CSS, and a downloaded file, like a ppt
|
169
168
|
|
170
|
-
|
171
|
-
it "should return a titleized string" do
|
172
|
-
"the quick red fox".titleize.should == "The Quick Red Fox"
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
end
|
169
|
+
end
|
data/spec/baron_theme_spec.rb
CHANGED
@@ -13,26 +13,26 @@ describe "Baron::Theme" do
|
|
13
13
|
end
|
14
14
|
|
15
15
|
it "finds all parameters in theme_config.yml" do
|
16
|
-
@theme.root.
|
17
|
-
@theme[:root].
|
18
|
-
@theme[:masthead_url].
|
19
|
-
@theme[:param_test].
|
20
|
-
@theme[:company_description].
|
16
|
+
expect(@theme.root).to eq('/themes/typography')
|
17
|
+
expect(@theme[:root]).to eq('/themes/typography')
|
18
|
+
expect(@theme[:masthead_url]).to eq("http://www.my-corporation.com")
|
19
|
+
expect(@theme[:param_test]).to eq("FOOBAR")
|
20
|
+
expect(@theme[:company_description]).to eq("Insert content here")
|
21
21
|
end
|
22
22
|
|
23
23
|
it "finds all the rendering templates" do
|
24
|
-
@theme.get_template('article').
|
25
|
-
@theme.get_template('category').
|
26
|
-
@theme.get_template('error').
|
27
|
-
@theme.get_template('home').
|
28
|
-
@theme.get_template('layout').
|
24
|
+
expect(@theme.get_template('article')).to eq(SAMPLE_DATA_PATH + 'themes/typography/templates/article.rhtml')
|
25
|
+
expect(@theme.get_template('category')).to eq(SAMPLE_DATA_PATH + 'themes/typography/templates/category.rhtml')
|
26
|
+
expect(@theme.get_template('error')).to eq(SAMPLE_DATA_PATH + 'themes/typography/templates/error.rhtml')
|
27
|
+
expect(@theme.get_template('home')).to eq(SAMPLE_DATA_PATH + 'themes/typography/templates/home.rhtml')
|
28
|
+
expect(@theme.get_template('layout')).to eq(SAMPLE_DATA_PATH + 'themes/typography/templates/layout.rhtml')
|
29
29
|
end
|
30
30
|
|
31
31
|
it "doesn't crash with a bad or empty config file" do
|
32
32
|
theme = Baron::Theme.new({})
|
33
33
|
theme.load_config("#{SAMPLE_DATA_PATH}supplemental-files/theme_config.yml")
|
34
|
-
theme.length.
|
34
|
+
expect(theme.length).to eq(4)
|
35
35
|
theme.load_config("FOOBAR-FAKE-URL-FAKE-FAKE")
|
36
|
-
theme.length.
|
36
|
+
expect(theme.length).to eq(4)
|
37
37
|
end
|
38
38
|
end
|
metadata
CHANGED
@@ -1,49 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: baron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.17
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Nathan Buggia
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2016-07-07 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - ">="
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - ">="
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rack
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - ">="
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - ">="
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
|
-
description:
|
41
|
+
description: 'What''s in the box: (1) Publish to Heroku using Git (2) Author articles
|
47
42
|
in markdown (3) Article categories (4) Multiple permalink formats supported (5)
|
48
43
|
Redirects (6) Advanced SEO optimizations and Google Analytics/ Webmaster Tools support.
|
49
44
|
Uses Rack, RSpec, Bootstrap, JQuery, Disqus, Thin'
|
@@ -64,7 +59,6 @@ files:
|
|
64
59
|
- lib/baron/models/article.rb
|
65
60
|
- lib/baron/models/theme.rb
|
66
61
|
- lib/baron/page_controller.rb
|
67
|
-
- lib/baron/utils.rb
|
68
62
|
- spec/baron_article_spec.rb
|
69
63
|
- spec/baron_blog_engine_spec.rb
|
70
64
|
- spec/baron_spec.rb
|
@@ -111,26 +105,25 @@ files:
|
|
111
105
|
- spec/spec_helper.rb
|
112
106
|
homepage: https://github.com/nbuggia/baron-blog-engine-gem
|
113
107
|
licenses: []
|
108
|
+
metadata: {}
|
114
109
|
post_install_message:
|
115
110
|
rdoc_options: []
|
116
111
|
require_paths:
|
117
112
|
- lib
|
118
113
|
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
-
none: false
|
120
114
|
requirements:
|
121
|
-
- -
|
115
|
+
- - ">="
|
122
116
|
- !ruby/object:Gem::Version
|
123
117
|
version: '0'
|
124
118
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
-
none: false
|
126
119
|
requirements:
|
127
|
-
- -
|
120
|
+
- - ">="
|
128
121
|
- !ruby/object:Gem::Version
|
129
122
|
version: '0'
|
130
123
|
requirements: []
|
131
124
|
rubyforge_project:
|
132
|
-
rubygems_version:
|
125
|
+
rubygems_version: 2.5.1
|
133
126
|
signing_key:
|
134
|
-
specification_version:
|
127
|
+
specification_version: 4
|
135
128
|
summary: Minimalist, yet full-featured, blog engine.
|
136
129
|
test_files: []
|
data/lib/baron/utils.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# Converts a number into an ordinal, 1=>1st, 2=>2nd, 3=>3rd, etc
|
2
|
-
class Fixnum
|
3
|
-
def ordinal
|
4
|
-
case self % 100
|
5
|
-
when 11..13; "#{self}th"
|
6
|
-
else
|
7
|
-
case self % 10
|
8
|
-
when 1; "#{self}st"
|
9
|
-
when 2; "#{self}nd"
|
10
|
-
when 3; "#{self}rd"
|
11
|
-
else "#{self}th"
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
# Avoid a collision with ActiveSupport
|
18
|
-
|
19
|
-
class String
|
20
|
-
# Support String::bytesize in old versions of Ruby
|
21
|
-
if RUBY_VERSION < "1.9"
|
22
|
-
def bytesize
|
23
|
-
size
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Capitalize the first letter of each word in a string
|
28
|
-
def titleize
|
29
|
-
self.split(/(\W)/).map(&:capitalize).join
|
30
|
-
end
|
31
|
-
end
|