ruby_slippers 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.rvmrc +2 -0
  2. data/Gemfile +16 -0
  3. data/Gemfile.lock +45 -0
  4. data/Guardfile +16 -0
  5. data/LICENSE +20 -0
  6. data/README.md +122 -0
  7. data/Rakefile +5 -0
  8. data/TODO +32 -0
  9. data/VERSION +1 -0
  10. data/lib/ext/ext.rb +164 -0
  11. data/lib/ruby_slippers.rb +28 -0
  12. data/lib/ruby_slippers/app.rb +47 -0
  13. data/lib/ruby_slippers/archives.rb +22 -0
  14. data/lib/ruby_slippers/article.rb +104 -0
  15. data/lib/ruby_slippers/config.rb +37 -0
  16. data/lib/ruby_slippers/context.rb +38 -0
  17. data/lib/ruby_slippers/engine.rb +21 -0
  18. data/lib/ruby_slippers/repo.rb +21 -0
  19. data/lib/ruby_slippers/site.rb +115 -0
  20. data/lib/ruby_slippers/template.rb +28 -0
  21. data/lib/tasks/gemspec.rake +24 -0
  22. data/lib/tasks/test.rake +17 -0
  23. data/test/fixtures/articles/2010-05-17-the-wonderful-wizard-of-oz.txt +6 -0
  24. data/test/fixtures/articles/2010-05-18-the-marvelous-land-of-oz.txt +7 -0
  25. data/test/fixtures/articles/2010-05-20-dorothy-and-the-wizard-of-oz.txt +6 -0
  26. data/test/fixtures/articles/2011-05-18-ozma-of-oz.txt +10 -0
  27. data/test/fixtures/images/ozma.png +0 -0
  28. data/test/fixtures/pages/about.html.erb +1 -0
  29. data/test/fixtures/pages/archives.html.erb +23 -0
  30. data/test/fixtures/pages/article.html.erb +28 -0
  31. data/test/fixtures/pages/index.html.erb +27 -0
  32. data/test/fixtures/pages/sitemap.html.erb +0 -0
  33. data/test/fixtures/pages/tagged.html.erb +9 -0
  34. data/test/fixtures/templates/index.builder +21 -0
  35. data/test/fixtures/templates/layout.html.erb +4 -0
  36. data/test/fixtures/templates/repo.html.erb +1 -0
  37. data/test/fixtures/templates/sitemap.builder +25 -0
  38. data/test/integration/about_test.rb +23 -0
  39. data/test/integration/archives_test.rb +34 -0
  40. data/test/integration/articles_test.rb +56 -0
  41. data/test/integration/atom_test.rb +24 -0
  42. data/test/integration/invalid_route_test.rb +31 -0
  43. data/test/integration/tags_test.rb +24 -0
  44. data/test/support/test_helper.rb +47 -0
  45. data/test/unit/app_test.rb +14 -0
  46. data/test/unit/archives_test.rb +10 -0
  47. data/test/unit/article_test.rb +145 -0
  48. data/test/unit/date_patch_test.rb +10 -0
  49. data/test/unit/engine_test.rb +71 -0
  50. data/test/unit/site_test.rb +52 -0
  51. metadata +270 -0
@@ -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, oz
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,7 @@
1
+ title: The Marvelous Land of Oz
2
+ date: 18/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
+
7
+ 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,10 @@
1
+ title: Ozma of Oz
2
+ date: 18/05/2011
3
+ tags: ozma, oz
4
+ image: 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
+ ~
9
+
10
+ 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 @@
1
+ Fill me in with your about info!
@@ -0,0 +1,23 @@
1
+ <h1><%= @path %></h1>
2
+
3
+ <table>
4
+ <% archives.in_groups_of(3, false) do |group| %>
5
+ <tr>
6
+ <% if group.length > 0 %>
7
+ <% for article in group %>
8
+ <td style="border: 1px solid #ddd; padding: 4px; text-align: center;">
9
+ <%=
10
+ begin;
11
+ "<div class='archived_article'>"+
12
+ "<div class='title'>#{article.title}</div>"+
13
+ "<a href='#{article.url}'><img src='#{article.thumb}' width='200'></img></a>"+
14
+ "<div class='title'>#{article.date}</div>"+
15
+ "</div>";
16
+ rescue;end
17
+ %>
18
+ <% end %>
19
+ </td>
20
+ <% end %>
21
+ </tr>
22
+ <% end %>
23
+ </table>
@@ -0,0 +1,28 @@
1
+ <article class="article">
2
+ <header>
3
+ <h1><%= title %></h1>
4
+ <span class="date"><%= date %></span>
5
+ </header>
6
+
7
+
8
+ <% if image_src %>
9
+ <figure>
10
+ <img src="<%= image_src || '/img/missing_thumb.png' %>" alt="Post thumbnail" class="thumbnail alignleft" />
11
+ </figure>
12
+ <% end %>
13
+
14
+
15
+ <section class="content">
16
+ <%= body %>
17
+ </section>
18
+
19
+ <section class="comments">
20
+ <% if @config[:disqus] %>
21
+ <div id="disqus_thread"></div>
22
+ <script type="text/javascript" src="http://disqus.com/forums/<%= @config[:disqus] %>/embed.js"> </script>
23
+ <noscript><a href="http://<%= @config[:disqus] %>.disqus.com/?url=ref">View the discussion thread.</a></noscript>
24
+ <a href="http://disqus.com" class="dsq-brlink">blog comments powered by <span class="logo-disqus">Disqus</span></a>
25
+ <% end %>
26
+ </section>
27
+ </article>
28
+
@@ -0,0 +1,27 @@
1
+ <section id="articles">
2
+ <% for article in articles[0...3] %>
3
+ <article class="article">
4
+ <header>
5
+ <h1><a href="<%= article.path %>"><%= article.title %></a></h1>
6
+ <span class="date"><%= article.date %></span>
7
+ </header>
8
+
9
+ <% if article.image_src %>
10
+ <figure>
11
+ <img src="<%= article.image_src || '/img/missing_thumb.png' %>" alt="Post thumbnail" class="thumbnail alignleft" />
12
+ </figure>
13
+ <% end %>
14
+
15
+ <section class="content">
16
+ <%= article.summary %>
17
+ </section>
18
+
19
+ <footer>
20
+ <div class="more-link"><a href="<%= article.path %>">read on &raquo;</a></div>
21
+ <span class="meta">
22
+ <p>Posted by <%= article.author %> in <%= article.tag_links %> on <%= article.date %></p>
23
+ </div>
24
+ </footer>
25
+ </article>
26
+ <% end %>
27
+ </section>
File without changes
@@ -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="article"><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,25 @@
1
+ xml.instruct!
2
+ xml.urlset("xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9") do
3
+
4
+ xml.url do
5
+ xml.loc @config[:site_url]
6
+ xml.changefreq "weekly"
7
+ end
8
+
9
+ xml.url do
10
+ xml.loc @config[:site_url] + '/about'
11
+ xml.changefreq "weekly"
12
+ end
13
+
14
+ xml.url do
15
+ xml.loc @config[:site_url] + '/archives'
16
+ xml.changefreq "weekly"
17
+ end
18
+
19
+ articles.each do |article|
20
+ xml.url do
21
+ xml.loc @config[:site_url] + article.path
22
+ xml.changefreq "weekly"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'support/test_helper'
2
+ require 'date'
3
+
4
+ module RubySlippers::Engine
5
+ context "Routes" do
6
+ setup do
7
+ @config = Config.new(:markdown => true, :author => AUTHOR, :url => URL)
8
+ @ruby_slippers = Rack::MockRequest.new(App.new(@config))
9
+
10
+ if File.expand_path("../../", __FILE__) =~ /engine/
11
+ Paths[:articles] = "test/fixtures/articles"
12
+ Paths[:templates] = "test/fixtures/templates"
13
+ Paths[:pages] = "test/fixtures/pages"
14
+ end
15
+ end
16
+
17
+ context "GET /about" do
18
+ setup { @ruby_slippers.get('/about') }
19
+ asserts("return a 200") { topic.status }.equals 200
20
+ asserts("body is not empty") { not topic.body.empty? }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ require 'support/test_helper'
2
+
3
+ module RubySlippers::Engine
4
+ context "Archive Routes" do
5
+ setup do
6
+ @config = Config.new(:markdown => true, :author => AUTHOR, :url => URL)
7
+ @ruby_slippers = Rack::MockRequest.new(App.new(@config))
8
+
9
+ if File.expand_path("../../", __FILE__) =~ /engine/
10
+ Paths[:articles] = "test/fixtures/articles"
11
+ Paths[:templates] = "test/fixtures/templates"
12
+ Paths[:pages] = "test/fixtures/pages"
13
+ end
14
+ end
15
+ context "GET to the archive" do
16
+ context "through a year" do
17
+ setup { @ruby_slippers.get('/2011') }
18
+ asserts("return a 200") { topic.status }.equals 200
19
+ should("include the entries for that year") { topic.body }.includes_elements("div.archived_article", 1)
20
+ end
21
+
22
+ context "through a year & month" do
23
+ setup { @ruby_slippers.get('/2011/05') }
24
+ asserts("return a 200") { topic.status }.equals 200
25
+ should("include the entries for that month") { topic.body }.includes_elements("div.archived_article", 1)
26
+ should("include the year & month") { topic.body }.includes_html("h1" => /2011\/05/)
27
+ end
28
+
29
+ context "through /archives" do
30
+ setup { @ruby_slippers.get('/archives') }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,56 @@
1
+ require 'support/test_helper'
2
+
3
+ module RubySlippers::Engine
4
+ context "Article Routes" do
5
+ setup do
6
+ @config = Config.new(:markdown => true, :author => AUTHOR, :url => URL)
7
+ @ruby_slippers = Rack::MockRequest.new(App.new(@config))
8
+
9
+ if File.expand_path("../../", __FILE__) =~ /engine/
10
+ Paths[:articles] = "test/fixtures/articles"
11
+ Paths[:templates] = "test/fixtures/templates"
12
+ Paths[:pages] = "test/fixtures/pages"
13
+ end
14
+ end
15
+
16
+ context "GET /" do
17
+ setup { @ruby_slippers.get('/') }
18
+
19
+ asserts("return a 200") { topic.status }.equals 200
20
+ asserts("body is not empty") { not topic.body.empty? }
21
+ asserts("content type is set properly") { topic.content_type }.equals "text/html"
22
+ should("include 3 articles"){ topic.body }.includes_elements("article", 3)
23
+
24
+ context "with no articles" do
25
+ setup { Rack::MockRequest.new(App.new(@config.merge(:ext => 'oxo'))).get('/') }
26
+
27
+ asserts("body is not empty") { not topic.body.empty? }
28
+ asserts("return a 200") { topic.status }.equals 200
29
+ end
30
+
31
+ context "with a user-defined to_html" do
32
+ setup do
33
+ @config[:to_html] = lambda do |path, page, binding|
34
+ ERB.new(File.read("#{path}/#{page}.html.erb")).result(binding)
35
+ end
36
+ @ruby_slippers.get('/')
37
+ end
38
+
39
+ asserts("return a 200") { topic.status }.equals 200
40
+ asserts("body is not empty") { not topic.body.empty? }
41
+ asserts("content type is set properly") { topic.content_type }.equals "text/html"
42
+ should("include 3 articles"){ topic.body }.includes_elements("article", 3)
43
+ asserts("Etag header present") { topic.headers.include? "ETag" }
44
+ asserts("Etag header has a value") { not topic.headers["ETag"].empty? }
45
+ end
46
+ end
47
+
48
+ context "GET a single article" do
49
+ setup { @ruby_slippers.get("/2010/05/17/the-wonderful-wizard-of-oz") }
50
+ asserts("return a 200") { topic.status }.equals 200
51
+ asserts("content type is set properly") { topic.content_type }.equals "text/html"
52
+ 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./)
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,24 @@
1
+ require 'support/test_helper'
2
+
3
+ module RubySlippers::Engine
4
+ context "Atom Feed" do
5
+ setup do
6
+ @config = Config.new(:markdown => true, :author => AUTHOR, :url => URL)
7
+ @ruby_slippers = Rack::MockRequest.new(App.new(@config))
8
+
9
+ if File.expand_path("../../", __FILE__) =~ /engine/
10
+ Paths[:articles] = "test/fixtures/articles"
11
+ Paths[:templates] = "test/fixtures/templates"
12
+ Paths[:pages] = "test/fixtures/pages"
13
+ end
14
+ end
15
+
16
+ context "GET /index.xml (atom feed)" do
17
+ setup { @ruby_slippers.get('/index.xml') }
18
+ asserts("content type is set properly") { topic.content_type }.equals "application/xml"
19
+ asserts("body should be valid xml") { topic.body }.includes_html("feed > entry" => /.+/)
20
+ asserts("summary shouldn't be empty") { topic.body }.includes_html("summary" => /.{10,}/)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ require 'support/test_helper'
2
+
3
+ module RubySlippers::Engine
4
+ context "Invalid Routes" do
5
+ setup do
6
+ @config = Config.new(:markdown => true, :author => AUTHOR, :url => URL)
7
+ @ruby_slippers = Rack::MockRequest.new(App.new(@config))
8
+
9
+ if File.expand_path("../../", __FILE__) =~ /engine/
10
+ Paths[:articles] = "test/fixtures/articles"
11
+ Paths[:templates] = "test/fixtures/templates"
12
+ Paths[:pages] = "test/fixtures/pages"
13
+ end
14
+ end
15
+
16
+ context "GET to an unknown route with a custom error" do
17
+ setup do
18
+ @config[:error] = lambda {|code| "error: #{code}" }
19
+ @ruby_slippers.get('/unknown')
20
+ end
21
+
22
+ should("returns a 404") { topic.status }.equals 404
23
+ should("return the custom error") { topic.body }.equals "error: 404"
24
+ end
25
+
26
+ context "Request is invalid" do
27
+ setup { @ruby_slippers.delete('/invalid') }
28
+ should("returns a 400") { topic.status }.equals 400
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'support/test_helper'
2
+
3
+ module RubySlippers::Engine
4
+ context "Tagged Routes" do
5
+ setup do
6
+ @config = Config.new(:markdown => true, :author => AUTHOR, :url => URL)
7
+ @ruby_slippers = Rack::MockRequest.new(App.new(@config))
8
+
9
+ if File.expand_path("../../", __FILE__) =~ /engine/
10
+ Paths[:articles] = "test/fixtures/articles"
11
+ Paths[:templates] = "test/fixtures/templates"
12
+ Paths[:pages] = "test/fixtures/pages"
13
+ end
14
+ end
15
+
16
+ context "GET the tagged page" do
17
+ setup { @ruby_slippers.get('/tagged/oz') }
18
+ asserts("return a 200") { topic.status }.equals 200
19
+ asserts("body is not empty") {not topic.body.empty? }
20
+ should("include only the entries for that tag") { topic.body }.includes_elements("li.article", 2)
21
+ should("have access to @tag") { topic.body }.includes_html("h1" => /oz/)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'hpricot'
3
+ require 'riot'
4
+ require 'riot/rr'
5
+
6
+ $:.unshift File.dirname(__FILE__)
7
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
8
+
9
+ require File.expand_path('../../../lib/ruby_slippers', __FILE__)
10
+
11
+ URL = "http://domain.oz"
12
+ AUTHOR = "author"
13
+
14
+ class IncludesHTMLMacro < Riot::AssertionMacro
15
+ register :includes_html
16
+
17
+ def evaluate(actual, expected)
18
+ doc = Hpricot.parse(actual)
19
+ expected = expected.to_a.flatten
20
+
21
+ if (doc/expected.first).empty?
22
+ fail("expected #{actual} to contain a <#{expected.first}>")
23
+ elsif !(doc/expected.first).inner_html.match(expected.last)
24
+ fail("expected <#{expected.first}> to contain #{expected.last}")
25
+ else
26
+ pass
27
+ end
28
+ end
29
+ end
30
+
31
+ class IncludesElementsMacro < Riot::AssertionMacro
32
+ register :includes_elements
33
+
34
+ def evaluate(actual, selector, count)
35
+ doc = Hpricot.parse(actual)
36
+ (doc/selector).size == count ? pass : fail("expected #{actual} to contain #{count} #{selector}(s)")
37
+ end
38
+ end
39
+
40
+ class WithinMacro < Riot::AssertionMacro
41
+ register :within
42
+
43
+ def evaluate(actual, expected)
44
+ expected.include?(actual) ? pass : fail("expected #{actual} to be within #{expected}")
45
+ end
46
+ end
47
+