schnitzelpress 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGES.md +20 -3
  2. data/README.md +4 -2
  3. data/bin/schnitzelpress +1 -1
  4. data/lib/assets/js/jquery-1.7.1.js +673 -0
  5. data/lib/assets/js/jquery-ujs.js +373 -0
  6. data/lib/assets/js/jquery.cookie.js +47 -0
  7. data/lib/{public → assets}/js/schnitzelpress.js +7 -2
  8. data/lib/public/font/fontawesome-webfont.eot +0 -0
  9. data/lib/public/font/fontawesome-webfont.svg +175 -0
  10. data/lib/public/font/fontawesome-webfont.svgz +0 -0
  11. data/lib/public/font/fontawesome-webfont.ttf +0 -0
  12. data/lib/public/font/fontawesome-webfont.woff +0 -0
  13. data/lib/schnitzelpress.rb +21 -5
  14. data/lib/schnitzelpress/actions/admin.rb +21 -1
  15. data/lib/schnitzelpress/actions/assets.rb +36 -0
  16. data/lib/schnitzelpress/actions/auth.rb +16 -3
  17. data/lib/schnitzelpress/actions/blog.rb +10 -12
  18. data/lib/schnitzelpress/app.rb +16 -19
  19. data/lib/schnitzelpress/cache_control.rb +17 -0
  20. data/lib/schnitzelpress/cli.rb +40 -9
  21. data/lib/schnitzelpress/config.rb +43 -0
  22. data/lib/schnitzelpress/env.rb +5 -0
  23. data/lib/schnitzelpress/helpers.rb +168 -4
  24. data/lib/schnitzelpress/markdown_renderer.rb +2 -2
  25. data/lib/schnitzelpress/post.rb +2 -6
  26. data/lib/schnitzelpress/static.rb +1 -1
  27. data/lib/schnitzelpress/version.rb +2 -2
  28. data/lib/templates/new_blog/Gemfile +4 -5
  29. data/lib/templates/new_blog/Gemfile.lock.tt +137 -0
  30. data/lib/templates/new_blog/Procfile +1 -1
  31. data/lib/templates/new_blog/config.ru.tt +6 -17
  32. data/lib/views/admin/admin.haml +3 -2
  33. data/lib/views/admin/config.haml +27 -0
  34. data/lib/views/atom.haml +3 -3
  35. data/lib/views/index.haml +1 -1
  36. data/lib/views/layout.haml +15 -11
  37. data/lib/views/login.haml +4 -0
  38. data/lib/views/partials/_admin_post_list.haml +8 -8
  39. data/lib/views/partials/_disqus.haml +1 -1
  40. data/lib/views/partials/_gauges.haml +1 -1
  41. data/lib/views/partials/_google_analytics.haml +1 -1
  42. data/lib/views/partials/_post.haml +6 -5
  43. data/lib/views/partials/_post_form.haml +3 -1
  44. data/lib/views/post.haml +4 -1
  45. data/lib/views/schnitzelpress.scss +67 -3
  46. data/schnitzelpress.gemspec +5 -2
  47. data/spec/app_spec.rb +5 -11
  48. data/spec/assets_spec.rb +26 -0
  49. data/spec/factories.rb +1 -1
  50. data/spec/post_spec.rb +9 -2
  51. data/spec/spec_helper.rb +2 -1
  52. metadata +115 -71
  53. data/lib/schnitzelpress/rake.rb +0 -20
  54. data/lib/templates/new_blog/Rakefile +0 -2
  55. data/lib/templates/new_blog/app.rb.tt +0 -34
  56. data/lib/templates/new_blog/config/unicorn.rb +0 -5
  57. data/lib/templates/new_blog/public/.gitkeep +0 -0
@@ -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 SchnitzelPress
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
- SchnitzelPress::Post.create_indexes
41
+ Schnitzelpress::Post.create_indexes
37
42
  @@mongo_uri = uri
38
43
  end
39
44
 
40
- def env
41
- (ENV['RACK_ENV'] || 'development').inquiry
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(SchnitzelPress::MarkdownRenderer,
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 SchnitzelPress
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 SchnitzelPress
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::Strategies::BrowserID
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
- redirect admin_logged_in? ? '/admin/' : '/'
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 SchnitzelPress
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
- cache_for 3.minutes
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 settings.feed_url, 307
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, :etag => @post.to_etag
94
- cache_for 60
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.to_etag
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
@@ -1,10 +1,13 @@
1
- module SchnitzelPress
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 SchnitzelPress::Helpers
18
+ helpers Sinatra::ContentFor
19
+ helpers Schnitzelpress::Helpers
16
20
  include Rack::Utils
17
- include SchnitzelPress::Actions::Auth
18
- include SchnitzelPress::Actions::Admin
19
- include SchnitzelPress::Actions::Blog
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
- def cache_for(time)
39
- cache_control :public, :must_revalidate, :s_maxage => 2, :max_age => time.to_i
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
@@ -1,27 +1,58 @@
1
1
  require "thor"
2
2
 
3
- module SchnitzelPress
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 SchnitzelPress blog."
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
- run "bundle" if options[:bundle]
24
- run "git init" if options[:git]
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
@@ -0,0 +1,5 @@
1
+ module Schnitzelpress
2
+ def self.env
3
+ (ENV['RACK_ENV'] || 'development').inquiry
4
+ end
5
+ end