ruby-slippers 0.0.1
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 +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
|