schreihals 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -1
- data/.rspec +1 -0
- data/.travis.yml +9 -0
- data/.watchr +6 -5
- data/LICENSE +2 -2
- data/README.md +2 -54
- data/Rakefile +4 -9
- data/lib/schreihals.rb +10 -9
- data/lib/schreihals/actions/admin.rb +49 -0
- data/lib/schreihals/actions/auth.rb +29 -0
- data/lib/schreihals/actions/blog.rb +68 -0
- data/lib/schreihals/app.rb +15 -43
- data/lib/schreihals/cli.rb +0 -14
- data/lib/schreihals/helpers.rb +38 -5
- data/lib/schreihals/post.rb +126 -44
- data/lib/schreihals/rake.rb +21 -0
- data/lib/schreihals/version.rb +1 -1
- data/lib/templates/new_blog/Gemfile +17 -1
- data/lib/templates/new_blog/Rakefile +2 -0
- data/lib/templates/new_blog/app.rb.tt +28 -0
- data/lib/templates/new_blog/config.ru.tt +2 -14
- data/{example/public/media → lib/templates/new_blog/public}/.gitkeep +0 -0
- data/lib/views/admin/admin.haml +12 -0
- data/lib/views/admin/edit.haml +3 -0
- data/lib/views/admin/new.haml +3 -0
- data/lib/views/atom.haml +12 -10
- data/lib/views/index.haml +3 -0
- data/lib/views/layout.haml +4 -3
- data/lib/views/partials/_admin_post_list.haml +11 -0
- data/lib/views/partials/_form_field.haml +27 -0
- data/lib/views/partials/_post.haml +16 -12
- data/lib/views/partials/_post_form.haml +12 -0
- data/lib/views/schreihals.scss +61 -25
- data/schreihals.gemspec +27 -11
- data/spec/app_spec.rb +56 -0
- data/spec/factories.rb +15 -0
- data/spec/post_spec.rb +60 -0
- data/spec/spec_helper.rb +35 -0
- data/{test → test.old}/app_test.rb +0 -0
- data/{test → test.old}/files/simple_document.md +0 -0
- data/test.old/post_test.rb +62 -0
- data/{test → test.old}/posts/2011-12-23-first-post.md +0 -0
- data/{test → test.old}/posts/2011-12-24-second-post.md +0 -0
- data/{test → test.old}/posts/static-page.md +0 -0
- data/{test → test.old}/test_helper.rb +0 -0
- metadata +158 -87
- data/example/Gemfile +0 -5
- data/example/config.ru +0 -8
- data/example/posts/2010-06-17-pythton-is-great.md +0 -9
- data/example/posts/2011-12-22-ruby-is-great.md +0 -16
- data/lib/schreihals/actions.rb +0 -45
- data/lib/schreihals/document.rb +0 -52
- data/lib/templates/first-post.md.tt +0 -1
- data/lib/templates/new-post.md.tt +0 -13
- data/lib/templates/new_blog/posts/.gitkeep +0 -0
- data/lib/templates/new_blog/public/media/.gitkeep +0 -0
- data/test/document_test.rb +0 -41
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.2
|
4
|
+
- 1.9.3
|
5
|
+
#- jruby-19mode # JRuby in 1.9 mode
|
6
|
+
# - rbx-18mode
|
7
|
+
# - rbx-19mode # currently in active development, may or may not work for your project
|
8
|
+
# uncomment this line if your project needs to run something other than `rake`:
|
9
|
+
# script: bundle exec rspec spec
|
data/.watchr
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
def run(cmd)
|
1
|
+
def run(cmd, msg = nil)
|
2
|
+
puts "=== %s" % msg if msg
|
2
3
|
puts "=== %s" % cmd
|
3
4
|
system cmd
|
4
5
|
puts "\n"
|
5
6
|
end
|
6
7
|
|
7
|
-
watch("
|
8
|
-
watch("lib/schreihals/(.*)\.rb") { |m| run("bundle exec
|
9
|
-
watch('^
|
8
|
+
watch("spec/.*_spec\.rb") { |m| run("bundle exec rspec %s" % m[0]) }
|
9
|
+
watch("lib/schreihals/(.*)\.rb") { |m| run("bundle exec rspec spec/%s_spec.rb" % m[1]) }
|
10
|
+
watch('^spec/(spec_helper|factories)\.rb') { |f| run "bundle exec rake spec", "%s.rb has been modified" % f }
|
10
11
|
|
11
12
|
# Ctrl-\
|
12
|
-
Signal.trap('QUIT') { run("rake
|
13
|
+
Signal.trap('QUIT') { run("bundle exec rake spec") }
|
13
14
|
# Ctrl-C
|
14
15
|
Signal.trap('INT') { abort("\nQuitting.") }
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c)
|
1
|
+
Copyright (c) 2012 Hendrik Mans
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,57 +1,5 @@
|
|
1
1
|
# Schreihals
|
2
2
|
|
3
|
-
|
4
|
-
Documentation is pretty minimal at the moment, so for now, how about a list of
|
5
|
-
overall design goals?
|
3
|
+
## A blogging engine for sane hackers.
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
* Minimal code design.
|
10
|
-
* Author your blog through git, using Markdown and friends.
|
11
|
-
* Near-zero boilerplate code in your blog project. (It's all in the gem.)
|
12
|
-
* Theme support through gems.
|
13
|
-
* No static document generation. (Not interested, there are enough engines out there that do this.)
|
14
|
-
* HAML and SASS support out of the box.
|
15
|
-
|
16
|
-
## Usage
|
17
|
-
|
18
|
-
TODO: soon
|
19
|
-
|
20
|
-
## Stuff
|
21
|
-
|
22
|
-
Just a list of keywords I need to write about:
|
23
|
-
|
24
|
-
* Configuration
|
25
|
-
* Automatic `date` and `slug` recognition
|
26
|
-
* `status` attribute and drafts
|
27
|
-
* `disqus` and `disqus_identifier` attributes
|
28
|
-
* Using different post formats (markdown, textile, haml etc.)
|
29
|
-
* pages (= posts without dates)
|
30
|
-
* deployment (Heroku!)
|
31
|
-
* code highlighting
|
32
|
-
* `google_analytics_id` and `gauges_id`
|
33
|
-
* `footer`
|
34
|
-
* `link`
|
35
|
-
* `read_more`
|
36
|
-
* `schreihals create NAME`
|
37
|
-
* `schreihals post TITLE`
|
38
|
-
* `documents_store`, `documents_source`
|
39
|
-
* `twitter_id`
|
40
|
-
* cascading views
|
41
|
-
|
42
|
-
## Version History
|
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
|
-
|
50
|
-
### 0.0.2
|
51
|
-
|
52
|
-
* Fix various stupid bugs from the initial release, including the broken example app. Duh!
|
53
|
-
* When a post or page could not be found, we now display a proper 404 page (with correct status code.)
|
54
|
-
|
55
|
-
### 0.0.1
|
56
|
-
|
57
|
-
* Initial release.
|
5
|
+
[![Build Status](https://secure.travis-ci.org/hmans/schreihals.png?branch=master)](http://travis-ci.org/hmans/schreihals)
|
data/Rakefile
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
3
|
|
4
|
-
# integrate
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
8
|
-
test.pattern = 'test/*_test.rb'
|
9
|
-
test.verbose = true
|
10
|
-
end
|
11
|
-
|
12
|
-
task :default => :test
|
4
|
+
# integrate rspec
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
RSpec::Core::RakeTask.new('spec')
|
7
|
+
task :default => :spec
|
data/lib/schreihals.rb
CHANGED
@@ -6,23 +6,24 @@ require 'sass'
|
|
6
6
|
require 'redcarpet'
|
7
7
|
require 'schnitzelstyle'
|
8
8
|
require 'rack-cache'
|
9
|
-
require '
|
10
|
-
require 'rack/codehighlighter'
|
11
|
-
require 'yaml'
|
9
|
+
require 'mongoid'
|
12
10
|
|
13
|
-
require 'active_support/
|
11
|
+
require 'active_support/inflector'
|
14
12
|
require 'active_support/core_ext/class'
|
15
13
|
require 'active_support/concern'
|
16
14
|
|
17
|
-
require 'schreihals/static'
|
18
|
-
require 'schreihals/helpers'
|
19
|
-
require 'schreihals/document'
|
20
|
-
require 'schreihals/post'
|
21
|
-
require 'schreihals/actions'
|
22
15
|
require 'schreihals/app'
|
23
16
|
|
24
17
|
Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("../views", __FILE__))
|
25
18
|
Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("./views"))
|
26
19
|
|
20
|
+
Mongoid.logger.level = 3
|
21
|
+
|
27
22
|
module Schreihals
|
23
|
+
mattr_reader :mongo_uri
|
24
|
+
|
25
|
+
def self.mongo_uri=(uri)
|
26
|
+
Mongoid::Config.from_hash("uri" => uri)
|
27
|
+
@@mongo_uri = uri
|
28
|
+
end
|
28
29
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Schreihals
|
2
|
+
module Actions
|
3
|
+
module Admin
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
before '/admin/?*' do
|
8
|
+
admin_only!
|
9
|
+
end
|
10
|
+
|
11
|
+
get '/admin/?' do
|
12
|
+
@posts = Post.published.posts.desc(:published_at)
|
13
|
+
@pages = Post.published.pages
|
14
|
+
@drafts = Post.drafts
|
15
|
+
haml :'admin/admin'
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/admin/new/?' do
|
19
|
+
@post = Post.new
|
20
|
+
haml :'admin/new'
|
21
|
+
end
|
22
|
+
|
23
|
+
post '/admin/new/?' do
|
24
|
+
@post = Post.new(params[:post])
|
25
|
+
if @post.save
|
26
|
+
redirect url_for(@post)
|
27
|
+
else
|
28
|
+
haml :'admin/new'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/admin/edit/:id/?' do
|
33
|
+
@post = Post.find(params[:id])
|
34
|
+
haml :'admin/edit'
|
35
|
+
end
|
36
|
+
|
37
|
+
post '/admin/edit/:id/?' do
|
38
|
+
@post = Post.find(params[:id])
|
39
|
+
@post.attributes = params[:post]
|
40
|
+
if @post.save
|
41
|
+
redirect url_for(@post)
|
42
|
+
else
|
43
|
+
haml :'admin/edit'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'omniauth'
|
2
|
+
require 'omniauth-browserid'
|
3
|
+
|
4
|
+
module Schreihals
|
5
|
+
module Actions
|
6
|
+
module Auth
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
included do
|
10
|
+
use OmniAuth::Strategies::BrowserID
|
11
|
+
|
12
|
+
post '/auth/:provider/callback' do
|
13
|
+
auth = request.env['omniauth.auth']
|
14
|
+
session[:user] = "#{auth['provider']}:#{auth['uid']}"
|
15
|
+
redirect admin_logged_in? ? '/admin/' : '/'
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/login' do
|
19
|
+
redirect '/auth/browser_id'
|
20
|
+
end
|
21
|
+
|
22
|
+
get '/logout' do
|
23
|
+
session[:user] = nil
|
24
|
+
redirect '/'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Schreihals
|
2
|
+
module Actions
|
3
|
+
module Blog
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
get '/' do
|
8
|
+
# cache_for 5.minutes
|
9
|
+
@posts = Post.latest.skip(params[:page].to_i * 10).limit(10)
|
10
|
+
@show_description = true
|
11
|
+
haml :index
|
12
|
+
end
|
13
|
+
|
14
|
+
get '/blog.css' do
|
15
|
+
cache_for 1.hour
|
16
|
+
scss :blog
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/index.atom' do
|
20
|
+
cache_for 3.minutes
|
21
|
+
@posts = Post.latest.limit(10)
|
22
|
+
content_type 'application/atom+xml; charset=utf-8'
|
23
|
+
haml :atom, :format => :xhtml, :layout => false
|
24
|
+
end
|
25
|
+
|
26
|
+
get '/feed/?' do
|
27
|
+
redirect settings.feed_url
|
28
|
+
end
|
29
|
+
|
30
|
+
get %r{^/(\d{4})/(\d{1,2})/?$} do
|
31
|
+
year, month = params[:captures]
|
32
|
+
@posts = Post.for_month(year.to_i, month.to_i)
|
33
|
+
haml :index
|
34
|
+
end
|
35
|
+
|
36
|
+
get %r{^/(\d{4})/?$} do
|
37
|
+
year = params[:captures].first
|
38
|
+
@posts = Post.for_year(year.to_i)
|
39
|
+
haml :index
|
40
|
+
end
|
41
|
+
|
42
|
+
get '/:year/:month/:day/:slug/?' do |year, month, day, slug|
|
43
|
+
# cache_for 1.hour
|
44
|
+
render_post(slug)
|
45
|
+
end
|
46
|
+
|
47
|
+
get '/:slug/?' do |slug|
|
48
|
+
# cache_for 1.hour
|
49
|
+
render_post(slug)
|
50
|
+
end
|
51
|
+
|
52
|
+
def render_post(slug)
|
53
|
+
|
54
|
+
if @post = Post.where(slugs: slug).first
|
55
|
+
# enforce canonical URL
|
56
|
+
if request.path != url_for(@post)
|
57
|
+
redirect url_for(@post)
|
58
|
+
else
|
59
|
+
haml :post
|
60
|
+
end
|
61
|
+
else
|
62
|
+
halt 404
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/schreihals/app.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
require 'schreihals/static'
|
2
|
+
require 'schreihals/helpers'
|
3
|
+
require 'schreihals/post'
|
4
|
+
require 'schreihals/actions/blog'
|
5
|
+
require 'schreihals/actions/auth'
|
6
|
+
require 'schreihals/actions/admin'
|
7
|
+
|
1
8
|
module Schreihals
|
2
9
|
class App < Sinatra::Base
|
3
10
|
set :views, ['./views/', File.expand_path('../../views/', __FILE__)]
|
@@ -6,64 +13,29 @@ module Schreihals
|
|
6
13
|
use Schreihals::Static
|
7
14
|
use Rack::ShowExceptions
|
8
15
|
use Rack::Cache
|
9
|
-
use Rack::
|
16
|
+
use Rack::Session::Cookie
|
10
17
|
|
11
18
|
helpers Schreihals::Helpers
|
12
|
-
include Schreihals::Actions
|
19
|
+
include Schreihals::Actions::Auth
|
20
|
+
include Schreihals::Actions::Admin
|
21
|
+
include Schreihals::Actions::Blog
|
13
22
|
|
14
23
|
configure do
|
15
24
|
set :blog_title, "My Schreihals Blog"
|
16
|
-
set :blog_url, ""
|
17
25
|
set :blog_description, ""
|
18
26
|
set :author_name, "Author"
|
19
27
|
set :disqus_name, nil
|
20
28
|
set :google_analytics_id, nil
|
21
29
|
set :gauges_id, nil
|
22
30
|
set :read_more, "Read Complete Article"
|
23
|
-
set :documents_store, :filesystem
|
24
|
-
set :documents_source, './posts'
|
25
|
-
set :documents_cache, nil
|
26
31
|
set :twitter_id, nil
|
27
32
|
set :footer, ""
|
33
|
+
set :administrator, nil
|
34
|
+
set :feed_url, '/index.atom'
|
28
35
|
end
|
29
36
|
|
30
|
-
def
|
31
|
-
|
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']}"
|
37
|
+
def cache_for(time)
|
38
|
+
cache_control :public, :must_revalidate, :max_age => time.to_i
|
67
39
|
end
|
68
40
|
|
69
41
|
not_found do
|
data/lib/schreihals/cli.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "thor"
|
2
|
-
require 'active_support/core_ext/string/inflections'
|
3
2
|
|
4
3
|
module Schreihals
|
5
4
|
class Cli < Thor
|
@@ -19,24 +18,11 @@ module Schreihals
|
|
19
18
|
@name = name
|
20
19
|
self.destination_root = name
|
21
20
|
directory 'new_blog', '.'
|
22
|
-
post('My First Post')
|
23
21
|
|
24
22
|
in_root do
|
25
23
|
run "bundle" if options[:bundle]
|
26
24
|
run "git init" if options[:git]
|
27
25
|
end
|
28
26
|
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
27
|
end
|
42
28
|
end
|
data/lib/schreihals/helpers.rb
CHANGED
@@ -4,6 +4,10 @@ module Schreihals
|
|
4
4
|
Array(views).each { |v| super(v, name, engine, &block) }
|
5
5
|
end
|
6
6
|
|
7
|
+
def base_url
|
8
|
+
"#{env['rack.url_scheme']}://#{env['HTTP_HOST']}/"
|
9
|
+
end
|
10
|
+
|
7
11
|
def partial(thing, locals = {})
|
8
12
|
name = case thing
|
9
13
|
when String then thing
|
@@ -17,13 +21,9 @@ module Schreihals
|
|
17
21
|
@page_title = title
|
18
22
|
end
|
19
23
|
|
20
|
-
def link_to(title, thing)
|
21
|
-
haml "%a{href: '#{url_for thing}'} #{title}"
|
22
|
-
end
|
23
|
-
|
24
24
|
def url_for(thing, options = {})
|
25
25
|
url = thing.respond_to?(:to_url) ? thing.to_url : thing.to_s
|
26
|
-
url = "#{
|
26
|
+
url = "#{base_url.sub(/\/$/, '')}#{url}" if options[:absolute]
|
27
27
|
url
|
28
28
|
end
|
29
29
|
|
@@ -34,5 +34,38 @@ module Schreihals
|
|
34
34
|
def production?
|
35
35
|
settings.environment.to_sym == :production
|
36
36
|
end
|
37
|
+
|
38
|
+
def user_logged_in?
|
39
|
+
session[:user].present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def admin_logged_in?
|
43
|
+
user_logged_in? && session[:user] == settings.administrator
|
44
|
+
end
|
45
|
+
|
46
|
+
def admin_only!
|
47
|
+
redirect '/login' unless admin_logged_in?
|
48
|
+
end
|
49
|
+
|
50
|
+
def form_field(object, attribute, options = {})
|
51
|
+
options = {
|
52
|
+
label: attribute.to_s.humanize,
|
53
|
+
value: object.send(attribute),
|
54
|
+
errors: object.errors[attribute.to_sym],
|
55
|
+
class_name: object.class.to_s.demodulize.underscore
|
56
|
+
}.merge(options)
|
57
|
+
|
58
|
+
options[:name] ||= "#{options[:class_name]}[#{attribute}]"
|
59
|
+
options[:id] ||= object.new_record? ?
|
60
|
+
"new_#{options[:class_name]}_#{attribute}" :
|
61
|
+
"#{options[:class_name]}_#{object.id}_#{attribute}"
|
62
|
+
|
63
|
+
options[:type] ||= case options[:value]
|
64
|
+
when DateTime, Time, Date then :datetime
|
65
|
+
else :text
|
66
|
+
end
|
67
|
+
|
68
|
+
partial 'form_field', object: object, attribute: attribute, options: options
|
69
|
+
end
|
37
70
|
end
|
38
71
|
end
|