ruby-slippers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.rvmrc +2 -0
  2. data/Gemfile +16 -0
  3. data/Gemfile.lock +90 -0
  4. data/Guardfile +16 -0
  5. data/LICENSE +20 -0
  6. data/README.md +114 -0
  7. data/Rakefile +5 -0
  8. data/VERSION +1 -0
  9. data/lib/ext/ext.rb +57 -0
  10. data/lib/ruby_slippers/app.rb +41 -0
  11. data/lib/ruby_slippers/archives.rb +22 -0
  12. data/lib/ruby_slippers/article.rb +84 -0
  13. data/lib/ruby_slippers/config.rb +35 -0
  14. data/lib/ruby_slippers/context.rb +38 -0
  15. data/lib/ruby_slippers/engine.rb +21 -0
  16. data/lib/ruby_slippers/repo.rb +21 -0
  17. data/lib/ruby_slippers/site.rb +103 -0
  18. data/lib/ruby_slippers/template.rb +28 -0
  19. data/lib/ruby_slippers.rb +28 -0
  20. data/lib/tasks/gemspec.rake +24 -0
  21. data/lib/tasks/test.rake +17 -0
  22. data/test/fixtures/articles/2010-05-17-the-wonderful-wizard-of-oz.txt +6 -0
  23. data/test/fixtures/articles/2010-05-18-the-marvelous-land-of-oz.txt +6 -0
  24. data/test/fixtures/articles/2010-05-20-dorothy-and-the-wizard-of-oz.txt +6 -0
  25. data/test/fixtures/articles/2011-05-18-ozma-of-oz.txt +8 -0
  26. data/test/fixtures/images/ozma.png +0 -0
  27. data/test/fixtures/pages/about.rhtml +1 -0
  28. data/test/fixtures/pages/archives.rhtml +5 -0
  29. data/test/fixtures/pages/article.rhtml +4 -0
  30. data/test/fixtures/pages/index.rhtml +13 -0
  31. data/test/fixtures/pages/tagged.rhtml +9 -0
  32. data/test/fixtures/templates/index.builder +21 -0
  33. data/test/fixtures/templates/layout.rhtml +4 -0
  34. data/test/fixtures/templates/repo.rhtml +1 -0
  35. data/test/integration/article_test.rb +44 -0
  36. data/test/integration/atom_test.rb +19 -0
  37. data/test/integration/ruby_slippers_test.rb +77 -0
  38. data/test/support/test_helper.rb +46 -0
  39. data/test/unit/archives_test.rb +8 -0
  40. data/test/unit/article_test.rb +118 -0
  41. data/test/unit/ruby_slippers_test.rb +75 -0
  42. 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
@@ -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,5 @@
1
+ <h1><%= @path %></h1>
2
+ <% for entry in archives %>
3
+ <li class="entry"><%= entry.title %></li>
4
+ <% end %>
5
+
@@ -0,0 +1,4 @@
1
+ <h2><%= title %></h2>
2
+ <span><%= date %></h2>
3
+ <p><%= body %></p>
4
+
@@ -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,9 @@
1
+ <h1>Posts tagged with: <%= tagged %></h1>
2
+
3
+ <% unless articles.empty? %>
4
+ <ul>
5
+ <% for article in articles %>
6
+ <li class="entry"><a href="<%= article.url %>"><%= article.title %></a></li>
7
+ <% end %>
8
+ </ul>
9
+ <% end %>
@@ -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,4 @@
1
+ <html>
2
+ <%= yield %>
3
+ </html>
4
+
@@ -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,8 @@
1
+ require 'support/test_helper'
2
+
3
+ context RubySlippers::Engine::Archives do
4
+ setup do
5
+ @config = RubySlippers::Engine::Config.new(:markdown => true, :author => AUTHOR, :url => URL)
6
+ end
7
+
8
+ end
@@ -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 + "&hellip;</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