ruby_slippers 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rvmrc +2 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +45 -0
- data/Guardfile +16 -0
- data/LICENSE +20 -0
- data/README.md +122 -0
- data/Rakefile +5 -0
- data/TODO +32 -0
- data/VERSION +1 -0
- data/lib/ext/ext.rb +164 -0
- data/lib/ruby_slippers.rb +28 -0
- data/lib/ruby_slippers/app.rb +47 -0
- data/lib/ruby_slippers/archives.rb +22 -0
- data/lib/ruby_slippers/article.rb +104 -0
- data/lib/ruby_slippers/config.rb +37 -0
- data/lib/ruby_slippers/context.rb +38 -0
- data/lib/ruby_slippers/engine.rb +21 -0
- data/lib/ruby_slippers/repo.rb +21 -0
- data/lib/ruby_slippers/site.rb +115 -0
- data/lib/ruby_slippers/template.rb +28 -0
- data/lib/tasks/gemspec.rake +24 -0
- data/lib/tasks/test.rake +17 -0
- data/test/fixtures/articles/2010-05-17-the-wonderful-wizard-of-oz.txt +6 -0
- data/test/fixtures/articles/2010-05-18-the-marvelous-land-of-oz.txt +7 -0
- data/test/fixtures/articles/2010-05-20-dorothy-and-the-wizard-of-oz.txt +6 -0
- data/test/fixtures/articles/2011-05-18-ozma-of-oz.txt +10 -0
- data/test/fixtures/images/ozma.png +0 -0
- data/test/fixtures/pages/about.html.erb +1 -0
- data/test/fixtures/pages/archives.html.erb +23 -0
- data/test/fixtures/pages/article.html.erb +28 -0
- data/test/fixtures/pages/index.html.erb +27 -0
- data/test/fixtures/pages/sitemap.html.erb +0 -0
- data/test/fixtures/pages/tagged.html.erb +9 -0
- data/test/fixtures/templates/index.builder +21 -0
- data/test/fixtures/templates/layout.html.erb +4 -0
- data/test/fixtures/templates/repo.html.erb +1 -0
- data/test/fixtures/templates/sitemap.builder +25 -0
- data/test/integration/about_test.rb +23 -0
- data/test/integration/archives_test.rb +34 -0
- data/test/integration/articles_test.rb +56 -0
- data/test/integration/atom_test.rb +24 -0
- data/test/integration/invalid_route_test.rb +31 -0
- data/test/integration/tags_test.rb +24 -0
- data/test/support/test_helper.rb +47 -0
- data/test/unit/app_test.rb +14 -0
- data/test/unit/archives_test.rb +10 -0
- data/test/unit/article_test.rb +145 -0
- data/test/unit/date_patch_test.rb +10 -0
- data/test/unit/engine_test.rb +71 -0
- data/test/unit/site_test.rb +52 -0
- metadata +270 -0
data/.rvmrc
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'http://rubygems.org'
|
2
|
+
|
3
|
+
gem 'rake'
|
4
|
+
gem 'rack'
|
5
|
+
gem 'hpricot'
|
6
|
+
gem 'builder'
|
7
|
+
gem 'rdiscount'
|
8
|
+
gem 'maruku'
|
9
|
+
|
10
|
+
group :test, :development do
|
11
|
+
gem 'guard'
|
12
|
+
gem 'guard-bundler'
|
13
|
+
gem 'guard-test'
|
14
|
+
gem 'riot'
|
15
|
+
gem 'jeweler'
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
builder (3.0.0)
|
5
|
+
git (1.2.5)
|
6
|
+
guard (0.4.2)
|
7
|
+
thor (~> 0.14.6)
|
8
|
+
guard-bundler (0.1.3)
|
9
|
+
bundler (>= 1.0.0)
|
10
|
+
guard (>= 0.2.2)
|
11
|
+
guard-test (0.3.0)
|
12
|
+
guard (>= 0.2.2)
|
13
|
+
test-unit (~> 2.2)
|
14
|
+
hpricot (0.8.4)
|
15
|
+
jeweler (1.6.2)
|
16
|
+
bundler (~> 1.0)
|
17
|
+
git (>= 1.2.5)
|
18
|
+
rake
|
19
|
+
maruku (0.6.0)
|
20
|
+
syntax (>= 1.0.0)
|
21
|
+
rack (1.3.0)
|
22
|
+
rake (0.9.2)
|
23
|
+
rdiscount (1.6.8)
|
24
|
+
riot (0.12.4)
|
25
|
+
rr
|
26
|
+
rr (1.0.2)
|
27
|
+
syntax (1.0.0)
|
28
|
+
test-unit (2.3.0)
|
29
|
+
thor (0.14.6)
|
30
|
+
|
31
|
+
PLATFORMS
|
32
|
+
ruby
|
33
|
+
|
34
|
+
DEPENDENCIES
|
35
|
+
builder
|
36
|
+
guard
|
37
|
+
guard-bundler
|
38
|
+
guard-test
|
39
|
+
hpricot
|
40
|
+
jeweler
|
41
|
+
maruku
|
42
|
+
rack
|
43
|
+
rake
|
44
|
+
rdiscount
|
45
|
+
riot
|
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
guard 'bundler' do
|
2
|
+
watch('Gemfile')
|
3
|
+
watch('Gemfile.lock')
|
4
|
+
end
|
5
|
+
|
6
|
+
guard 'test' do
|
7
|
+
watch(%r{^test/fixtures/articles/(.+)\.txt$})
|
8
|
+
watch(%r{^test/fixtures/templates/(.+)$})
|
9
|
+
watch(%r{^test/fixtures/pages/(.+)$})
|
10
|
+
|
11
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
|
12
|
+
watch(%r{^lib/ruby_slippers/(.+)\.rb$}) { |m| ["test/unit/#{m[1]}_test.rb", "test/integration/#{m[1]}_test.rb"] }
|
13
|
+
watch(%r{^test/integration/.+_test\.rb$})
|
14
|
+
watch(%r{^test/unit/.+_test\.rb$})
|
15
|
+
watch(%r{^test/support/(.+)\.rb$}) { "test" }
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 dreamr
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
README
|
2
|
+
======
|
3
|
+
|
4
|
+
RubySlippers, the smartest blogging engine in all of Oz!
|
5
|
+
--------------------------------------------------------
|
6
|
+
|
7
|
+
[website](http://ruby-slippers.heroku.com)
|
8
|
+
|
9
|
+
Introduction
|
10
|
+
------------
|
11
|
+
|
12
|
+
### To set up a new blog
|
13
|
+
|
14
|
+
$ git clone git://github.com/dreamr/ruby-slippers.git myblog
|
15
|
+
$ cd myblog
|
16
|
+
$ gem install bundler
|
17
|
+
$ bundle
|
18
|
+
$ rake install
|
19
|
+
$ -> Blog name: My new blog
|
20
|
+
$ Installing your blog to my-new-blog
|
21
|
+
$ Blog installed!
|
22
|
+
|
23
|
+
|
24
|
+
### To create an article
|
25
|
+
|
26
|
+
$ rake new
|
27
|
+
$ -> Title: My new blog post!
|
28
|
+
$ Creating and opening my-new-blog-post (opens in your text editor!)
|
29
|
+
$ rake publish (commits, pushes, publishes then opens in your browser!)
|
30
|
+
|
31
|
+
|
32
|
+
Philosophy
|
33
|
+
----------
|
34
|
+
|
35
|
+
RubySlippers is based on [Toto](http://github.com/cloudhead/toto) and aims to achieve their goals as well as our own. Hosting a ruby based free blog shouldn't be hard. We want to take that a step further and say it should be easy. Easy as pie. Easy as my best friend's Mom. Easy as a 1 click installer.
|
36
|
+
|
37
|
+
Oh yeah, MRI, bytecode whatever. If it is Ruby, it should run.
|
38
|
+
|
39
|
+
How it works
|
40
|
+
------------
|
41
|
+
|
42
|
+
- Article management is done with a text editor and git
|
43
|
+
* stored as _.txt_ files, with embeded metadata (in yaml format).
|
44
|
+
* processed through a markdown converter (rdiscount) by default.
|
45
|
+
* can have tags
|
46
|
+
* can have images
|
47
|
+
* can be browsed by date, or tags
|
48
|
+
* comments are handled by [disqus](http://disqus.com)
|
49
|
+
- built for easy use with _ERB_.
|
50
|
+
- built right on top of _Rack_.
|
51
|
+
- built to take advantage of _HTTP caching_.
|
52
|
+
- built with _heroku_ in mind.
|
53
|
+
|
54
|
+
|
55
|
+
RubySlippers comes with a basic default theme for you to mangle. I hope to release more themes shortly and will accept your submitted themes.
|
56
|
+
|
57
|
+
Deployment
|
58
|
+
==========
|
59
|
+
|
60
|
+
#### On heroku
|
61
|
+
|
62
|
+
$ git add .
|
63
|
+
$ git commit -m 'updated blog'
|
64
|
+
$ git push heroku
|
65
|
+
|
66
|
+
#### On your own server or locally
|
67
|
+
|
68
|
+
Once you have created the remote git repo, and pushed your changes to it, you can run RubySlippers with any Rack compliant web server, such as **thin**, **mongrel** or **unicorn**.
|
69
|
+
|
70
|
+
I like to use shotgun as it reloads while I work
|
71
|
+
|
72
|
+
$ cd myblog
|
73
|
+
$ bundle
|
74
|
+
$ shotgun
|
75
|
+
|
76
|
+
With thin, you would do something like:
|
77
|
+
|
78
|
+
$ thin start -R config.ru
|
79
|
+
|
80
|
+
With unicorn, you can just do:
|
81
|
+
|
82
|
+
$ unicorn
|
83
|
+
|
84
|
+
|
85
|
+
### Configuration
|
86
|
+
|
87
|
+
You can configure ruby-slippers, by modifying the _config.ru_ file. For example, if you want to set the blog author to 'John Galt',
|
88
|
+
you could add `set :author, 'John Galt'` inside the `RubySlippers::Engine::App.new` block. Here are the defaults, to get you started:
|
89
|
+
|
90
|
+
#
|
91
|
+
# Add your settings here
|
92
|
+
# set [:setting], [value]
|
93
|
+
#
|
94
|
+
set :author, "Dreamr" # blog author
|
95
|
+
set :title, "RubySlippers, the smartest blog engine in all of Oz!" # site title
|
96
|
+
# set :root, "index" # page to load on /
|
97
|
+
set :date, lambda {|now| now.strftime("%m/%d/%Y") } # date format for articles
|
98
|
+
# set :markdown, :smart # use markdown + smart-mode
|
99
|
+
# set :disqus, false # disqus id, or false
|
100
|
+
set :summary, :max => 300, :delim => /~/ # length of article summary and delimiter
|
101
|
+
# set :ext, 'txt' # file extension for articles
|
102
|
+
# set :cache, 28800 # cache duration, in seconds
|
103
|
+
set :tag_separator, ', ' # tag separator for articles
|
104
|
+
set :date, lambda {|now| now.strftime("%B #{now.day.ordinal} %Y") }
|
105
|
+
# set this to your local port. I use shotgun, so 9393.
|
106
|
+
set :url, "http://localhost:9393" if ENV['RACK_ENV'] == 'development'
|
107
|
+
|
108
|
+
# to use haml, add the gem to your Gemfile and bundle, then uncomment this
|
109
|
+
# and redo your templates using haml and renamed to html.haml
|
110
|
+
# set :to_html, lambda { |path, page, binding|
|
111
|
+
# Haml::Engine.new(File.read("#{path}/#{page}.html.haml"),
|
112
|
+
# :attr_wrapper => '"',
|
113
|
+
# :filename => path ).render(binding)
|
114
|
+
# }
|
115
|
+
|
116
|
+
Thanks
|
117
|
+
------
|
118
|
+
|
119
|
+
* To heroku for making this easy as pie.
|
120
|
+
* To the developers of [Toto](http://github.com/cloudhead/toto), for making such an awesome minimal blog engine in Ruby.
|
121
|
+
|
122
|
+
Copyright (c) 2011 dreamr. See LICENSE for details.
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
TODO
|
2
|
+
====
|
3
|
+
|
4
|
+
Site features
|
5
|
+
-------------
|
6
|
+
Contact form (email)
|
7
|
+
* reverse captcha
|
8
|
+
* must be free service
|
9
|
+
|
10
|
+
|
11
|
+
Blog features
|
12
|
+
-----------------------------
|
13
|
+
|
14
|
+
* Most popular posts (hits)
|
15
|
+
* Most commented posts (comments)
|
16
|
+
* Related posts (by tag)
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
Full stack test/deployment scheme
|
21
|
+
-------------------------------------------
|
22
|
+
|
23
|
+
* Engine
|
24
|
+
[*] Unit Tests
|
25
|
+
[*] Integration Tests
|
26
|
+
|
27
|
+
* Staged (deployed)
|
28
|
+
[*] Integration Tests
|
29
|
+
[ ] Client Rake Tests
|
30
|
+
|
31
|
+
|
32
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/lib/ext/ext.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
class Array
|
4
|
+
# Splits or iterates over the array in groups of size +number+,
|
5
|
+
# padding any remaining slots with +fill_with+ unless it is +false+.
|
6
|
+
#
|
7
|
+
# %w(1 2 3 4 5 6 7).in_groups_of(3) {|group| p group}
|
8
|
+
# ["1", "2", "3"]
|
9
|
+
# ["4", "5", "6"]
|
10
|
+
# ["7", nil, nil]
|
11
|
+
#
|
12
|
+
# %w(1 2 3).in_groups_of(2, ' ') {|group| p group}
|
13
|
+
# ["1", "2"]
|
14
|
+
# ["3", " "]
|
15
|
+
#
|
16
|
+
# %w(1 2 3).in_groups_of(2, false) {|group| p group}
|
17
|
+
# ["1", "2"]
|
18
|
+
# ["3"]
|
19
|
+
def in_groups_of(number, fill_with = nil)
|
20
|
+
if fill_with == false
|
21
|
+
collection = self
|
22
|
+
else
|
23
|
+
# size % number gives how many extra we have;
|
24
|
+
# subtracting from number gives how many to add;
|
25
|
+
# modulo number ensures we don't add group of just fill.
|
26
|
+
padding = (number - size % number) % number
|
27
|
+
collection = dup.concat([fill_with] * padding)
|
28
|
+
end
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
collection.each_slice(number) { |slice| yield(slice) }
|
32
|
+
else
|
33
|
+
groups = []
|
34
|
+
collection.each_slice(number) { |group| groups << group }
|
35
|
+
groups
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Splits or iterates over the array in +number+ of groups, padding any
|
40
|
+
# remaining slots with +fill_with+ unless it is +false+.
|
41
|
+
#
|
42
|
+
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
|
43
|
+
# ["1", "2", "3", "4"]
|
44
|
+
# ["5", "6", "7", nil]
|
45
|
+
# ["8", "9", "10", nil]
|
46
|
+
#
|
47
|
+
# %w(1 2 3 4 5 6 7).in_groups(3, ' ') {|group| p group}
|
48
|
+
# ["1", "2", "3"]
|
49
|
+
# ["4", "5", " "]
|
50
|
+
# ["6", "7", " "]
|
51
|
+
#
|
52
|
+
# %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
|
53
|
+
# ["1", "2", "3"]
|
54
|
+
# ["4", "5"]
|
55
|
+
# ["6", "7"]
|
56
|
+
def in_groups(number, fill_with = nil)
|
57
|
+
# size / number gives minor group size;
|
58
|
+
# size % number gives how many objects need extra accommodation;
|
59
|
+
# each group hold either division or division + 1 items.
|
60
|
+
division = size / number
|
61
|
+
modulo = size % number
|
62
|
+
|
63
|
+
# create a new array avoiding dup
|
64
|
+
groups = []
|
65
|
+
start = 0
|
66
|
+
|
67
|
+
number.times do |index|
|
68
|
+
length = division + (modulo > 0 && modulo > index ? 1 : 0)
|
69
|
+
padding = fill_with != false &&
|
70
|
+
modulo > 0 && length == division ? 1 : 0
|
71
|
+
groups << slice(start, length).concat([fill_with] * padding)
|
72
|
+
start += length
|
73
|
+
end
|
74
|
+
|
75
|
+
if block_given?
|
76
|
+
groups.each { |g| yield(g) }
|
77
|
+
else
|
78
|
+
groups
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Divides the array into one or more subarrays based on a delimiting +value+
|
83
|
+
# or the result of an optional block.
|
84
|
+
#
|
85
|
+
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
|
86
|
+
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
|
87
|
+
def split(value = nil)
|
88
|
+
using_block = block_given?
|
89
|
+
|
90
|
+
inject([[]]) do |results, element|
|
91
|
+
if (using_block && yield(element)) || (value == element)
|
92
|
+
results << []
|
93
|
+
else
|
94
|
+
results.last << element
|
95
|
+
end
|
96
|
+
|
97
|
+
results
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Object
|
103
|
+
def meta_def name, &blk
|
104
|
+
(class << self; self; end).instance_eval do
|
105
|
+
define_method(name, &blk)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class String
|
111
|
+
def slugize
|
112
|
+
self.downcase.gsub(/&/, 'and').gsub(/\s+/, '-').gsub(/[^a-z0-9-]/, '')
|
113
|
+
end
|
114
|
+
|
115
|
+
def humanize
|
116
|
+
self.capitalize.gsub(/[-_]+/, ' ')
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Fixnum
|
121
|
+
def ordinal
|
122
|
+
# 1 => 1st
|
123
|
+
# 2 => 2nd
|
124
|
+
# 3 => 3rd
|
125
|
+
# ...
|
126
|
+
case self % 100
|
127
|
+
when 11..13; "#{self}th"
|
128
|
+
else
|
129
|
+
case self % 10
|
130
|
+
when 1; "#{self}st"
|
131
|
+
when 2; "#{self}nd"
|
132
|
+
when 3; "#{self}rd"
|
133
|
+
else "#{self}th"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class Object
|
140
|
+
def blank?
|
141
|
+
self.nil? || self == ""
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class Date
|
146
|
+
# This check is for people running RubySlippers::Enginewith ActiveSupport, avoid a collision
|
147
|
+
unless respond_to? :iso8601
|
148
|
+
# Return the date as a String formatted according to ISO 8601.
|
149
|
+
def iso8601
|
150
|
+
::Time.utc(year, month, day, 0, 0, 0, 0).iso8601
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
module Kernel
|
156
|
+
def silence_stream(stream)
|
157
|
+
old_stream = stream.dup
|
158
|
+
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
|
159
|
+
stream.sync = true
|
160
|
+
yield
|
161
|
+
ensure
|
162
|
+
stream.reopen(old_stream)
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'date'
|
3
|
+
require 'erb'
|
4
|
+
require 'rack'
|
5
|
+
require 'digest'
|
6
|
+
require 'open-uri'
|
7
|
+
|
8
|
+
if RUBY_PLATFORM =~ /win32/
|
9
|
+
require 'maruku'
|
10
|
+
Markdown = Maruku
|
11
|
+
else
|
12
|
+
require 'rdiscount'
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'builder'
|
16
|
+
|
17
|
+
$:.unshift File.dirname(__FILE__)
|
18
|
+
|
19
|
+
require 'ext/ext'
|
20
|
+
require 'ruby_slippers/app'
|
21
|
+
require 'ruby_slippers/site'
|
22
|
+
require 'ruby_slippers/engine'
|
23
|
+
require 'ruby_slippers/config'
|
24
|
+
require 'ruby_slippers/template'
|
25
|
+
require 'ruby_slippers/context'
|
26
|
+
require 'ruby_slippers/article'
|
27
|
+
require 'ruby_slippers/archives'
|
28
|
+
require 'ruby_slippers/repo'
|