baron 1.0.14 → 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|