schreihals 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.watchr +14 -0
- data/README.md +13 -1
- data/Rakefile +9 -4
- data/bin/schreihals +5 -0
- data/example/Gemfile +0 -4
- data/lib/schreihals.rb +13 -140
- data/lib/schreihals/actions.rb +45 -0
- data/lib/schreihals/app.rb +73 -0
- data/lib/schreihals/cli.rb +42 -0
- data/lib/schreihals/document.rb +52 -0
- data/lib/schreihals/helpers.rb +38 -0
- data/lib/schreihals/post.rb +74 -0
- data/lib/schreihals/static.rb +17 -0
- data/lib/schreihals/version.rb +1 -1
- data/lib/templates/first-post.md.tt +1 -0
- data/lib/templates/new-post.md.tt +13 -0
- data/lib/templates/new_blog/.gitignore +5 -0
- data/lib/templates/new_blog/Gemfile +3 -0
- data/lib/templates/new_blog/config.ru.tt +14 -0
- data/lib/templates/new_blog/posts/.gitkeep +0 -0
- data/lib/templates/new_blog/public/media/.gitkeep +0 -0
- data/lib/views/404.haml +3 -4
- data/lib/views/blog.scss +1 -0
- data/lib/views/index.haml +2 -3
- data/lib/views/layout.haml +13 -9
- data/lib/views/partials/_gauges.haml +12 -0
- data/lib/views/partials/_post.haml +10 -2
- data/lib/views/post.haml +2 -5
- data/lib/views/schreihals.scss +24 -12
- data/schreihals.gemspec +14 -5
- data/test/app_test.rb +68 -0
- data/test/document_test.rb +41 -0
- data/test/files/simple_document.md +7 -0
- data/test/posts/2011-12-23-first-post.md +8 -0
- data/test/posts/2011-12-24-second-post.md +8 -0
- data/test/posts/static-page.md +5 -0
- data/test/test_helper.rb +63 -0
- metadata +172 -43
- data/.rspec +0 -2
- data/spec/spec_helper.rb +0 -7
@@ -0,0 +1,38 @@
|
|
1
|
+
module Schreihals
|
2
|
+
module Helpers
|
3
|
+
def find_template(views, name, engine, &block)
|
4
|
+
Array(views).each { |v| super(v, name, engine, &block) }
|
5
|
+
end
|
6
|
+
|
7
|
+
def partial(thing, locals = {})
|
8
|
+
name = case thing
|
9
|
+
when String then thing
|
10
|
+
else thing.class.to_s.demodulize.underscore
|
11
|
+
end
|
12
|
+
|
13
|
+
haml :"partials/_#{name}", :locals => { name.to_sym => thing }.merge(locals)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_page_title(title)
|
17
|
+
@page_title = title
|
18
|
+
end
|
19
|
+
|
20
|
+
def link_to(title, thing)
|
21
|
+
haml "%a{href: '#{url_for thing}'} #{title}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def url_for(thing, options = {})
|
25
|
+
url = thing.respond_to?(:to_url) ? thing.to_url : thing.to_s
|
26
|
+
url = "#{settings.blog_url}#{url}" if options[:absolute]
|
27
|
+
url
|
28
|
+
end
|
29
|
+
|
30
|
+
def show_disqus?
|
31
|
+
settings.disqus_name.present?
|
32
|
+
end
|
33
|
+
|
34
|
+
def production?
|
35
|
+
settings.environment.to_sym == :production
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module Schreihals
|
4
|
+
class Post < Document
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
self.attributes = {
|
8
|
+
'disqus' => true,
|
9
|
+
'status' => 'published',
|
10
|
+
'summary' => nil,
|
11
|
+
'link' => nil,
|
12
|
+
'read_more' => nil,
|
13
|
+
'date' => nil,
|
14
|
+
'title' => nil,
|
15
|
+
'slug' => nil,
|
16
|
+
'disqus_identifier' => file_name
|
17
|
+
}.merge(attributes)
|
18
|
+
|
19
|
+
# extract date and slug from file name, if possible
|
20
|
+
if file_name_without_extension =~ /^(\d{4}-\d{1,2}-\d{1,2})-?(.+)$/
|
21
|
+
attributes['date'] ||= Date.parse($1)
|
22
|
+
attributes['slug'] ||= $2
|
23
|
+
else
|
24
|
+
attributes['slug'] ||= file_name_without_extension
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def year
|
29
|
+
date.year
|
30
|
+
end
|
31
|
+
|
32
|
+
def month
|
33
|
+
date.month
|
34
|
+
end
|
35
|
+
|
36
|
+
def day
|
37
|
+
date.day
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_url
|
41
|
+
date.present? ? "/#{year}/#{month}/#{day}/#{slug}/" : "/#{slug}/"
|
42
|
+
end
|
43
|
+
|
44
|
+
def disqus?
|
45
|
+
disqus && published?
|
46
|
+
end
|
47
|
+
|
48
|
+
def published?
|
49
|
+
status == 'published'
|
50
|
+
end
|
51
|
+
|
52
|
+
def post?
|
53
|
+
date.present?
|
54
|
+
end
|
55
|
+
|
56
|
+
def page?
|
57
|
+
!post?
|
58
|
+
end
|
59
|
+
|
60
|
+
class << self
|
61
|
+
def latest(options = {})
|
62
|
+
options = {published_only: false}.merge(options)
|
63
|
+
|
64
|
+
posts = documents.select(&:date)
|
65
|
+
posts = posts.select(&:published?) if options[:published_only]
|
66
|
+
posts.sort_by(&:date).reverse.first(10)
|
67
|
+
end
|
68
|
+
|
69
|
+
def with_slug(slug)
|
70
|
+
documents.detect { |p| p.slug == slug }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Schreihals
|
2
|
+
class Static
|
3
|
+
def initialize(app, public_dir = './public')
|
4
|
+
@file = Rack::File.new(public_dir)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
status, headers, body = @file.call(env)
|
10
|
+
if status > 400
|
11
|
+
@app.call(env)
|
12
|
+
else
|
13
|
+
[status, headers, body]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/schreihals/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
first_post
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'schreihals'
|
4
|
+
|
5
|
+
class MyBlog < Schreihals::App
|
6
|
+
set :blog_title, "<%= @name %>"
|
7
|
+
set :blog_url, "http://<%= @name %>.info"
|
8
|
+
set :blog_description, ""
|
9
|
+
set :author_name, "Your Name"
|
10
|
+
# set :disqus_name, ""
|
11
|
+
# set :google_analytics_id, ""
|
12
|
+
end
|
13
|
+
|
14
|
+
run MyBlog
|
File without changes
|
File without changes
|
data/lib/views/404.haml
CHANGED
data/lib/views/blog.scss
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
@import 'schreihals';
|
data/lib/views/index.haml
CHANGED
data/lib/views/layout.haml
CHANGED
@@ -2,19 +2,23 @@
|
|
2
2
|
%html
|
3
3
|
%head
|
4
4
|
%title= [@page_title, settings.blog_title].compact.join(" | ")
|
5
|
-
%
|
5
|
+
%meta{ name: "viewport", content: "width=device-width, initial-scale=1.0" }
|
6
|
+
%link{ href: '/blog.css', media: "screen", rel: "stylesheet", type: "text/css" }
|
6
7
|
%link{ href: '/atom.xml', title: "Subscribe via Atom Feed", rel: 'alternate', type: 'application/atom+xml' }
|
7
8
|
%body
|
8
|
-
|
9
|
-
|
9
|
+
.container
|
10
|
+
%header
|
10
11
|
%h1
|
11
12
|
= link_to settings.blog_title, '/'
|
13
|
+
- if @show_description
|
14
|
+
~ markdown settings.blog_description
|
12
15
|
|
13
|
-
|
16
|
+
= yield
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
~ markdown settings.blog_description
|
18
|
+
%footer
|
19
|
+
~ markdown settings.footer
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
+
- if production? && settings.google_analytics_id.present?
|
22
|
+
= partial 'google_analytics'
|
23
|
+
- if production? && settings.gauges_id.present?
|
24
|
+
= partial 'gauges'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
:javascript
|
2
|
+
var _gauges = _gauges || [];
|
3
|
+
(function() {
|
4
|
+
var t = document.createElement('script');
|
5
|
+
t.type = 'text/javascript';
|
6
|
+
t.async = true;
|
7
|
+
t.id = 'gauges-tracker';
|
8
|
+
t.setAttribute('data-site-id', '#{settings.gauges_id}');
|
9
|
+
t.src = '//secure.gaug.es/track.js';
|
10
|
+
var s = document.getElementsByTagName('script')[0];
|
11
|
+
s.parentNode.insertBefore(t, s);
|
12
|
+
})();
|
@@ -4,6 +4,7 @@
|
|
4
4
|
- show_body ||= complete || !show_summary
|
5
5
|
- show_read_more ||= !complete && post.summary.present?
|
6
6
|
- show_permalink ||= post.post? && !show_read_more
|
7
|
+
- show_twitter ||= complete && post.post? && settings.twitter_id.present?
|
7
8
|
|
8
9
|
%article.post{class: post.status}
|
9
10
|
%header
|
@@ -27,5 +28,12 @@
|
|
27
28
|
- if show_body
|
28
29
|
~ post.to_html
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
%footer
|
32
|
+
- if show_permalink
|
33
|
+
%p.permalink= link_to '∞', post
|
34
|
+
- if show_twitter
|
35
|
+
.social_media_buttons
|
36
|
+
- if show_twitter
|
37
|
+
%a{ href: "https://twitter.com/share", class: "twitter-share-button", data: { via: settings.twitter_id } }
|
38
|
+
:javascript
|
39
|
+
!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
|
data/lib/views/post.haml
CHANGED
@@ -1,10 +1,7 @@
|
|
1
1
|
- set_page_title @post.title.presence
|
2
2
|
|
3
|
-
|
4
|
-
.container
|
5
|
-
= partial @post, complete: true
|
3
|
+
= partial @post, complete: true
|
6
4
|
|
7
5
|
- if show_disqus? && @post.disqus?
|
8
6
|
%section.disqus
|
9
|
-
.
|
10
|
-
= partial 'disqus', :disqus_identifier => @post.disqus_identifier
|
7
|
+
= partial 'disqus', :disqus_identifier => @post.disqus_identifier
|
data/lib/views/schreihals.scss
CHANGED
@@ -1,30 +1,41 @@
|
|
1
|
-
|
1
|
+
// load stuff!
|
2
2
|
@import 'schnitzelstyle';
|
3
3
|
@import 'schnitzelstyle/coderay';
|
4
|
-
|
5
|
-
$font-default: 16px "PT Serif";
|
6
|
-
$font-headlines: 15px "Inder";
|
7
|
-
$font-footer: 11px "Inder";
|
4
|
+
@import 'schnitzelstyle/export';
|
8
5
|
|
9
6
|
@include schnitzel-complete;
|
7
|
+
@include schnitzel-export;
|
10
8
|
|
11
|
-
|
12
|
-
|
9
|
+
/* misc crap */
|
10
|
+
.social_media_buttons {
|
11
|
+
margin: 1em 0;
|
13
12
|
}
|
14
13
|
|
14
|
+
/* posts */
|
15
15
|
article.post {
|
16
|
-
margin: 5em 0;
|
17
16
|
header {
|
18
|
-
|
19
|
-
|
17
|
+
.info {
|
18
|
+
display: inline;
|
19
|
+
margin-left: 1em;
|
20
|
+
color: #999;
|
21
|
+
}
|
20
22
|
}
|
21
23
|
|
22
24
|
&.draft header h2 { background-color: #ccc; }
|
23
25
|
|
24
26
|
div.summary p {
|
25
|
-
font:
|
27
|
+
font: $font-header;
|
28
|
+
font-size: 115%;
|
26
29
|
line-height: 150%;
|
27
30
|
color: #000;
|
31
|
+
|
32
|
+
@media only screen and (max-width: 640px) {
|
33
|
+
line-height: 130%;
|
34
|
+
|
35
|
+
section.posts & {
|
36
|
+
font-size: 100%;
|
37
|
+
}
|
38
|
+
}
|
28
39
|
}
|
29
40
|
|
30
41
|
p.permalink {
|
@@ -35,6 +46,7 @@ article.post {
|
|
35
46
|
}
|
36
47
|
}
|
37
48
|
|
49
|
+
/* disqus */
|
38
50
|
#dsq-content {
|
39
51
|
a {
|
40
52
|
border: 0;
|
@@ -43,7 +55,7 @@ article.post {
|
|
43
55
|
margin-bottom: 2em;
|
44
56
|
}
|
45
57
|
h3 {
|
46
|
-
margin-top: 1em;
|
58
|
+
margin-top: 1em !important;
|
47
59
|
@include clearfix;
|
48
60
|
}
|
49
61
|
}
|
data/schreihals.gemspec
CHANGED
@@ -15,17 +15,26 @@ Gem::Specification.new do |gem|
|
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Schreihals::VERSION
|
17
17
|
|
18
|
-
gem.add_dependency '
|
19
|
-
gem.add_dependency '
|
18
|
+
gem.add_dependency 'rack', '~> 1.4.0'
|
19
|
+
gem.add_dependency 'sinatra', '~> 1.3.2'
|
20
|
+
gem.add_dependency 'activesupport', '~> 3.1.0'
|
20
21
|
gem.add_dependency 'shotgun'
|
21
22
|
gem.add_dependency 'haml'
|
22
23
|
gem.add_dependency 'sass'
|
23
|
-
gem.add_dependency 'document_mapper'
|
24
24
|
gem.add_dependency 'coderay'
|
25
25
|
gem.add_dependency 'redcarpet'
|
26
26
|
gem.add_dependency 'rack-cache'
|
27
27
|
gem.add_dependency 'rack-codehighlighter'
|
28
|
-
gem.add_dependency 'schnitzelstyle', '
|
28
|
+
gem.add_dependency 'schnitzelstyle', '~> 0.0.2'
|
29
|
+
gem.add_dependency 'nokogiri'
|
30
|
+
gem.add_dependency 'thor'
|
31
|
+
gem.add_dependency 'psych'
|
32
|
+
gem.add_dependency 'i18n'
|
33
|
+
gem.add_dependency 'tilt', '~> 1.3.0'
|
29
34
|
|
30
|
-
gem.add_development_dependency '
|
35
|
+
gem.add_development_dependency 'rake'
|
36
|
+
gem.add_development_dependency 'riot'
|
37
|
+
gem.add_development_dependency 'rack-test'
|
38
|
+
gem.add_development_dependency 'watchr'
|
39
|
+
gem.add_development_dependency 'awesome_print'
|
31
40
|
end
|
data/test/app_test.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class MyTestApp < Schreihals::App
|
4
|
+
set :documents_source, './test/posts'
|
5
|
+
set :blog_title, "A Little Test Blog"
|
6
|
+
set :footer, "This is just a test blog. Have fun."
|
7
|
+
end
|
8
|
+
|
9
|
+
context Schreihals::App do
|
10
|
+
setup { Rack::MockRequest.new(MyTestApp.new) }
|
11
|
+
|
12
|
+
context "when loading the home page" do
|
13
|
+
setup { topic.get '/' }
|
14
|
+
|
15
|
+
asserts(:status).equals 200
|
16
|
+
asserts(:body).present
|
17
|
+
asserts(:content_type).equals 'text/html;charset=utf-8'
|
18
|
+
asserts(:body).includes_elements("section.posts article.post", 2)
|
19
|
+
|
20
|
+
asserts(:body).includes_html('title' => MyTestApp.settings.blog_title)
|
21
|
+
asserts(:body).includes_html('.container>footer' => MyTestApp.settings.footer)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when loading a specific post's page" do
|
25
|
+
setup { topic.get '/2011/12/23/first-post/' }
|
26
|
+
|
27
|
+
asserts(:status).equals 200
|
28
|
+
asserts(:body).present
|
29
|
+
asserts(:content_type).equals 'text/html;charset=utf-8'
|
30
|
+
asserts(:body).includes_elements("article.post", 1)
|
31
|
+
asserts(:body).includes_html('article.post header h2 a' => "First Post\.")
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when loading a static page" do
|
35
|
+
setup { topic.get '/static-page/' }
|
36
|
+
|
37
|
+
asserts(:status).equals 200
|
38
|
+
asserts(:body).present
|
39
|
+
asserts(:content_type).equals 'text/html;charset=utf-8'
|
40
|
+
asserts(:body).includes_elements("article.post", 1)
|
41
|
+
asserts(:body).includes_html('article.post header h2 a' => "A Static Page\.")
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when loading the ATOM feed" do
|
45
|
+
setup { topic.get '/atom.xml' }
|
46
|
+
|
47
|
+
asserts(:status).equals 200
|
48
|
+
asserts(:body).present
|
49
|
+
asserts(:content_type).equals 'application/xml+atom'
|
50
|
+
asserts(:body).includes_elements("entry", 2)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when loading a static asset provided by schreihals" do
|
54
|
+
setup { topic.get '/favicon.ico' }
|
55
|
+
|
56
|
+
asserts(:status).equals 200
|
57
|
+
asserts(:body).present
|
58
|
+
asserts(:content_type).equals 'image/vnd.microsoft.icon'
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when loading an invalid URL" do
|
62
|
+
setup { topic.get '/ooga/booga/foo/to/the/bar' }
|
63
|
+
|
64
|
+
asserts(:status).equals 404
|
65
|
+
asserts(:body).present
|
66
|
+
asserts(:content_type).equals 'text/html;charset=utf-8'
|
67
|
+
end
|
68
|
+
end
|