schnitzelpress 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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