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
data/.gitignore
CHANGED
data/.watchr
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
def run(cmd)
|
2
|
+
puts "=== %s" % cmd
|
3
|
+
system cmd
|
4
|
+
puts "\n"
|
5
|
+
end
|
6
|
+
|
7
|
+
watch("test/.*_test\.rb") { |m| run("bundle exec ruby %s" % m[0]) }
|
8
|
+
watch("lib/schreihals/(.*)\.rb") { |m| run("bundle exec ruby test/%s_test.rb" % m[1]) }
|
9
|
+
watch('^test/test_helper\.rb') { run "rake test" }
|
10
|
+
|
11
|
+
# Ctrl-\
|
12
|
+
Signal.trap('QUIT') { run("rake test") }
|
13
|
+
# Ctrl-C
|
14
|
+
Signal.trap('INT') { abort("\nQuitting.") }
|
data/README.md
CHANGED
@@ -29,12 +29,24 @@ Just a list of keywords I need to write about:
|
|
29
29
|
* pages (= posts without dates)
|
30
30
|
* deployment (Heroku!)
|
31
31
|
* code highlighting
|
32
|
-
* `google_analytics_id`
|
32
|
+
* `google_analytics_id` and `gauges_id`
|
33
|
+
* `footer`
|
33
34
|
* `link`
|
34
35
|
* `read_more`
|
36
|
+
* `schreihals create NAME`
|
37
|
+
* `schreihals post TITLE`
|
38
|
+
* `documents_store`, `documents_source`
|
39
|
+
* `twitter_id`
|
40
|
+
* cascading views
|
35
41
|
|
36
42
|
## Version History
|
37
43
|
|
44
|
+
### development
|
45
|
+
|
46
|
+
* Add `schreihals` executable. Use it to generate new Schreihals blogs (`schreihals create <name>`) and blog posts (`schreihals post <title>`).
|
47
|
+
* Removed dependency from `data_mapper` and added our own implementation.
|
48
|
+
* The contents of the `blog_description` configuration variable are now displayed at the top of the home page. The small footer at the bottom of all pages is now being populated through the `footer` variable.
|
49
|
+
|
38
50
|
### 0.0.2
|
39
51
|
|
40
52
|
* Fix various stupid bugs from the initial release, including the broken example app. Duh!
|
data/Rakefile
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
3
|
|
4
|
-
# integrate
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
4
|
+
# integrate riot
|
5
|
+
require 'rake/testtask'
|
6
|
+
Rake::TestTask.new(:test) do |test|
|
7
|
+
test.libs << 'lib' << 'test'
|
8
|
+
test.pattern = 'test/*_test.rb'
|
9
|
+
test.verbose = true
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :test
|
data/bin/schreihals
ADDED
data/example/Gemfile
CHANGED
@@ -3,7 +3,3 @@ source 'https://rubygems.org'
|
|
3
3
|
# In a real-world schreihals blog, remove the :path option, obviously.
|
4
4
|
#
|
5
5
|
gem 'schreihals', :path => '../'
|
6
|
-
|
7
|
-
# For the time being, we need this fork of document_mapper.
|
8
|
-
#
|
9
|
-
gem 'document_mapper', :git => 'git://github.com/hmans/document_mapper.git', :branch => "after_load_hook"
|
data/lib/schreihals.rb
CHANGED
@@ -1,155 +1,28 @@
|
|
1
1
|
require 'schreihals/version'
|
2
|
+
|
2
3
|
require 'sinatra'
|
3
4
|
require 'haml'
|
4
5
|
require 'sass'
|
5
6
|
require 'redcarpet'
|
6
7
|
require 'schnitzelstyle'
|
7
|
-
require 'document_mapper'
|
8
8
|
require 'rack-cache'
|
9
9
|
require 'coderay'
|
10
10
|
require 'rack/codehighlighter'
|
11
|
+
require 'yaml'
|
11
12
|
|
12
13
|
require 'active_support/core_ext/string/inflections'
|
14
|
+
require 'active_support/core_ext/class'
|
15
|
+
require 'active_support/concern'
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
self.attributes = {
|
22
|
-
disqus: true,
|
23
|
-
status: 'published',
|
24
|
-
summary: nil,
|
25
|
-
link: nil
|
26
|
-
}.merge(attributes)
|
27
|
-
|
28
|
-
# Set slug
|
29
|
-
#
|
30
|
-
if !attributes.has_key? :slug
|
31
|
-
begin
|
32
|
-
match = attributes[:file_name_without_extension].match(/(\d{4}-\d{1,2}-\d{1,2}[-_])?(.*)/)
|
33
|
-
attributes[:slug] = match[2]
|
34
|
-
rescue NoMethodError => err
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_url
|
40
|
-
date.present? ? "/#{year}/#{month}/#{day}/#{slug}/" : "/#{slug}/"
|
41
|
-
end
|
42
|
-
|
43
|
-
def disqus_identifier
|
44
|
-
attributes[:disqus_identifier] || file_name_without_extension
|
45
|
-
end
|
46
|
-
|
47
|
-
def disqus?
|
48
|
-
disqus && published?
|
49
|
-
end
|
50
|
-
|
51
|
-
def published?
|
52
|
-
status == 'published'
|
53
|
-
end
|
54
|
-
|
55
|
-
def post?
|
56
|
-
date.present?
|
57
|
-
end
|
58
|
-
|
59
|
-
def page?
|
60
|
-
!post?
|
61
|
-
end
|
62
|
-
|
63
|
-
# load all posts.
|
64
|
-
self.directory = 'posts'
|
65
|
-
end
|
66
|
-
|
67
|
-
class App < Sinatra::Application
|
68
|
-
set :blog_title, "My Schreihals Blog"
|
69
|
-
set :blog_url, ""
|
70
|
-
set :blog_description, ""
|
71
|
-
set :author_name, "Author"
|
72
|
-
set :disqus_name, nil
|
73
|
-
set :google_analytics_id, nil
|
74
|
-
set :read_more, "Read Complete Article"
|
75
|
-
|
76
|
-
use Rack::ShowExceptions
|
77
|
-
use Rack::Cache
|
78
|
-
use Rack::Static, :urls => ["/media"], :root => "public"
|
79
|
-
use Rack::Codehighlighter, :coderay, :markdown => true, :element => "pre>code", :pattern => /\A:::(\w+)\s*\n/
|
80
|
-
|
81
|
-
helpers do
|
82
|
-
def partial(thing, locals = {})
|
83
|
-
name = case thing
|
84
|
-
when String then thing
|
85
|
-
else thing.class.to_s.demodulize.underscore
|
86
|
-
end
|
17
|
+
require 'schreihals/static'
|
18
|
+
require 'schreihals/helpers'
|
19
|
+
require 'schreihals/document'
|
20
|
+
require 'schreihals/post'
|
21
|
+
require 'schreihals/actions'
|
22
|
+
require 'schreihals/app'
|
87
23
|
|
88
|
-
|
89
|
-
|
24
|
+
Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("../views", __FILE__))
|
25
|
+
Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("./views"))
|
90
26
|
|
91
|
-
|
92
|
-
@page_title = title
|
93
|
-
end
|
94
|
-
|
95
|
-
def link_to(title, thing)
|
96
|
-
haml "%a{href: '#{url_for thing}'} #{title}"
|
97
|
-
end
|
98
|
-
|
99
|
-
def url_for(thing, options = {})
|
100
|
-
url = thing.respond_to?(:to_url) ? thing.to_url : thing.to_s
|
101
|
-
url = "#{settings.blog_url}#{url}" if options[:absolute]
|
102
|
-
url
|
103
|
-
end
|
104
|
-
|
105
|
-
def show_disqus?
|
106
|
-
settings.disqus_name.present?
|
107
|
-
end
|
108
|
-
|
109
|
-
def production?
|
110
|
-
settings.environment.to_sym == :production
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
before do
|
115
|
-
cache_control :public, :must_revalidate, :max_age => 60
|
116
|
-
end
|
117
|
-
|
118
|
-
get '/' do
|
119
|
-
@posts = Post.order_by(:date => :desc)
|
120
|
-
@posts = @posts.where(:status => 'published') if production?
|
121
|
-
@posts = @posts.limit(10).all
|
122
|
-
haml :index
|
123
|
-
end
|
124
|
-
|
125
|
-
get '/schreihals.css' do
|
126
|
-
scss :schreihals
|
127
|
-
end
|
128
|
-
|
129
|
-
get '/atom.xml' do
|
130
|
-
@posts = Post.where(:status => 'published').order_by(:date => :desc).limit(10).all
|
131
|
-
content_type 'application/xml+atom'
|
132
|
-
haml :atom, :layout => false
|
133
|
-
end
|
134
|
-
|
135
|
-
get '/:year/:month/:day/:slug/?' do |year, month, day, slug|
|
136
|
-
render_page(slug)
|
137
|
-
end
|
138
|
-
|
139
|
-
get '/:slug/?' do |slug|
|
140
|
-
render_page(slug)
|
141
|
-
end
|
142
|
-
|
143
|
-
def render_page(slug)
|
144
|
-
if @post = Post.where(:slug => slug).first
|
145
|
-
haml :post
|
146
|
-
else
|
147
|
-
halt 404
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
not_found do
|
152
|
-
haml :"404"
|
153
|
-
end
|
154
|
-
end
|
27
|
+
module Schreihals
|
155
28
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Schreihals
|
2
|
+
module Actions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before do
|
7
|
+
refresh_documents! if refresh_documents_now?
|
8
|
+
cache_control :public, :must_revalidate, :max_age => 60
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/' do
|
12
|
+
@posts = Post.latest(published_only: production?)
|
13
|
+
|
14
|
+
@show_description = true
|
15
|
+
haml :index
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/blog.css' do
|
19
|
+
scss :blog
|
20
|
+
end
|
21
|
+
|
22
|
+
get '/atom.xml' do
|
23
|
+
@posts = Post.latest(published_only: production?)
|
24
|
+
|
25
|
+
xml = haml :atom, :layout => false
|
26
|
+
|
27
|
+
doc = Nokogiri::XML(xml)
|
28
|
+
doc.css("content img").each do |node|
|
29
|
+
node['src'] = absolutionize(node['src'])
|
30
|
+
end
|
31
|
+
|
32
|
+
content_type 'application/xml+atom'
|
33
|
+
doc.to_xml
|
34
|
+
end
|
35
|
+
|
36
|
+
get '/:year/:month/:day/:slug/?' do |year, month, day, slug|
|
37
|
+
render_page(slug)
|
38
|
+
end
|
39
|
+
|
40
|
+
get '/:slug/?' do |slug|
|
41
|
+
render_page(slug)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Schreihals
|
2
|
+
class App < Sinatra::Base
|
3
|
+
set :views, ['./views/', File.expand_path('../../views/', __FILE__)]
|
4
|
+
set :public_folder, File.expand_path('../../public/', __FILE__)
|
5
|
+
|
6
|
+
use Schreihals::Static
|
7
|
+
use Rack::ShowExceptions
|
8
|
+
use Rack::Cache
|
9
|
+
use Rack::Codehighlighter, :coderay, :markdown => true, :element => "pre>code", :pattern => /\A:::(\w+)\s*\n/
|
10
|
+
|
11
|
+
helpers Schreihals::Helpers
|
12
|
+
include Schreihals::Actions
|
13
|
+
|
14
|
+
configure do
|
15
|
+
set :blog_title, "My Schreihals Blog"
|
16
|
+
set :blog_url, ""
|
17
|
+
set :blog_description, ""
|
18
|
+
set :author_name, "Author"
|
19
|
+
set :disqus_name, nil
|
20
|
+
set :google_analytics_id, nil
|
21
|
+
set :gauges_id, nil
|
22
|
+
set :read_more, "Read Complete Article"
|
23
|
+
set :documents_store, :filesystem
|
24
|
+
set :documents_source, './posts'
|
25
|
+
set :documents_cache, nil
|
26
|
+
set :twitter_id, nil
|
27
|
+
set :footer, ""
|
28
|
+
end
|
29
|
+
|
30
|
+
def refresh_documents_now?
|
31
|
+
!Post.documents.any?
|
32
|
+
end
|
33
|
+
|
34
|
+
def refresh_documents!
|
35
|
+
case settings.documents_store
|
36
|
+
when :filesystem
|
37
|
+
Post.load_from_directory(settings.documents_source)
|
38
|
+
# when :dropbox
|
39
|
+
# Post.load_from_dropbox(settings.documents_source)
|
40
|
+
else
|
41
|
+
raise "Unknown documents store '#{settings.documents_store}'."
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_page(slug)
|
46
|
+
if @post = Post.with_slug(slug)
|
47
|
+
haml :post
|
48
|
+
else
|
49
|
+
halt 404
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def absolutionize(url)
|
54
|
+
if should_absolutionize?(url)
|
55
|
+
"#{base_url}#{url}"
|
56
|
+
else
|
57
|
+
url
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def should_absolutionize?(url)
|
62
|
+
url && url[0] == '/'
|
63
|
+
end
|
64
|
+
|
65
|
+
def base_url
|
66
|
+
"#{env['rack.url_scheme']}://#{env['HTTP_HOST']}"
|
67
|
+
end
|
68
|
+
|
69
|
+
not_found do
|
70
|
+
haml :"404"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "thor"
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
3
|
+
|
4
|
+
module Schreihals
|
5
|
+
class Cli < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
source_root(File.expand_path('../../templates', __FILE__))
|
9
|
+
|
10
|
+
desc "create NAME", "Creates a new Schreihals blog."
|
11
|
+
|
12
|
+
method_option :git, :aliases => "-g", :default => false,
|
13
|
+
:desc => "Initialize a git repository in your blog's directory."
|
14
|
+
|
15
|
+
method_option :bundle, :aliases => "-b", :default => false,
|
16
|
+
:desc => "Run 'bundle install' after generating your new blog."
|
17
|
+
|
18
|
+
def create(name)
|
19
|
+
@name = name
|
20
|
+
self.destination_root = name
|
21
|
+
directory 'new_blog', '.'
|
22
|
+
post('My First Post')
|
23
|
+
|
24
|
+
in_root do
|
25
|
+
run "bundle" if options[:bundle]
|
26
|
+
run "git init" if options[:git]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
desc "post TITLE", "Creates a new blog post."
|
32
|
+
|
33
|
+
def post(title)
|
34
|
+
@title = title
|
35
|
+
@date = Date.today.strftime("%Y-%m-%d")
|
36
|
+
@slug = title.downcase.gsub(/ +/,'-')
|
37
|
+
@text = "Type your post body here."
|
38
|
+
|
39
|
+
template 'new-post.md.tt', "posts/#{@date}-#{@slug}.md"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module Schreihals
|
4
|
+
class Document
|
5
|
+
cattr_accessor :documents
|
6
|
+
@@documents = []
|
7
|
+
|
8
|
+
attr_accessor :attributes
|
9
|
+
|
10
|
+
def initialize(attrs = {})
|
11
|
+
@attributes = attrs
|
12
|
+
@@documents << self
|
13
|
+
end
|
14
|
+
|
15
|
+
def file_extension
|
16
|
+
File.extname(file_name).sub(/^\./, '')
|
17
|
+
end
|
18
|
+
|
19
|
+
def file_name_without_extension
|
20
|
+
File.basename(file_name, '.'+file_extension)
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(name, *args)
|
24
|
+
attributes.has_key?(name.to_s) ? attributes[name.to_s] : super
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_html
|
28
|
+
Tilt.new(file_extension) { body }.render
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def from_string(s, attrs = {})
|
33
|
+
frontmatter, body = split_original_document(s)
|
34
|
+
new(Psych.load(frontmatter).
|
35
|
+
merge('body' => body.strip).
|
36
|
+
merge(attrs))
|
37
|
+
end
|
38
|
+
|
39
|
+
def from_file(name)
|
40
|
+
from_string(open(name).read, 'file_name' => File.basename(name))
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_from_directory(dir)
|
44
|
+
Dir[File.join(dir, "*")].collect { |f| from_file f }
|
45
|
+
end
|
46
|
+
|
47
|
+
def split_original_document(s)
|
48
|
+
s =~ /.*(---\s*\n.*)\n---\s*\n(.*)/m ? [$1, $2] : [nil, s]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|