schnitzelpress 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.md +20 -3
- data/README.md +4 -2
- data/bin/schnitzelpress +1 -1
- data/lib/assets/js/jquery-1.7.1.js +673 -0
- data/lib/assets/js/jquery-ujs.js +373 -0
- data/lib/assets/js/jquery.cookie.js +47 -0
- data/lib/{public → assets}/js/schnitzelpress.js +7 -2
- data/lib/public/font/fontawesome-webfont.eot +0 -0
- data/lib/public/font/fontawesome-webfont.svg +175 -0
- data/lib/public/font/fontawesome-webfont.svgz +0 -0
- data/lib/public/font/fontawesome-webfont.ttf +0 -0
- data/lib/public/font/fontawesome-webfont.woff +0 -0
- data/lib/schnitzelpress.rb +21 -5
- data/lib/schnitzelpress/actions/admin.rb +21 -1
- data/lib/schnitzelpress/actions/assets.rb +36 -0
- data/lib/schnitzelpress/actions/auth.rb +16 -3
- data/lib/schnitzelpress/actions/blog.rb +10 -12
- data/lib/schnitzelpress/app.rb +16 -19
- data/lib/schnitzelpress/cache_control.rb +17 -0
- data/lib/schnitzelpress/cli.rb +40 -9
- data/lib/schnitzelpress/config.rb +43 -0
- data/lib/schnitzelpress/env.rb +5 -0
- data/lib/schnitzelpress/helpers.rb +168 -4
- data/lib/schnitzelpress/markdown_renderer.rb +2 -2
- data/lib/schnitzelpress/post.rb +2 -6
- data/lib/schnitzelpress/static.rb +1 -1
- data/lib/schnitzelpress/version.rb +2 -2
- data/lib/templates/new_blog/Gemfile +4 -5
- data/lib/templates/new_blog/Gemfile.lock.tt +137 -0
- data/lib/templates/new_blog/Procfile +1 -1
- data/lib/templates/new_blog/config.ru.tt +6 -17
- data/lib/views/admin/admin.haml +3 -2
- data/lib/views/admin/config.haml +27 -0
- data/lib/views/atom.haml +3 -3
- data/lib/views/index.haml +1 -1
- data/lib/views/layout.haml +15 -11
- data/lib/views/login.haml +4 -0
- data/lib/views/partials/_admin_post_list.haml +8 -8
- data/lib/views/partials/_disqus.haml +1 -1
- data/lib/views/partials/_gauges.haml +1 -1
- data/lib/views/partials/_google_analytics.haml +1 -1
- data/lib/views/partials/_post.haml +6 -5
- data/lib/views/partials/_post_form.haml +3 -1
- data/lib/views/post.haml +4 -1
- data/lib/views/schnitzelpress.scss +67 -3
- data/schnitzelpress.gemspec +5 -2
- data/spec/app_spec.rb +5 -11
- data/spec/assets_spec.rb +26 -0
- data/spec/factories.rb +1 -1
- data/spec/post_spec.rb +9 -2
- data/spec/spec_helper.rb +2 -1
- metadata +115 -71
- data/lib/schnitzelpress/rake.rb +0 -20
- data/lib/templates/new_blog/Rakefile +0 -2
- data/lib/templates/new_blog/app.rb.tt +0 -34
- data/lib/templates/new_blog/config/unicorn.rb +0 -5
- data/lib/templates/new_blog/public/.gitkeep +0 -0
Binary file
|
Binary file
|
Binary file
|
data/lib/schnitzelpress.rb
CHANGED
@@ -6,6 +6,7 @@ require 'sass'
|
|
6
6
|
require 'redcarpet'
|
7
7
|
require 'schnitzelstyle'
|
8
8
|
require 'rack/contrib'
|
9
|
+
require 'rack/cache'
|
9
10
|
require 'mongoid'
|
10
11
|
require 'chronic'
|
11
12
|
|
@@ -13,10 +14,14 @@ require 'active_support/inflector'
|
|
13
14
|
require 'active_support/core_ext/class'
|
14
15
|
require 'active_support/concern'
|
15
16
|
|
17
|
+
require 'schnitzelpress/cache_control'
|
18
|
+
require 'schnitzelpress/env'
|
16
19
|
require 'schnitzelpress/static'
|
17
20
|
require 'schnitzelpress/helpers'
|
18
21
|
require 'schnitzelpress/markdown_renderer'
|
22
|
+
require 'schnitzelpress/config'
|
19
23
|
require 'schnitzelpress/post'
|
24
|
+
require 'schnitzelpress/actions/assets'
|
20
25
|
require 'schnitzelpress/actions/blog'
|
21
26
|
require 'schnitzelpress/actions/auth'
|
22
27
|
require 'schnitzelpress/actions/admin'
|
@@ -27,18 +32,29 @@ Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("./views"))
|
|
27
32
|
|
28
33
|
Mongoid.logger.level = 3
|
29
34
|
|
30
|
-
module
|
35
|
+
module Schnitzelpress
|
31
36
|
mattr_reader :mongo_uri
|
32
37
|
|
33
38
|
class << self
|
34
39
|
def mongo_uri=(uri)
|
35
40
|
Mongoid::Config.from_hash("uri" => uri)
|
36
|
-
|
41
|
+
Schnitzelpress::Post.create_indexes
|
37
42
|
@@mongo_uri = uri
|
38
43
|
end
|
39
44
|
|
40
|
-
def
|
41
|
-
(
|
45
|
+
def init!
|
46
|
+
# Mongoid.load!("./config/mongo.yml")
|
47
|
+
if mongo_uri = ENV['MONGOLAB_URI'] || ENV['MONGOHQ_URL'] || ENV['MONGO_URL']
|
48
|
+
self.mongo_uri = mongo_uri
|
49
|
+
else
|
50
|
+
raise "Please set MONGO_URL, MONGOHQ_URL or MONGOLAB_URI to your MongoDB connection string."
|
51
|
+
end
|
52
|
+
Schnitzelpress::Post.create_indexes
|
53
|
+
end
|
54
|
+
|
55
|
+
def omnomnom!
|
56
|
+
init!
|
57
|
+
App.with_local_files
|
42
58
|
end
|
43
59
|
end
|
44
60
|
end
|
@@ -48,7 +64,7 @@ module Haml::Filters::Redcarpet
|
|
48
64
|
include Haml::Filters::Base
|
49
65
|
|
50
66
|
def render(text)
|
51
|
-
Redcarpet::Markdown.new(
|
67
|
+
Redcarpet::Markdown.new(Schnitzelpress::MarkdownRenderer,
|
52
68
|
:autolink => true, :space_after_headers => true, :fenced_code_blocks => true).
|
53
69
|
render(text)
|
54
70
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module Schnitzelpress
|
2
2
|
module Actions
|
3
3
|
module Admin
|
4
4
|
extend ActiveSupport::Concern
|
@@ -15,6 +15,20 @@ module SchnitzelPress
|
|
15
15
|
haml :'admin/admin'
|
16
16
|
end
|
17
17
|
|
18
|
+
get '/admin/config/?' do
|
19
|
+
haml :'admin/config'
|
20
|
+
end
|
21
|
+
|
22
|
+
post '/admin/config' do
|
23
|
+
config.attributes = params[:config]
|
24
|
+
if config.save
|
25
|
+
CacheControl.bust!
|
26
|
+
redirect '/admin'
|
27
|
+
else
|
28
|
+
haml :'admin/config'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
18
32
|
get '/admin/new/?' do
|
19
33
|
@post = Post.new
|
20
34
|
haml :'admin/new'
|
@@ -43,6 +57,12 @@ module SchnitzelPress
|
|
43
57
|
haml :'admin/edit'
|
44
58
|
end
|
45
59
|
end
|
60
|
+
|
61
|
+
delete '/admin/edit/:id/?' do
|
62
|
+
@post = Post.find(params[:id])
|
63
|
+
@post.destroy
|
64
|
+
redirect '/admin'
|
65
|
+
end
|
46
66
|
end
|
47
67
|
end
|
48
68
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'packr'
|
2
|
+
|
3
|
+
|
4
|
+
module Schnitzelpress
|
5
|
+
class JavascriptPacker
|
6
|
+
def self.pack_javascripts!(files)
|
7
|
+
plain = files.map do |filename|
|
8
|
+
File.read(File.expand_path("../lib/assets/js/#{filename}", settings.root))
|
9
|
+
end.join("\n")
|
10
|
+
|
11
|
+
Packr.pack(plain)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module Actions
|
16
|
+
module Assets
|
17
|
+
extend ActiveSupport::Concern
|
18
|
+
|
19
|
+
ASSET_TIMESTAMP = Time.now.to_i
|
20
|
+
JAVASCRIPT_ASSETS = ['jquery-1.7.1.js', 'jquery.cookie.js', 'schnitzelpress.js', 'jquery-ujs.js']
|
21
|
+
|
22
|
+
included do
|
23
|
+
get '/assets/schnitzelpress.:timestamp.css' do
|
24
|
+
cache_control :public, :max_age => 1.year.to_i
|
25
|
+
scss :blog
|
26
|
+
end
|
27
|
+
|
28
|
+
get '/assets/schnitzelpress.:timestamp.js' do
|
29
|
+
cache_control :public, :max_age => 1.year.to_i
|
30
|
+
content_type 'text/javascript; charset=utf-8'
|
31
|
+
JavascriptPacker.pack_javascripts!(JAVASCRIPT_ASSETS)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,18 +1,29 @@
|
|
1
1
|
require 'omniauth'
|
2
2
|
require 'omniauth-browserid'
|
3
3
|
|
4
|
-
module
|
4
|
+
module Schnitzelpress
|
5
5
|
module Actions
|
6
6
|
module Auth
|
7
7
|
extend ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
use OmniAuth::
|
10
|
+
use OmniAuth::Builder do
|
11
|
+
provider :browser_id
|
12
|
+
if Schnitzelpress.env.development?
|
13
|
+
provider :developer , :fields => [:email], :uid_field => :email
|
14
|
+
end
|
15
|
+
end
|
11
16
|
|
12
17
|
post '/auth/:provider/callback' do
|
13
18
|
auth = request.env['omniauth.auth']
|
14
19
|
session[:auth] = {:provider => auth['provider'], :uid => auth['uid']}
|
15
|
-
|
20
|
+
|
21
|
+
if admin_logged_in?
|
22
|
+
response.set_cookie('show_admin', :value => true, :path => '/')
|
23
|
+
redirect '/admin/'
|
24
|
+
else
|
25
|
+
redirect '/'
|
26
|
+
end
|
16
27
|
end
|
17
28
|
|
18
29
|
get '/login' do
|
@@ -21,6 +32,8 @@ module SchnitzelPress
|
|
21
32
|
|
22
33
|
get '/logout' do
|
23
34
|
session[:auth] = nil
|
35
|
+
response.delete_cookie('show_admin')
|
36
|
+
|
24
37
|
redirect '/login'
|
25
38
|
end
|
26
39
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
module
|
1
|
+
module Schnitzelpress
|
2
2
|
module Actions
|
3
3
|
module Blog
|
4
4
|
extend ActiveSupport::Concern
|
@@ -31,25 +31,21 @@ module SchnitzelPress
|
|
31
31
|
render_posts
|
32
32
|
end
|
33
33
|
|
34
|
-
get '/blog.css' do
|
35
|
-
cache_for 1.hour
|
36
|
-
scss :blog
|
37
|
-
end
|
38
|
-
|
39
34
|
# /posts.atom is now deprecated.
|
40
35
|
get '/posts.atom' do
|
41
36
|
redirect '/blog.atom', 301
|
42
37
|
end
|
43
38
|
|
44
39
|
get '/blog.atom' do
|
45
|
-
|
40
|
+
cache_control :public, :must_revalidate, :s_maxage => 2, :max_age => 3.minutes.to_i
|
41
|
+
|
46
42
|
@posts = Post.latest.limit(10)
|
47
43
|
content_type 'application/atom+xml; charset=utf-8'
|
48
44
|
haml :atom, :format => :xhtml, :layout => false
|
49
45
|
end
|
50
46
|
|
51
47
|
get '/feed/?' do
|
52
|
-
redirect
|
48
|
+
redirect config.blog_feed_url, 307
|
53
49
|
end
|
54
50
|
|
55
51
|
get %r{^/(\d{4})/(\d{1,2})/(\d{1,2})/?$} do
|
@@ -90,10 +86,12 @@ module SchnitzelPress
|
|
90
86
|
if enforce_canonical_url && request.path != url_for(@post)
|
91
87
|
redirect url_for(@post)
|
92
88
|
else
|
93
|
-
fresh_when :last_modified => @post.updated_at,
|
94
|
-
|
89
|
+
fresh_when :last_modified => @post.updated_at,
|
90
|
+
:etag => CacheControl.etag(@post.updated_at)
|
95
91
|
|
96
92
|
@show_description = @post.home_page?
|
93
|
+
|
94
|
+
cache_control :public, :must_revalidate, :s_maxage => 2, :max_age => 60
|
97
95
|
haml :post
|
98
96
|
end
|
99
97
|
else
|
@@ -104,10 +102,10 @@ module SchnitzelPress
|
|
104
102
|
def render_posts
|
105
103
|
if freshest_post = @posts.where(:updated_at.ne => nil).desc(:updated_at).first
|
106
104
|
fresh_when :last_modified => freshest_post.updated_at,
|
107
|
-
:etag => freshest_post.
|
108
|
-
cache_for 60
|
105
|
+
:etag => CacheControl.etag(freshest_post.updated_at)
|
109
106
|
end
|
110
107
|
|
108
|
+
cache_control :public, :must_revalidate, :s_maxage => 2, :max_age => 60
|
111
109
|
haml :index
|
112
110
|
end
|
113
111
|
end
|
data/lib/schnitzelpress/app.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
-
|
1
|
+
require "sinatra/content_for"
|
2
|
+
|
3
|
+
module Schnitzelpress
|
2
4
|
class App < Sinatra::Base
|
3
5
|
STATIC_PATHS = ["/favicon.ico", "/img", "/js"]
|
4
6
|
|
5
7
|
set :views, ['./views/', File.expand_path('../../views/', __FILE__)]
|
6
8
|
set :public_folder, File.expand_path('../../public/', __FILE__)
|
7
9
|
|
10
|
+
use Rack::Cache if Schnitzelpress.env.production?
|
8
11
|
use Rack::ShowExceptions
|
9
12
|
use Rack::StaticCache,
|
10
13
|
:urls => STATIC_PATHS,
|
@@ -12,31 +15,25 @@ module SchnitzelPress
|
|
12
15
|
use Rack::MethodOverride
|
13
16
|
use Rack::Session::Cookie
|
14
17
|
|
15
|
-
helpers
|
18
|
+
helpers Sinatra::ContentFor
|
19
|
+
helpers Schnitzelpress::Helpers
|
16
20
|
include Rack::Utils
|
17
|
-
include
|
18
|
-
include
|
19
|
-
include
|
21
|
+
include Schnitzelpress::Actions::Auth
|
22
|
+
include Schnitzelpress::Actions::Assets
|
23
|
+
include Schnitzelpress::Actions::Admin
|
24
|
+
include Schnitzelpress::Actions::Blog
|
20
25
|
|
21
26
|
configure do
|
22
|
-
set :blog_title, "My SchnitzelPress Blog"
|
23
|
-
set :blog_description, ""
|
24
|
-
set :author_name, "Author"
|
25
|
-
set :disqus_name, nil
|
26
|
-
set :google_analytics_id, nil
|
27
|
-
set :gauges_id, nil
|
28
|
-
set :read_more, "Read Complete Article"
|
29
|
-
set :twitter_id, nil
|
30
|
-
set :footer, ""
|
31
|
-
set :administrator, nil
|
32
|
-
set :feed_url, '/blog.atom'
|
33
|
-
|
34
27
|
disable :protection
|
35
28
|
set :logging, true
|
36
29
|
end
|
37
30
|
|
38
|
-
|
39
|
-
|
31
|
+
before do
|
32
|
+
# Reload configuration before every request. I know this isn't ideal,
|
33
|
+
# but right now it's the easiest way to get the configuration in synch
|
34
|
+
# across multiple instances of the app.
|
35
|
+
#
|
36
|
+
Config.instance.reload
|
40
37
|
end
|
41
38
|
|
42
39
|
def fresh_when(options)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Schnitzelpress
|
2
|
+
module CacheControl
|
3
|
+
class << self
|
4
|
+
def timestamp
|
5
|
+
Schnitzelpress::Config.get 'cache_timestamp'
|
6
|
+
end
|
7
|
+
|
8
|
+
def bust!
|
9
|
+
Schnitzelpress::Config.set 'cache_timestamp', Time.now
|
10
|
+
end
|
11
|
+
|
12
|
+
def etag(*args)
|
13
|
+
Digest::MD5.hexdigest("-#{timestamp.to_i}-#{args.join '-'}-")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/schnitzelpress/cli.rb
CHANGED
@@ -1,27 +1,58 @@
|
|
1
1
|
require "thor"
|
2
2
|
|
3
|
-
module
|
3
|
+
module Schnitzelpress
|
4
4
|
class Cli < Thor
|
5
5
|
include Thor::Actions
|
6
6
|
|
7
7
|
source_root(File.expand_path('../../templates', __FILE__))
|
8
8
|
|
9
|
-
desc "create NAME", "Creates a new
|
10
|
-
|
11
|
-
method_option :git, :aliases => "-g", :default => false,
|
9
|
+
desc "create NAME", "Creates a new Schnitzelpress blog."
|
10
|
+
method_option :git, :aliases => "-g", :default => false, :type => :boolean,
|
12
11
|
:desc => "Initialize a git repository in your blog's directory."
|
13
12
|
|
14
|
-
method_option :bundle, :aliases => "-b", :default => false,
|
15
|
-
:desc => "Run 'bundle install' after generating your new blog."
|
16
|
-
|
17
13
|
def create(name)
|
18
14
|
@name = name
|
19
15
|
self.destination_root = name
|
20
16
|
directory 'new_blog', '.'
|
21
17
|
|
22
18
|
in_root do
|
23
|
-
|
24
|
-
|
19
|
+
if options[:git]
|
20
|
+
run "git init"
|
21
|
+
run "git add ."
|
22
|
+
run "git commit -m 'Created new Schnitzelpress blog'"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "update", "Update your blog's bundled Schnitzelpress version."
|
28
|
+
def update
|
29
|
+
run "bundle update schnitzelpress"
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "console", "Run the Schnitzelpress console."
|
33
|
+
def console
|
34
|
+
require 'schnitzelpress'
|
35
|
+
require 'pry'
|
36
|
+
Schnitzelpress.init!
|
37
|
+
ARGV.clear
|
38
|
+
pry Schnitzelpress
|
39
|
+
end
|
40
|
+
|
41
|
+
desc "mongo_pull", "Pulls contents of remote MongoDB into your local MongoDB"
|
42
|
+
def mongo_pull
|
43
|
+
if uri = YAML.load_file('./config/mongo.yml')['development']['uri']
|
44
|
+
system "MONGO_URL=\"#{uri}\" heroku mongo:pull"
|
45
|
+
else
|
46
|
+
abort "URI is missing :("
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "mongo_push", "Pushes contents of your local MongoDB to remote MongoDB"
|
51
|
+
def mongo_push
|
52
|
+
if uri = YAML.load_file('./config/mongo.yml')['development']['uri']
|
53
|
+
system "MONGO_URL=\"#{uri}\" heroku mongo:push"
|
54
|
+
else
|
55
|
+
abort "URI is missing :("
|
25
56
|
end
|
26
57
|
end
|
27
58
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Schnitzelpress
|
2
|
+
class Config
|
3
|
+
include Mongoid::Document
|
4
|
+
include Mongoid::Timestamps
|
5
|
+
identity :type => String
|
6
|
+
store_in :config
|
7
|
+
|
8
|
+
field :blog_title, :type => String, :default => "A New Schnitzelpress Blog"
|
9
|
+
field :blog_description, :type => String, :default => ""
|
10
|
+
field :blog_footer, :type => String, :default => "powered by [Schnitzelpress](http://schnitzelpress.org)"
|
11
|
+
field :blog_feed_url, :type => String, :default => "/blog.atom"
|
12
|
+
|
13
|
+
field :author_name, :type => String, :default => "Joe Schnitzel"
|
14
|
+
|
15
|
+
field :disqus_id, :type => String
|
16
|
+
field :google_analytics_id, :type => String
|
17
|
+
field :gauges_id, :type => String
|
18
|
+
field :twitter_id, :type => String
|
19
|
+
|
20
|
+
field :cache_timestamp, :type => DateTime
|
21
|
+
|
22
|
+
validates :blog_title, :author_name, :presence => true
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def instance
|
26
|
+
@@instance ||= find_or_create_by(:id => 'schnitzelpress')
|
27
|
+
end
|
28
|
+
|
29
|
+
def forget_instance
|
30
|
+
@@instance = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def get(k)
|
34
|
+
instance.send(k)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set(k, v)
|
38
|
+
instance.update_attributes!(k => v)
|
39
|
+
v
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|