ruby-slippers 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +2 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +90 -0
- data/Guardfile +16 -0
- data/LICENSE +20 -0
- data/README.md +114 -0
- data/Rakefile +5 -0
- data/VERSION +1 -0
- data/lib/ext/ext.rb +57 -0
- data/lib/ruby_slippers/app.rb +41 -0
- data/lib/ruby_slippers/archives.rb +22 -0
- data/lib/ruby_slippers/article.rb +84 -0
- data/lib/ruby_slippers/config.rb +35 -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 +103 -0
- data/lib/ruby_slippers/template.rb +28 -0
- data/lib/ruby_slippers.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 +6 -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 +8 -0
- data/test/fixtures/images/ozma.png +0 -0
- data/test/fixtures/pages/about.rhtml +1 -0
- data/test/fixtures/pages/archives.rhtml +5 -0
- data/test/fixtures/pages/article.rhtml +4 -0
- data/test/fixtures/pages/index.rhtml +13 -0
- data/test/fixtures/pages/tagged.rhtml +9 -0
- data/test/fixtures/templates/index.builder +21 -0
- data/test/fixtures/templates/layout.rhtml +4 -0
- data/test/fixtures/templates/repo.rhtml +1 -0
- data/test/integration/article_test.rb +44 -0
- data/test/integration/atom_test.rb +19 -0
- data/test/integration/ruby_slippers_test.rb +77 -0
- data/test/support/test_helper.rb +46 -0
- data/test/unit/archives_test.rb +8 -0
- data/test/unit/article_test.rb +118 -0
- data/test/unit/ruby_slippers_test.rb +75 -0
- metadata +318 -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,90 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
builder (3.0.0)
|
5
|
+
capybara (1.0.0)
|
6
|
+
mime-types (>= 1.16)
|
7
|
+
nokogiri (>= 1.3.3)
|
8
|
+
rack (>= 1.0.0)
|
9
|
+
rack-test (>= 0.5.4)
|
10
|
+
selenium-webdriver (~> 0.2.0)
|
11
|
+
xpath (~> 0.1.4)
|
12
|
+
childprocess (0.1.9)
|
13
|
+
ffi (~> 1.0.6)
|
14
|
+
cucumber (1.0.0)
|
15
|
+
builder (>= 2.1.2)
|
16
|
+
diff-lcs (>= 1.1.2)
|
17
|
+
gherkin (~> 2.4.1)
|
18
|
+
json (>= 1.4.6)
|
19
|
+
term-ansicolor (>= 1.0.5)
|
20
|
+
diff-lcs (1.1.2)
|
21
|
+
ffi (1.0.9)
|
22
|
+
gherkin (2.4.1)
|
23
|
+
json (>= 1.4.6)
|
24
|
+
git (1.2.5)
|
25
|
+
guard (0.4.2)
|
26
|
+
thor (~> 0.14.6)
|
27
|
+
guard-bundler (0.1.3)
|
28
|
+
bundler (>= 1.0.0)
|
29
|
+
guard (>= 0.2.2)
|
30
|
+
guard-test (0.3.0)
|
31
|
+
guard (>= 0.2.2)
|
32
|
+
test-unit (~> 2.2)
|
33
|
+
hpricot (0.8.4)
|
34
|
+
jeweler (1.6.2)
|
35
|
+
bundler (~> 1.0)
|
36
|
+
git (>= 1.2.5)
|
37
|
+
rake
|
38
|
+
json (1.5.3)
|
39
|
+
json_pure (1.5.3)
|
40
|
+
maruku (0.6.0)
|
41
|
+
syntax (>= 1.0.0)
|
42
|
+
mime-types (1.16)
|
43
|
+
nokogiri (1.4.6)
|
44
|
+
rack (1.3.0)
|
45
|
+
rack-test (0.6.0)
|
46
|
+
rack (>= 1.0)
|
47
|
+
rake (0.9.2)
|
48
|
+
rdiscount (1.6.8)
|
49
|
+
riot (0.12.4)
|
50
|
+
rr
|
51
|
+
rr (1.0.2)
|
52
|
+
rspec (2.6.0)
|
53
|
+
rspec-core (~> 2.6.0)
|
54
|
+
rspec-expectations (~> 2.6.0)
|
55
|
+
rspec-mocks (~> 2.6.0)
|
56
|
+
rspec-core (2.6.4)
|
57
|
+
rspec-expectations (2.6.0)
|
58
|
+
diff-lcs (~> 1.1.2)
|
59
|
+
rspec-mocks (2.6.0)
|
60
|
+
rubyzip (0.9.4)
|
61
|
+
selenium-webdriver (0.2.1)
|
62
|
+
childprocess (>= 0.1.7)
|
63
|
+
ffi (>= 1.0.7)
|
64
|
+
json_pure
|
65
|
+
rubyzip
|
66
|
+
syntax (1.0.0)
|
67
|
+
term-ansicolor (1.0.5)
|
68
|
+
test-unit (2.3.0)
|
69
|
+
thor (0.14.6)
|
70
|
+
xpath (0.1.4)
|
71
|
+
nokogiri (~> 1.3)
|
72
|
+
|
73
|
+
PLATFORMS
|
74
|
+
ruby
|
75
|
+
|
76
|
+
DEPENDENCIES
|
77
|
+
builder
|
78
|
+
capybara
|
79
|
+
cucumber
|
80
|
+
guard
|
81
|
+
guard-bundler
|
82
|
+
guard-test
|
83
|
+
hpricot
|
84
|
+
jeweler
|
85
|
+
maruku
|
86
|
+
rack
|
87
|
+
rake
|
88
|
+
rdiscount
|
89
|
+
riot
|
90
|
+
rspec
|
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,114 @@
|
|
1
|
+
RubySlippers, the smartest blogging engine in all of Oz!
|
2
|
+
========================================================
|
3
|
+
|
4
|
+
[website](http://ruby-slippers.heroku.com)
|
5
|
+
|
6
|
+
Introduction
|
7
|
+
------------
|
8
|
+
|
9
|
+
|
10
|
+
For Developers
|
11
|
+
--------------
|
12
|
+
|
13
|
+
#### To set up a new blog
|
14
|
+
|
15
|
+
$ git clone git://github.com/dreamr/ruby-slippers.git myblog
|
16
|
+
$ cd myblog
|
17
|
+
$ gem install bundler
|
18
|
+
$ bundle
|
19
|
+
$ rake install
|
20
|
+
$ -> Blog name: My new blog
|
21
|
+
$ Installing your blog to my-new-blog
|
22
|
+
$ Blog installed!
|
23
|
+
|
24
|
+
|
25
|
+
#### To create an article
|
26
|
+
|
27
|
+
$ rake new
|
28
|
+
$ -> Title: My new blog post!
|
29
|
+
$ Creating and opening my-new-blog-post (opens in your text editor!)
|
30
|
+
$ rake publish (commits, pushes, publishes then opens in your browser!)
|
31
|
+
|
32
|
+
|
33
|
+
Philosophy
|
34
|
+
----------
|
35
|
+
|
36
|
+
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.
|
37
|
+
|
38
|
+
Oh yeah, MRI, bytecode whatever. If it is Ruby, it should run.
|
39
|
+
|
40
|
+
How it works
|
41
|
+
------------
|
42
|
+
|
43
|
+
- Article management is done with a text editor and git
|
44
|
+
* stored as _.txt_ files, with embeded metadata (in yaml format).
|
45
|
+
* processed through a markdown converter (rdiscount) by default.
|
46
|
+
* can have tags
|
47
|
+
* can have images
|
48
|
+
* can be browsed by date, or tags
|
49
|
+
* comments are handled by [disqus](http://disqus.com)
|
50
|
+
- built for easy use with _ERB_.
|
51
|
+
- built right on top of _Rack_.
|
52
|
+
- built to take advantage of _HTTP caching_.
|
53
|
+
- built with _heroku_ in mind.
|
54
|
+
|
55
|
+
|
56
|
+
RubySlippers comes with a basic default theme for you to mangle. I hope to release more themes shortly and will accept your submitted themes.
|
57
|
+
|
58
|
+
Deployment
|
59
|
+
==========
|
60
|
+
|
61
|
+
#### on heroku
|
62
|
+
|
63
|
+
RubySlippers comes with a basic rackup file. To start it up locally do:
|
64
|
+
|
65
|
+
$ cd myblog
|
66
|
+
$ bundle
|
67
|
+
$ shotgun
|
68
|
+
[2011-06-20 17:04:46] INFO WEBrick::HTTPServer#start: pid=61628 port=9393
|
69
|
+
|
70
|
+
#### on your own server
|
71
|
+
|
72
|
+
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**.
|
73
|
+
|
74
|
+
With thin, you would do something like:
|
75
|
+
|
76
|
+
$ thin start -R config.ru
|
77
|
+
|
78
|
+
With unicorn, you can just do:
|
79
|
+
|
80
|
+
$ unicorn
|
81
|
+
|
82
|
+
|
83
|
+
### Configuration
|
84
|
+
|
85
|
+
You can configure ruby-slippers, by modifying the _config.ru_ file. For example, if you want to set the blog author to 'John Galt',
|
86
|
+
you could add `set :author, 'John Galt'` inside the `RubySlippers::Engine::App.new` block. Here are the defaults, to get you started:
|
87
|
+
|
88
|
+
set :author, ENV['USER'] # blog author
|
89
|
+
set :title, Dir.pwd.split('/').last # site title
|
90
|
+
set :url, 'http://example.com' # site root URL
|
91
|
+
set :prefix, '' # common path prefix for all pages
|
92
|
+
set :root, "index" # page to load on /
|
93
|
+
set :date, lambda {|now| now.strftime("%d/%m/%Y") } # date format for articles
|
94
|
+
set :markdown, :smart # use markdown + smart-mode
|
95
|
+
set :disqus, false # disqus id, or false
|
96
|
+
set :summary, :max => 150, :delim => /~\n/ # length of article summary and delimiter
|
97
|
+
set :ext, 'txt' # file extension for articles
|
98
|
+
set :cache, 28800 # cache site for 8 hours
|
99
|
+
|
100
|
+
set :to_html do |path, page, ctx| # returns an html, from a path & context
|
101
|
+
ERB.new(File.read("#{path}/#{page}.rhtml")).result(ctx)
|
102
|
+
end
|
103
|
+
|
104
|
+
set :error do |code| # The HTML for your error page
|
105
|
+
"<font style='font-size:300%'>A large house has landed on you. You cannot continue because you are dead. <a href='/'>try again</a> (#{code})</font>"
|
106
|
+
end
|
107
|
+
|
108
|
+
Thanks
|
109
|
+
------
|
110
|
+
|
111
|
+
* To heroku for making this easy as pie.
|
112
|
+
* To the developers of [Toto](http://github.com/cloudhead/toto), for making such an awesome minimal blog engine in Ruby.
|
113
|
+
|
114
|
+
Copyright (c) 2011 dreamr. See LICENSE for details.
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/ext/ext.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
class Object
|
2
|
+
def meta_def name, &blk
|
3
|
+
(class << self; self; end).instance_eval do
|
4
|
+
define_method(name, &blk)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class String
|
10
|
+
def slugize
|
11
|
+
self.downcase.gsub(/&/, 'and').gsub(/\s+/, '-').gsub(/[^a-z0-9-]/, '')
|
12
|
+
end
|
13
|
+
|
14
|
+
def humanize
|
15
|
+
self.capitalize.gsub(/[-_]+/, ' ')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Fixnum
|
20
|
+
def ordinal
|
21
|
+
# 1 => 1st
|
22
|
+
# 2 => 2nd
|
23
|
+
# 3 => 3rd
|
24
|
+
# ...
|
25
|
+
case self % 100
|
26
|
+
when 11..13; "#{self}th"
|
27
|
+
else
|
28
|
+
case self % 10
|
29
|
+
when 1; "#{self}st"
|
30
|
+
when 2; "#{self}nd"
|
31
|
+
when 3; "#{self}rd"
|
32
|
+
else "#{self}th"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Date
|
39
|
+
# This check is for people running RubySlippers::Enginewith ActiveSupport, avoid a collision
|
40
|
+
unless respond_to? :iso8601
|
41
|
+
# Return the date as a String formatted according to ISO 8601.
|
42
|
+
def iso8601
|
43
|
+
::Time.utc(year, month, day, 0, 0, 0, 0).iso8601
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Kernel
|
49
|
+
def silence_stream(stream)
|
50
|
+
old_stream = stream.dup
|
51
|
+
stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
|
52
|
+
stream.sync = true
|
53
|
+
yield
|
54
|
+
ensure
|
55
|
+
stream.reopen(old_stream)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
class App
|
4
|
+
attr_reader :config, :site
|
5
|
+
|
6
|
+
def initialize config = {}, &blk
|
7
|
+
@config = config.is_a?(Config) ? config : Config.new(config)
|
8
|
+
@config.instance_eval(&blk) if block_given?
|
9
|
+
@site = RubySlippers::Engine::Site.new(@config)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call env
|
13
|
+
@request = Rack::Request.new env
|
14
|
+
@response = Rack::Response.new
|
15
|
+
|
16
|
+
return [400, {}, []] unless @request.get?
|
17
|
+
|
18
|
+
path, mime = @request.path_info.split('.')
|
19
|
+
route = (path || '/').split('/').reject {|i| i.empty? }
|
20
|
+
|
21
|
+
response = @site.go(route, env, *(mime ? mime : []))
|
22
|
+
|
23
|
+
@response.body = [response[:body]]
|
24
|
+
@response['Content-Length'] = response[:body].length.to_s unless response[:body].empty?
|
25
|
+
@response['Content-Type'] = Rack::Mime.mime_type(".#{response[:type]}")
|
26
|
+
|
27
|
+
# Set http cache headers
|
28
|
+
@response['Cache-Control'] = if RubySlippers::Engine.env == 'production'
|
29
|
+
"public, max-age=#{@config[:cache]}"
|
30
|
+
else
|
31
|
+
"no-cache, must-revalidate"
|
32
|
+
end
|
33
|
+
|
34
|
+
@response['ETag'] = %("#{Digest::SHA1.hexdigest(response[:body])}")
|
35
|
+
|
36
|
+
@response.status = response[:status]
|
37
|
+
@response.finish
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
class Archives < Array
|
4
|
+
include Template
|
5
|
+
|
6
|
+
def initialize articles, config
|
7
|
+
self.replace articles
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def [] a
|
12
|
+
a.is_a?(Range) ? self.class.new(self.slice(a) || [], @config) : super
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_html
|
16
|
+
super(:archives, @config)
|
17
|
+
end
|
18
|
+
alias :to_s to_html
|
19
|
+
alias :archive archives
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'ruby_slippers/template'
|
2
|
+
|
3
|
+
module RubySlippers
|
4
|
+
module Engine
|
5
|
+
class Article < Hash
|
6
|
+
include Template
|
7
|
+
|
8
|
+
def initialize obj, config = {}
|
9
|
+
@obj, @config = obj, config
|
10
|
+
self.load if obj.is_a? Hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def load
|
14
|
+
data = if @obj.is_a? String
|
15
|
+
meta, self[:body] = File.read(@obj).split(/\n\n/, 2)
|
16
|
+
|
17
|
+
# use the date from the filename, or else ruby-slippers won't find the article
|
18
|
+
@obj =~ /\/(\d{4}-\d{2}-\d{2})[^\/]*$/
|
19
|
+
($1 ? {:date => $1} : {}).merge(YAML.load(meta))
|
20
|
+
elsif @obj.is_a? Hash
|
21
|
+
@obj
|
22
|
+
end.inject({}) {|h, (k,v)| h.merge(k.to_sym => v) }
|
23
|
+
|
24
|
+
self.taint
|
25
|
+
self.update data
|
26
|
+
self[:date] = Date.parse(self[:date].gsub('/', '-')) rescue Date.today
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def [] key
|
31
|
+
self.load unless self.tainted?
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def slug
|
36
|
+
self[:slug] || self[:title].slugize
|
37
|
+
end
|
38
|
+
|
39
|
+
def summary length = nil
|
40
|
+
config = @config[:summary]
|
41
|
+
sum = if self[:body] =~ config[:delim]
|
42
|
+
self[:body].split(config[:delim]).first
|
43
|
+
else
|
44
|
+
self[:body].match(/(.{1,#{length || config[:length] || config[:max]}}.*?)(\n|\Z)/m).to_s
|
45
|
+
end
|
46
|
+
markdown(sum.length == self[:body].length ? sum : sum.strip.sub(/\.\Z/, '…'))
|
47
|
+
end
|
48
|
+
|
49
|
+
def url
|
50
|
+
"http://#{(@config[:url].sub("http://", '') + self.path).squeeze('/')}"
|
51
|
+
end
|
52
|
+
alias :permalink url
|
53
|
+
|
54
|
+
def body
|
55
|
+
markdown self[:body].sub(@config[:summary][:delim], '') rescue markdown self[:body]
|
56
|
+
end
|
57
|
+
|
58
|
+
def path
|
59
|
+
"/#{@config[:prefix]}#{self[:date].strftime("/%Y/%m/%d/#{slug}/")}".squeeze('/')
|
60
|
+
end
|
61
|
+
|
62
|
+
def tags
|
63
|
+
return [] unless self[:tags]
|
64
|
+
self[:tags].split(',').collect do |tag|
|
65
|
+
"#{tag.strip.humanize.downcase}"
|
66
|
+
end.join(@config[:tag_separator])
|
67
|
+
end
|
68
|
+
|
69
|
+
def tag_links
|
70
|
+
return [] unless self[:tags]
|
71
|
+
self[:tags].split(',').collect do |tag|
|
72
|
+
"<a href=\"/tagged/#{tag.strip.slugize}\">#{tag.strip.humanize.downcase}</a>"
|
73
|
+
end.join(@config[:tag_separator])
|
74
|
+
end
|
75
|
+
|
76
|
+
def title() self[:title] || "an article" end
|
77
|
+
def date() @config[:date].call(self[:date]) end
|
78
|
+
def author() self[:author] || @config[:author] end
|
79
|
+
def image_src() self[:image] end
|
80
|
+
def to_html() self.load; super(:article, @config) end
|
81
|
+
alias :to_s to_html
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class RubySlippers::Engine::Config < Hash
|
2
|
+
Defaults = {
|
3
|
+
:author => ENV['USER'], # blog author
|
4
|
+
:title => Dir.pwd.split('/').last, # site title
|
5
|
+
:root => "index", # site index
|
6
|
+
:url => "http://127.0.0.1", # root URL of the site
|
7
|
+
:prefix => "", # common path prefix for the blog
|
8
|
+
:date => lambda {|now| now.strftime("%d/%m/%Y") }, # date function
|
9
|
+
:markdown => :smart, # use markdown
|
10
|
+
:disqus => false, # disqus name
|
11
|
+
:summary => {:max => 150, :delim => /~\n/}, # length of summary and delimiter
|
12
|
+
:ext => 'txt', # extension for articles
|
13
|
+
:cache => 28800, # cache duration (seconds)
|
14
|
+
:tag_separator => ', ', # tag separator for articles
|
15
|
+
:github => {:user => "dreamr", :repos => [], :ext => 'md'}, # Github username and list of repos
|
16
|
+
:to_html => lambda {|path, page, ctx| # returns an html, from a path & context
|
17
|
+
ERB.new(File.read("#{path}/#{page}.rhtml")).result(ctx)
|
18
|
+
},
|
19
|
+
:error => lambda {|code| # The HTML for your error page
|
20
|
+
"<font style='font-size:300%'>A large house has landed on you. You cannot continue because you are dead. <a href='/'>try again</a> (#{code})</font>"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
def initialize obj
|
24
|
+
self.update Defaults
|
25
|
+
self.update obj
|
26
|
+
end
|
27
|
+
|
28
|
+
def set key, val = nil, &blk
|
29
|
+
if val.is_a? Hash
|
30
|
+
self[key].update val
|
31
|
+
else
|
32
|
+
self[key] = block_given?? blk : val
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
class Context
|
4
|
+
include Template
|
5
|
+
attr_reader :env
|
6
|
+
|
7
|
+
def initialize ctx = {}, config = {}, path = "/", env = {}
|
8
|
+
@config, @context, @path, @env = config, ctx, path, env
|
9
|
+
@articles = Site.articles(@config[:ext]).reverse.map do |a|
|
10
|
+
Article.new(a, @config)
|
11
|
+
end
|
12
|
+
|
13
|
+
ctx.each do |k, v|
|
14
|
+
meta_def(k) { ctx.instance_of?(Hash) ? v : ctx.send(k) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def title
|
19
|
+
@config[:title]
|
20
|
+
end
|
21
|
+
|
22
|
+
def render page, type
|
23
|
+
content = to_html page, @config
|
24
|
+
type == :html ? to_html(:layout, @config, &Proc.new { content }) : send(:"to_#{type}", page)
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_xml page
|
28
|
+
xml = Builder::XmlMarkup.new(:indent => 2)
|
29
|
+
instance_eval File.read("#{Paths[:templates]}/#{page}.builder")
|
30
|
+
end
|
31
|
+
alias :to_atom to_xml
|
32
|
+
|
33
|
+
def method_missing m, *args, &blk
|
34
|
+
@context.respond_to?(m) ? @context.send(m, *args, &blk) : super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
Paths = {
|
4
|
+
:templates => "templates",
|
5
|
+
:pages => "templates/pages",
|
6
|
+
:articles => "articles"
|
7
|
+
} unless defined?(Paths)
|
8
|
+
|
9
|
+
def self.gem_root
|
10
|
+
File.expand_path("../../", __FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.env
|
14
|
+
ENV['RACK_ENV'] || 'production'
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.env= env
|
18
|
+
ENV['RACK_ENV'] = env
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
class Repo < Hash
|
4
|
+
include Template
|
5
|
+
|
6
|
+
README = "https://github.com/%s/%s/raw/master/README.%s"
|
7
|
+
|
8
|
+
def initialize name, config
|
9
|
+
self[:name], @config = name, config
|
10
|
+
end
|
11
|
+
|
12
|
+
def readme
|
13
|
+
markdown open(README %
|
14
|
+
[@config[:github][:user], self[:name], @config[:github][:ext]]).read
|
15
|
+
rescue Timeout::Error, OpenURI::HTTPError => e
|
16
|
+
"This page isn't available."
|
17
|
+
end
|
18
|
+
alias :content readme
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|