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
@@ -0,0 +1,103 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
class Site
|
4
|
+
def initialize config
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
|
8
|
+
def [] *args
|
9
|
+
@config[*args]
|
10
|
+
end
|
11
|
+
|
12
|
+
def []= key, value
|
13
|
+
@config.set key, value
|
14
|
+
end
|
15
|
+
|
16
|
+
def index type = :html
|
17
|
+
articles = type == :html ? self.articles.reverse : self.articles
|
18
|
+
{:articles => articles.map do |article|
|
19
|
+
Article.new article, @config
|
20
|
+
end}.merge archives
|
21
|
+
end
|
22
|
+
|
23
|
+
def archives filter = ""
|
24
|
+
entries = ! self.articles.empty??
|
25
|
+
self.articles.select do |a|
|
26
|
+
filter !~ /^\d{4}/ || File.basename(a) =~ /^#{filter}/
|
27
|
+
end.reverse.map do |article|
|
28
|
+
Article.new article, @config
|
29
|
+
end : []
|
30
|
+
|
31
|
+
return :archives => Archives.new(entries, @config)
|
32
|
+
end
|
33
|
+
|
34
|
+
def article route
|
35
|
+
Article.new("#{Paths[:articles]}/#{route.join('-')}.#{self[:ext]}", @config).load
|
36
|
+
end
|
37
|
+
|
38
|
+
def tagged tag
|
39
|
+
articles = self.articles.collect do |article|
|
40
|
+
Article.new article, @config
|
41
|
+
end.select do |article|
|
42
|
+
article[:tags].index(tag.humanize.downcase) if article[:tags]
|
43
|
+
end
|
44
|
+
|
45
|
+
{:articles => articles, :tagged => tag}
|
46
|
+
end
|
47
|
+
|
48
|
+
def /
|
49
|
+
self[:root]
|
50
|
+
end
|
51
|
+
|
52
|
+
def go route, env = {}, type = :html
|
53
|
+
route << self./ if route.empty?
|
54
|
+
type, path = type =~ /html|xml|json/ ? type.to_sym : :html, route.join('/')
|
55
|
+
context = lambda do |data, page|
|
56
|
+
Context.new(data, @config, path, env).render(page, type)
|
57
|
+
end
|
58
|
+
|
59
|
+
body, status = if Context.new.respond_to?(:"to_#{type}")
|
60
|
+
if route.first =~ /\d{4}/
|
61
|
+
case route.size
|
62
|
+
when 1..3
|
63
|
+
context[archives(route * '-'), :archives]
|
64
|
+
when 4
|
65
|
+
context[article(route), :article]
|
66
|
+
else http 400
|
67
|
+
end
|
68
|
+
elsif route.first == "tagged"
|
69
|
+
context[tagged(route.last), :tagged]
|
70
|
+
elsif respond_to?(path)
|
71
|
+
context[send(path, type), path.to_sym]
|
72
|
+
elsif (repo = @config[:github][:repos].grep(/#{path}/).first) &&
|
73
|
+
!@config[:github][:user].empty?
|
74
|
+
context[Repo.new(repo, @config), :repo]
|
75
|
+
else
|
76
|
+
context[{}, path.to_sym]
|
77
|
+
end
|
78
|
+
else
|
79
|
+
http 400
|
80
|
+
end
|
81
|
+
|
82
|
+
rescue NoMethodError, Errno::ENOENT => e
|
83
|
+
return :body => http(404).first, :type => :html, :status => 404
|
84
|
+
else
|
85
|
+
return :body => body || "", :type => type, :status => status || 200
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def http code
|
91
|
+
[@config[:error].call(code), code]
|
92
|
+
end
|
93
|
+
|
94
|
+
def articles
|
95
|
+
self.class.articles self[:ext]
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.articles ext
|
99
|
+
Dir["#{Paths[:articles]}/*.#{ext}"].sort_by {|entry| File.basename(entry) }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RubySlippers
|
2
|
+
module Engine
|
3
|
+
module Template
|
4
|
+
def to_html page, config, &blk
|
5
|
+
path = ([:layout, :repo].include?(page) ? Paths[:templates] : Paths[:pages])
|
6
|
+
config[:to_html].call(path, page, binding)
|
7
|
+
end
|
8
|
+
|
9
|
+
def markdown text
|
10
|
+
if (options = @config[:markdown])
|
11
|
+
Markdown.new(text.to_s.strip, *(options.eql?(true) ? [] : options)).to_html
|
12
|
+
else
|
13
|
+
text.strip
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing m, *args, &blk
|
18
|
+
self.keys.include?(m) ? self[m] : super
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included obj
|
22
|
+
obj.class_eval do
|
23
|
+
define_method(obj.to_s.split('::').last.downcase) { self }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
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'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
namespace :gem do
|
2
|
+
begin
|
3
|
+
require 'jeweler'
|
4
|
+
Jeweler::Tasks.new do |gem|
|
5
|
+
gem.name = "ruby-slippers"
|
6
|
+
gem.summary = %Q{the smartest blog-engine in all of Oz}
|
7
|
+
gem.description = %Q{A ruby and rack based blog engine for heroku}
|
8
|
+
gem.email = "james@rubyloves.me"
|
9
|
+
gem.homepage = "http://github.com/dreamr/ruby_slippers"
|
10
|
+
gem.authors = ["dreamr", "cloudhead"]
|
11
|
+
gem.add_development_dependency "riot"
|
12
|
+
gem.add_dependency "builder"
|
13
|
+
gem.add_dependency "rack"
|
14
|
+
if RUBY_PLATFORM =~ /win32/
|
15
|
+
gem.add_dependency "maruku"
|
16
|
+
else
|
17
|
+
gem.add_dependency "rdiscount"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
end
|
data/lib/tasks/test.rake
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rake/testtask'
|
2
|
+
namespace :test do
|
3
|
+
TEST_TYPES = %w(unit integration)
|
4
|
+
TEST_TYPES.each do |type|
|
5
|
+
Rake::TestTask.new(type) do |test|
|
6
|
+
test.libs << 'lib' << 'test'
|
7
|
+
test.pattern = "test/#{type}/*_test.rb"
|
8
|
+
test.verbose = true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Rake::TestTask.new(:all) do |test|
|
13
|
+
test.libs << 'lib' << 'test'
|
14
|
+
test.pattern = 'test/**/*_test.rb'
|
15
|
+
test.verbose = true
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
title: The Wonderful Wizard of Oz
|
2
|
+
date: 17/05/2010
|
3
|
+
tags: wizard
|
4
|
+
|
5
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
6
|
+
|
@@ -0,0 +1,6 @@
|
|
1
|
+
title: The Marvelous Land of Oz
|
2
|
+
date: 18/05/2010
|
3
|
+
|
4
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
5
|
+
|
6
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
title: Dorothy and the Wizard of Oz
|
2
|
+
date: 20/05/2010
|
3
|
+
|
4
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
5
|
+
|
6
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
title: Ozma of Oz
|
2
|
+
date: 18/05/2011
|
3
|
+
tags: ozma, oz
|
4
|
+
image: test/fixtures/images/ozma.png
|
5
|
+
|
6
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
7
|
+
|
8
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
<span id="count"><%= @articles.length %></span>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<ul id="articles">
|
2
|
+
<% for article in articles[0...2] %>
|
3
|
+
<li><img src="<%= article.image_src %>" /><%= article %></li>
|
4
|
+
<% end %>
|
5
|
+
</ul>
|
6
|
+
<div id="archives">
|
7
|
+
<%= archives[2...5] %>
|
8
|
+
</div>
|
9
|
+
<!-- testing env variable passing -->
|
10
|
+
<p>env passed: <%= env != nil %><br/></p>
|
11
|
+
<!-- testing get/post parameter passing -->
|
12
|
+
<p>request method type: <%= env['REQUEST_METHOD'] %><br/></p>
|
13
|
+
<p>request name value pair: <%= env['QUERY_STRING'] %><br/></p>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
xml.instruct!
|
2
|
+
xml.feed "xmlns" => "http://www.w3.org/2005/Atom" do
|
3
|
+
xml.title @config[:title]
|
4
|
+
xml.id @config[:url]
|
5
|
+
xml.updated articles.first[:date].iso8601 unless articles.empty?
|
6
|
+
xml.author { xml.name @config[:author] }
|
7
|
+
|
8
|
+
articles.each do |article|
|
9
|
+
xml.entry do
|
10
|
+
xml.title article.title
|
11
|
+
xml.link "rel" => "alternate", "href" => article.url
|
12
|
+
xml.id article.url
|
13
|
+
xml.published article[:date].iso8601
|
14
|
+
xml.updated article[:date].iso8601
|
15
|
+
xml.author { xml.name @config[:author] }
|
16
|
+
xml.summary article.summary, "type" => "html"
|
17
|
+
xml.content article.body, "type" => "html"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= readme %>
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'support/test_helper'
|
2
|
+
context RubySlippers::Engine::Article do
|
3
|
+
setup do
|
4
|
+
@config = RubySlippers::Engine::Config.new(:markdown => true, :author => AUTHOR, :url => URL)
|
5
|
+
@ruby_slippers = Rack::MockRequest.new(RubySlippers::Engine::App.new(@config))
|
6
|
+
RubySlippers::Engine::Paths[:articles] = "test/fixtures/articles"
|
7
|
+
RubySlippers::Engine::Paths[:pages] = "test/fixtures/pages"
|
8
|
+
RubySlippers::Engine::Paths[:templates] = "test/fixtures/templates"
|
9
|
+
end
|
10
|
+
|
11
|
+
context "GET a single article" do
|
12
|
+
setup { @ruby_slippers.get("/2010/05/17/the-wonderful-wizard-of-oz") }
|
13
|
+
asserts("returns a 200") { topic.status }.equals 200
|
14
|
+
asserts("content type is set properly") { topic.content_type }.equals "text/html"
|
15
|
+
should("contain the article") { topic.body }.includes_html("p" => /Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum./)
|
16
|
+
end
|
17
|
+
|
18
|
+
context "GET to the archive" do
|
19
|
+
context "through a year" do
|
20
|
+
setup { @ruby_slippers.get('/2011') }
|
21
|
+
asserts("returns a 200") { topic.status }.equals 200
|
22
|
+
should("includes the entries for that year") { topic.body }.includes_elements("li.entry", 1)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "through a year & month" do
|
26
|
+
setup { @ruby_slippers.get('/2011/05') }
|
27
|
+
asserts("returns a 200") { topic.status }.equals 200
|
28
|
+
should("includes the entries for that month") { topic.body }.includes_elements("li.entry", 1)
|
29
|
+
should("includes the year & month") { topic.body }.includes_html("h1" => /2011\/05/)
|
30
|
+
end
|
31
|
+
|
32
|
+
context "through /archives" do
|
33
|
+
setup { @ruby_slippers.get('/archives') }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "GET the tagged page" do
|
38
|
+
setup { @ruby_slippers.get('/tagged/wizard') }
|
39
|
+
asserts("returns a 200") { topic.status }.equals 200
|
40
|
+
asserts("body is not empty") { not topic.body.empty? }
|
41
|
+
should("includes only the entries for that tag") { topic.body }.includes_elements("li.entry", 1)
|
42
|
+
should("has access to @tag") { topic.body }.includes_html("h1" => /wizard/)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'support/test_helper'
|
2
|
+
|
3
|
+
context RubySlippers::Engine::Article do
|
4
|
+
setup do
|
5
|
+
@config = RubySlippers::Engine::Config.new(:markdown => true, :author => AUTHOR, :url => URL)
|
6
|
+
@ruby_slippers = Rack::MockRequest.new(RubySlippers::Engine::App.new(@config))
|
7
|
+
RubySlippers::Engine::Paths[:articles] = "test/fixtures/articles"
|
8
|
+
RubySlippers::Engine::Paths[:pages] = "test/fixtures/pages"
|
9
|
+
RubySlippers::Engine::Paths[:templates] = "test/fixtures/templates"
|
10
|
+
end
|
11
|
+
|
12
|
+
context "GET /index.xml (atom feed)" do
|
13
|
+
setup { @ruby_slippers.get('/index.xml') }
|
14
|
+
asserts("content type is set properly") { topic.content_type }.equals "application/xml"
|
15
|
+
asserts("body should be valid xml") { topic.body }.includes_html("feed > entry" => /.+/)
|
16
|
+
asserts("summary shouldn't be empty") { topic.body }.includes_html("summary" => /.{10,}/)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'support/test_helper'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
context RubySlippers::Engine do
|
5
|
+
setup do
|
6
|
+
@config = RubySlippers::Engine::Config.new(:markdown => true, :author => AUTHOR, :url => URL)
|
7
|
+
@ruby_slippers = Rack::MockRequest.new(RubySlippers::Engine::App.new(@config))
|
8
|
+
RubySlippers::Engine::Paths[:articles] = "test/fixtures/articles"
|
9
|
+
RubySlippers::Engine::Paths[:pages] = "test/fixtures/pages"
|
10
|
+
RubySlippers::Engine::Paths[:templates] = "test/fixtures/templates"
|
11
|
+
end
|
12
|
+
|
13
|
+
context "GET /" do
|
14
|
+
setup { @ruby_slippers.get('/') }
|
15
|
+
|
16
|
+
asserts("returns a 200") { topic.status }.equals 200
|
17
|
+
asserts("body is not empty") { not topic.body.empty? }
|
18
|
+
asserts("content type is set properly") { topic.content_type }.equals "text/html"
|
19
|
+
should("include a couple of articles") { topic.body }.includes_elements("#articles li", 2)
|
20
|
+
should("include an archive") { topic.body }.includes_elements("#archives li", 2)
|
21
|
+
|
22
|
+
context "with no articles" do
|
23
|
+
setup { Rack::MockRequest.new(RubySlippers::Engine::App.new(@config.merge(:ext => 'oxo'))).get('/') }
|
24
|
+
|
25
|
+
asserts("body is not empty") { not topic.body.empty? }
|
26
|
+
asserts("returns a 200") { topic.status }.equals 200
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with a user-defined to_html" do
|
30
|
+
setup do
|
31
|
+
@config[:to_html] = lambda do |path, page, binding|
|
32
|
+
ERB.new(File.read("#{path}/#{page}.rhtml")).result(binding)
|
33
|
+
end
|
34
|
+
@ruby_slippers.get('/')
|
35
|
+
end
|
36
|
+
|
37
|
+
asserts("returns a 200") { topic.status }.equals 200
|
38
|
+
asserts("body is not empty") { not topic.body.empty? }
|
39
|
+
asserts("content type is set properly") { topic.content_type }.equals "text/html"
|
40
|
+
should("include a couple of article") { topic.body }.includes_elements("#articles li", 2)
|
41
|
+
should("include an archive") { topic.body }.includes_elements("#archives li", 2)
|
42
|
+
asserts("Etag header present") { topic.headers.include? "ETag" }
|
43
|
+
asserts("Etag header has a value") { not topic.headers["ETag"].empty? }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "GET /about" do
|
48
|
+
setup { @ruby_slippers.get('/about') }
|
49
|
+
asserts("returns a 200") { topic.status }.equals 200
|
50
|
+
asserts("body is not empty") { not topic.body.empty? }
|
51
|
+
should("have access to @articles") { topic.body }.includes_html("#count" => /4/)
|
52
|
+
end
|
53
|
+
|
54
|
+
context "GET to an unknown route with a custom error" do
|
55
|
+
setup do
|
56
|
+
@config[:error] = lambda {|code| "error: #{code}" }
|
57
|
+
@ruby_slippers.get('/unknown')
|
58
|
+
end
|
59
|
+
|
60
|
+
should("returns a 404") { topic.status }.equals 404
|
61
|
+
should("return the custom error") { topic.body }.equals "error: 404"
|
62
|
+
end
|
63
|
+
|
64
|
+
context "Request is invalid" do
|
65
|
+
setup { @ruby_slippers.delete('/invalid') }
|
66
|
+
should("returns a 400") { topic.status }.equals 400
|
67
|
+
end
|
68
|
+
|
69
|
+
context "GET /index?param=testparam (get parameter)" do
|
70
|
+
setup { @ruby_slippers.get('/index?param=testparam') }
|
71
|
+
asserts("returns a 200") { topic.status }.equals 200
|
72
|
+
asserts("content type is set properly") { topic.content_type }.equals "text/html"
|
73
|
+
asserts("contain the env variable") { topic.body }.includes_html("p" => /env passed: true/)
|
74
|
+
asserts("access the http get parameter") { topic.body }.includes_html("p" => /request method type: GET/)
|
75
|
+
asserts("access the http parameter name value pair") { topic.body }.includes_html("p" => /request name value pair: param=testparam/)
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hpricot'
|
3
|
+
require 'riot'
|
4
|
+
|
5
|
+
$:.unshift File.dirname(__FILE__)
|
6
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
7
|
+
|
8
|
+
require File.expand_path('../../../lib/ruby_slippers', __FILE__)
|
9
|
+
|
10
|
+
URL = "http://domain.oz"
|
11
|
+
AUTHOR = "author"
|
12
|
+
|
13
|
+
class IncludesHTMLMacro < Riot::AssertionMacro
|
14
|
+
register :includes_html
|
15
|
+
|
16
|
+
def evaluate(actual, expected)
|
17
|
+
doc = Hpricot.parse(actual)
|
18
|
+
expected = expected.to_a.flatten
|
19
|
+
|
20
|
+
if (doc/expected.first).empty?
|
21
|
+
fail("expected #{actual} to contain a <#{expected.first}>")
|
22
|
+
elsif !(doc/expected.first).inner_html.match(expected.last)
|
23
|
+
fail("expected <#{expected.first}> to contain #{expected.last}")
|
24
|
+
else
|
25
|
+
pass
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class IncludesElementsMacro < Riot::AssertionMacro
|
31
|
+
register :includes_elements
|
32
|
+
|
33
|
+
def evaluate(actual, selector, count)
|
34
|
+
doc = Hpricot.parse(actual)
|
35
|
+
(doc/selector).size == count ? pass : fail("expected #{actual} to contain #{count} #{selector}(s)")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class WithinMacro < Riot::AssertionMacro
|
40
|
+
register :within
|
41
|
+
|
42
|
+
def evaluate(actual, expected)
|
43
|
+
expected.include?(actual) ? pass : fail("expected #{actual} to be within #{expected}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'support/test_helper'
|
2
|
+
|
3
|
+
context RubySlippers::Engine::Article do
|
4
|
+
setup do
|
5
|
+
@config = RubySlippers::Engine::Config.new(:markdown => true, :author => AUTHOR, :url => URL)
|
6
|
+
@config[:markdown] = true
|
7
|
+
@config[:date] = lambda {|t| "the time is #{t.strftime("%Y/%m/%d %H:%M")}" }
|
8
|
+
@config[:summary] = {:length => 50}
|
9
|
+
@config[:tag_separator] = ", "
|
10
|
+
end
|
11
|
+
|
12
|
+
context "with the bare essentials" do
|
13
|
+
setup do
|
14
|
+
RubySlippers::Engine::Article.new({
|
15
|
+
:title => "Dorothy & The Wizard of Oz.",
|
16
|
+
:body => "#Chapter I\nhello, *stranger*."
|
17
|
+
}, @config)
|
18
|
+
end
|
19
|
+
|
20
|
+
should("have a title") { topic.title }.equals "Dorothy & The Wizard of Oz."
|
21
|
+
should("parse the body as markdown") { topic.body }.equals "<h1>Chapter I</h1>\n\n<p>hello, <em>stranger</em>.</p>\n"
|
22
|
+
should("create an appropriate slug") { topic.slug }.equals "dorothy-and-the-wizard-of-oz"
|
23
|
+
should("set the date") { topic.date }.equals "the time is #{Date.today.strftime("%Y/%m/%d %H:%M")}"
|
24
|
+
should("create a summary") { topic.summary == topic.body }
|
25
|
+
should("have an author") { topic.author }.equals AUTHOR
|
26
|
+
should("have a path") { topic.path }.equals Date.today.strftime("/%Y/%m/%d/dorothy-and-the-wizard-of-oz/")
|
27
|
+
should("have a url") { topic.url }.equals Date.today.strftime("#{URL}/%Y/%m/%d/dorothy-and-the-wizard-of-oz/")
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with a user-defined summary" do
|
31
|
+
setup do
|
32
|
+
RubySlippers::Engine::Article.new({
|
33
|
+
:title => "Dorothy & The Wizard of Oz.",
|
34
|
+
:body => "Well,\nhello ~\n, *stranger*."
|
35
|
+
}, @config.merge(:markdown => false, :summary => {:max => 150, :delim => /~\n/}))
|
36
|
+
end
|
37
|
+
|
38
|
+
should("split the article at the delimiter") { topic.summary }.equals "Well,\nhello"
|
39
|
+
should("not have the delimiter in the body") { topic.body !~ /~/ }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with everything specified" do
|
43
|
+
setup do
|
44
|
+
RubySlippers::Engine::Article.new({
|
45
|
+
:title => "The Wizard of Oz",
|
46
|
+
:body => ("a little bit of text." * 5) + "\n" + "filler" * 10,
|
47
|
+
:date => "19/10/1976",
|
48
|
+
:slug => "wizard-of-oz",
|
49
|
+
:author => "toetoe",
|
50
|
+
:tags => "wizards, oz",
|
51
|
+
:image => "img/articles/2011/may/ozma.png"
|
52
|
+
}, @config)
|
53
|
+
end
|
54
|
+
|
55
|
+
should("parse the date") { [topic[:date].month, topic[:date].year] }.equals [10, 1976]
|
56
|
+
should("use the slug") { topic.slug }.equals "wizard-of-oz"
|
57
|
+
should("use the author") { topic.author }.equals "toetoe"
|
58
|
+
should("have tags") { topic.tags }.equals "wizards, oz"
|
59
|
+
should("have tag links") { topic.tag_links }.equals "<a href=\"/tagged/wizards\">wizards</a>, <a href=\"/tagged/oz\">oz</a>"
|
60
|
+
should("have an image") { topic.image_src }.equals "img/articles/2011/may/ozma.png"
|
61
|
+
|
62
|
+
context "and long first paragraph" do
|
63
|
+
should("create a valid summary") { topic.summary }.equals "<p>" + ("a little bit of text." * 5).chop + "…</p>\n"
|
64
|
+
end
|
65
|
+
|
66
|
+
context "and a short first paragraph" do
|
67
|
+
setup do
|
68
|
+
@config[:markdown] = false
|
69
|
+
RubySlippers::Engine::Article.new({:body => "there ain't such thing as a free lunch\n" * 10}, @config)
|
70
|
+
end
|
71
|
+
|
72
|
+
should("create a valid summary") { topic.summary.size }.within 75..80
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "in a subdirectory" do
|
77
|
+
context "with implicit leading forward slash" do
|
78
|
+
setup do
|
79
|
+
conf = RubySlippers::Engine::Config.new({})
|
80
|
+
conf.set(:prefix, "blog")
|
81
|
+
RubySlippers::Engine::Article.new({
|
82
|
+
:title => "Dorothy & The Wizard of Oz.",
|
83
|
+
:body => "#Chapter I\nhello, *stranger*."
|
84
|
+
}, conf)
|
85
|
+
end
|
86
|
+
|
87
|
+
should("be in the directory") { topic.path }.equals Date.today.strftime("/blog/%Y/%m/%d/dorothy-and-the-wizard-of-oz/")
|
88
|
+
end
|
89
|
+
|
90
|
+
context "with explicit leading forward slash" do
|
91
|
+
setup do
|
92
|
+
conf = RubySlippers::Engine::Config.new({})
|
93
|
+
conf.set(:prefix, "/blog")
|
94
|
+
RubySlippers::Engine::Article.new({
|
95
|
+
:title => "Dorothy & The Wizard of Oz.",
|
96
|
+
:body => "#Chapter I\nhello, *stranger*."
|
97
|
+
}, conf)
|
98
|
+
end
|
99
|
+
|
100
|
+
should("be in the directory") { topic.path }.equals Date.today.strftime("/blog/%Y/%m/%d/dorothy-and-the-wizard-of-oz/")
|
101
|
+
end
|
102
|
+
|
103
|
+
context "with explicit trailing forward slash" do
|
104
|
+
setup do
|
105
|
+
conf = RubySlippers::Engine::Config.new({})
|
106
|
+
conf.set(:prefix, "blog/")
|
107
|
+
RubySlippers::Engine::Article.new({
|
108
|
+
:title => "Dorothy & The Wizard of Oz.",
|
109
|
+
:body => "#Chapter I\nhello, *stranger*."
|
110
|
+
}, conf)
|
111
|
+
end
|
112
|
+
|
113
|
+
should("be in the directory") { topic.path }.equals Date.today.strftime("/blog/%Y/%m/%d/dorothy-and-the-wizard-of-oz/")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
end
|