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
@@ -0,0 +1,27 @@
1
+ - set_page_title "Configuration"
2
+
3
+ %section
4
+ %form.post{:action => '/admin/config', :method => 'post'}
5
+ %h2 Blog
6
+ = form_field config, :blog_title
7
+ = form_field config, :blog_description
8
+ = form_field config, :blog_footer
9
+
10
+ %h2 Author
11
+ = form_field config, :author_name
12
+
13
+ %h2 External Services
14
+ = form_field config, :blog_feed_url, :label => "ATOM Feed URL", :hint => "This is what the /feed URL will redirect to. You can change this to eg. your FeedBurner URL."
15
+ .row
16
+ .six.columns
17
+ = form_field config, :disqus_id, :label => "Disqus shortname"
18
+ .six.columns
19
+ = form_field config, :twitter_id, :label => "Twitter user name"
20
+ .row
21
+ .six.columns
22
+ = form_field config, :google_analytics_id, :label => "Google Analytics ID"
23
+ .six.columns
24
+ = form_field config, :gauges_id, :label => "Gauges ID"
25
+
26
+ .buttons
27
+ %input{:type => 'submit', :value => 'Update Configuration'}
data/lib/views/atom.haml CHANGED
@@ -1,13 +1,13 @@
1
1
  !!! XML
2
2
  %feed{:xmlns => 'http://www.w3.org/2005/Atom'}
3
- %title= settings.blog_title
3
+ %title= config.blog_title
4
4
  %link{:href => url_for('/', :absolute => true)}
5
5
  %id= base_url
6
6
  - if @posts.any?
7
7
  %updated= @posts.first.published_at
8
8
 
9
9
  %author
10
- %name= settings.author_name
10
+ %name= config.author_name
11
11
 
12
12
  - @posts.each do |post|
13
13
  %entry
@@ -17,7 +17,7 @@
17
17
  %published= post.published_at
18
18
  %updated= post.published_at
19
19
  %author
20
- %name= settings.author_name
20
+ %name= config.author_name
21
21
  %content{:type => 'html'}
22
22
  :cdata
23
23
  #{post.to_html}
data/lib/views/index.haml CHANGED
@@ -5,7 +5,7 @@
5
5
  - else
6
6
  #welcome
7
7
  :redcarpet
8
- **Congratulations on setting up your new SchnitzelPress blog!**
8
+ **Congratulations on setting up your new Schnitzelpress blog!**
9
9
  A world of hackery and adventure awaits.
10
10
  <span class="heart">♥</span>
11
11
 
@@ -1,28 +1,32 @@
1
1
  !!! 5
2
2
  %html
3
3
  %head
4
- %title= [@page_title, settings.blog_title].compact.join(" | ")
4
+ %title= [@page_title, config.blog_title].compact.join(" | ")
5
5
  %meta{ :"http-equiv" => "content-type", :content => "text/html; charset=UTF-8" }
6
6
  %meta{ :name => "viewport", :content => "width=device-width, initial-scale=1.0" }
7
- %link{ :href => '/blog.css', :media => "screen", :rel => "stylesheet", :type => "text/css" }
8
- %link{ :href => settings.feed_url, :title => "Subscribe via Atom Feed", :rel => 'alternate', :type => 'application/atom+xml' }
9
- %script{ :type => 'text/javascript', :src => '//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js' }
10
- %script{ :type => 'text/javascript', :src => '//browserid.org/include.js' }
11
- %script{ :type => 'text/javascript', :src => '/js/schnitzelpress.js' }
7
+ %link{ :href => "/assets/schnitzelpress.#{ASSET_TIMESTAMP}.css", :media => "screen", :rel => "stylesheet", :type => "text/css" }
8
+ %link{ :href => config.blog_feed_url, :title => "Subscribe via Atom Feed", :rel => 'alternate', :type => 'application/atom+xml' }
12
9
  %body
13
10
  .container
14
11
  %header
15
12
  .site-title
16
- %a{:href => '/'}= settings.blog_title
13
+ %a{:href => '/'}= h config.blog_title
17
14
  - if @show_description
18
- ~ markdown settings.blog_description
15
+ ~ markdown config.blog_description
16
+
17
+ #actions.admin_only
18
+ = yield_content :actions
19
+ %a{:href => '/admin/'} #{icon 'cog'} Go To Admin
20
+ %a{:href => '/logout'} #{icon 'signout'} Logout
19
21
 
20
22
  = yield
21
23
 
22
24
  %footer
23
- ~ markdown settings.footer
25
+ ~ markdown config.blog_footer
24
26
 
25
- - if production? && settings.google_analytics_id.present?
27
+ - if production? && config.google_analytics_id.present?
26
28
  = partial 'google_analytics'
27
- - if production? && settings.gauges_id.present?
29
+ - if production? && config.gauges_id.present?
28
30
  = partial 'gauges'
31
+
32
+ %script{ :type => 'text/javascript', :src => "/assets/schnitzelpress.#{ASSET_TIMESTAMP}.js" }
data/lib/views/login.haml CHANGED
@@ -6,6 +6,10 @@
6
6
 
7
7
  %p
8
8
  %a.button#browser_id{:href => '/auth/browser_id'} Log in with BrowserID
9
+ - if Schnitzelpress.env.development?
10
+ %a.green.button{:href => '/auth/developer'} Developer Login
9
11
 
10
12
  %form{:method => 'post', :action => '/auth/browser_id/callback', :noValidate => 'noValidate'}
11
13
  %input{:type => 'hidden', :name => 'assertion'}
14
+
15
+ %script{ :type => 'text/javascript', :src => '//browserid.org/include.js' }
@@ -1,11 +1,11 @@
1
1
  - if posts.any?
2
2
  %h2= title
3
- %ul.admin-post-list
3
+ .admin-post-list
4
4
  - posts.each do |post|
5
- %ul.admin
6
- %li
7
- %a.button{:href => "/admin/edit/#{post.id}"} edit
8
- %li
9
- %a.button{:href => url_for(post)} view
10
- %li
11
- = h (post.title || ("%s..." % post.body.first(50)))
5
+ .row
6
+ .ten.columns
7
+ %a{:href => "/admin/edit/#{post.id}"}= h (post.title || ("%s..." % post.body.first(50)))
8
+ .two.columns.icons
9
+ %a{:href => url_for(post)}= icon 'eye-open'
10
+ %a{:href => "/admin/edit/#{post.id}"}= icon 'edit'
11
+ = link_to_delete_post icon('trash'), post
@@ -2,7 +2,7 @@
2
2
 
3
3
  :javascript
4
4
  /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
5
- var disqus_shortname = '#{settings.disqus_name}';
5
+ var disqus_shortname = '#{config.disqus_id}';
6
6
  var disqus_developer = #{production? ? 0 : 1};
7
7
  var disqus_identifier = '#{disqus_identifier}';
8
8
 
@@ -5,7 +5,7 @@
5
5
  t.type = 'text/javascript';
6
6
  t.async = true;
7
7
  t.id = 'gauges-tracker';
8
- t.setAttribute('data-site-id', '#{settings.gauges_id}');
8
+ t.setAttribute('data-site-id', '#{config.gauges_id}');
9
9
  t.src = '//secure.gaug.es/track.js';
10
10
  var s = document.getElementsByTagName('script')[0];
11
11
  s.parentNode.insertBefore(t, s);
@@ -1,6 +1,6 @@
1
1
  :javascript
2
2
  var _gaq = _gaq || [];
3
- _gaq.push(['_setAccount', '#{settings.google_analytics_id}']);
3
+ _gaq.push(['_setAccount', '#{config.google_analytics_id}']);
4
4
  _gaq.push(['_trackPageview']);
5
5
 
6
6
  (function() {
@@ -4,13 +4,13 @@
4
4
  - show_body ||= complete || !show_summary
5
5
  - show_read_more ||= !complete && post.summary.present?
6
6
  - show_permalink ||= true
7
- - show_twitter ||= complete && post.post? && settings.twitter_id.present?
7
+ - show_twitter ||= complete && post.post? && config.twitter_id.present?
8
8
 
9
9
  %article.post{:class => [post.status, post.link_post? ? 'link' : 'article']}
10
10
  %header
11
11
  - if show_title
12
12
  %h1
13
- %a{:href => post.link || post.to_url}= h post.title
13
+ %a.instapaper_title{:href => post.link || post.to_url}= h post.title
14
14
  - if post.link_post?
15
15
  %span.link-arrow ➝
16
16
 
@@ -19,11 +19,12 @@
19
19
  ~ markdown post.summary
20
20
  - if show_read_more
21
21
  %p
22
- %a{:href => url_for(post)}= post.read_more.presence || settings.read_more
22
+ %a{:href => url_for(post)}= post.read_more.presence || "Read Complete Article"
23
23
 
24
24
 
25
25
  - if show_body
26
- ~ post.to_html
26
+ .instapaper_body
27
+ ~ post.to_html
27
28
 
28
29
  %footer
29
30
  - if show_permalink
@@ -33,6 +34,6 @@
33
34
  - if show_twitter
34
35
  .social_media_buttons
35
36
  - if show_twitter
36
- %a{:href => "https://twitter.com/share", :class => "twitter-share-button", :data => {:via => settings.twitter_id}}
37
+ %a{:href => "https://twitter.com/share", :class => "twitter-share-button", :data => {:via => config.twitter_id}}
37
38
  :javascript
38
39
  !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");
@@ -17,4 +17,6 @@
17
17
  = form_field @post, :disqus
18
18
 
19
19
  .buttons
20
- %input{:type => 'submit', :value => @post.new_record? ? 'Create Post' : 'Update Post' }
20
+ %button.green{:type => 'submit'} #{icon 'ok'} #{@post.new_record? ? 'Create Post' : 'Update Post'}
21
+ or
22
+ = link_to_delete_post "delete this post", @post
data/lib/views/post.haml CHANGED
@@ -5,7 +5,7 @@
5
5
  - if @extra_posts
6
6
  %section.extra_posts
7
7
  %h1
8
- %a{href: '/blog'}= @extra_posts.first
8
+ %a{:href => '/blog'}= @extra_posts.first
9
9
  %ul
10
10
  - for post in @extra_posts.second
11
11
  %li
@@ -14,3 +14,6 @@
14
14
  - if show_disqus? && @post.disqus?
15
15
  %section.disqus
16
16
  = partial 'disqus', :disqus_identifier => @post.disqus_identifier
17
+
18
+ - content_for :actions do
19
+ %a{:href => "/admin/edit/%s" % @post.id} #{icon 'edit'} Edit
@@ -8,6 +8,34 @@ body, .container {
8
8
  background-image: url(/img/background.png);
9
9
  }
10
10
 
11
+ .admin_only {
12
+ display: none;
13
+ body.show_admin & { display: block; }
14
+ }
15
+
16
+ /* action panel */
17
+ #actions {
18
+ @include small-type;
19
+ margin: 5px;
20
+
21
+ a {
22
+ display: inline-block;
23
+ padding: 3px 6px;
24
+ background-color: $color-text;
25
+ color: $color-background;
26
+ font-weight: bold;
27
+ text-decoration: none;
28
+ border-radius: 3px;
29
+ @include appear-on-hover;
30
+ }
31
+
32
+ @media only screen and (min-width: 641px) {
33
+ position: fixed;
34
+ top: 0px;
35
+ right: 0px;
36
+ }
37
+ }
38
+
11
39
  /* posts */
12
40
  article.post {
13
41
  header {
@@ -31,8 +59,7 @@ article.post {
31
59
  @include animated;
32
60
 
33
61
  margin: 2em 0;
34
- opacity: 0.3;
35
- &:hover { opacity: 1 }
62
+ @include appear-on-hover;
36
63
  }
37
64
  }
38
65
  }
@@ -52,12 +79,35 @@ ul.admin {
52
79
  }
53
80
  }
54
81
 
55
- ul.admin-post-list {
82
+ div.admin-post-list {
56
83
  padding: 5px 0;
84
+ margin: 1em 0;
57
85
  max-height: 300px;
58
86
  overflow: auto;
59
87
  border-bottom: 1px dotted rgba($color-text, 0.5);
60
88
  border-top: 1px dotted rgba($color-text, 0.5);
89
+
90
+ .icons {
91
+ display: none;
92
+ text-align: right;
93
+ a {
94
+ color: $color-footer;
95
+ &:hover { color: $color-text };
96
+ }
97
+ }
98
+ .row {
99
+ padding: 3px;
100
+ margin-bottom: 0;
101
+ &:hover {
102
+ background-color: rgba($color-text, 0.05);
103
+ .icons {
104
+ display: block;
105
+ }
106
+ }
107
+ }
108
+ a {
109
+ border: none;
110
+ }
61
111
  }
62
112
 
63
113
  // forms
@@ -87,3 +137,17 @@ section.posts #welcome {
87
137
  @include clearfix;
88
138
  }
89
139
  }
140
+
141
+ // font-awesome
142
+ @font-face {
143
+ font-family: 'FontAwesome';
144
+ src: url('/font/fontawesome-webfont.eot');
145
+ src: url('/font/fontawesome-webfont.eot?#iefix') format('embedded-opentype'), url('/font/fontawesome-webfont.woff') format('woff'), url('/font/fontawesome-webfont.ttf') format('truetype'), url('/font/fontawesome-webfont.svgz#FontAwesomeRegular') format('svg'), url('/font/fontawesome-webfont.svg#FontAwesomeRegular') format('svg');
146
+ font-weight: normal;
147
+ font-style: normal;
148
+ }
149
+ .font-awesome {
150
+ font-family: FontAwesome;
151
+ font-weight: normal;
152
+ font-style: normal;
153
+ }
@@ -13,12 +13,14 @@ Gem::Specification.new do |gem|
13
13
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
14
  gem.name = "schnitzelpress"
15
15
  gem.require_paths = ["lib"]
16
- gem.version = SchnitzelPress::VERSION
16
+ gem.version = Schnitzelpress::VERSION
17
17
 
18
18
  # base dependencies
19
19
  gem.add_dependency 'rack', '~> 1.4.1'
20
20
  gem.add_dependency 'rack-contrib', '~> 1.1.0'
21
+ gem.add_dependency 'rack-cache', '~> 1.1.0'
21
22
  gem.add_dependency 'sinatra', '~> 1.3.2'
23
+ gem.add_dependency 'sinatra-contrib', '~> 1.3.1'
22
24
  gem.add_dependency 'activesupport', '~> 3.2.0'
23
25
 
24
26
  # database related
@@ -35,10 +37,11 @@ Gem::Specification.new do |gem|
35
37
  gem.add_dependency 'sass', '~> 3.1.15'
36
38
  gem.add_dependency 'redcarpet', '~> 2.1.0'
37
39
  gem.add_dependency 'coderay', '~> 1.0.5'
38
- gem.add_dependency 'schnitzelstyle', '~> 0.1.0'
40
+ gem.add_dependency 'schnitzelstyle', '~> 0.1.1'
39
41
  gem.add_dependency 'i18n', '~> 0.6.0'
40
42
  gem.add_dependency 'tilt', '~> 1.3.0'
41
43
  gem.add_dependency 'ruby-oembed', '~> 0.8.5'
44
+ gem.add_dependency 'packr', '~> 3.1.1'
42
45
 
43
46
  # CLI related
44
47
  gem.add_dependency 'thor', '~> 0.14.6'
data/spec/app_spec.rb CHANGED
@@ -1,16 +1,10 @@
1
1
  require 'spec_helper'
2
2
 
3
- class TestApp < SchnitzelPress::App
4
- configure do
5
- set :blog_title, "A Test Blog"
6
- end
7
- end
8
-
9
- describe SchnitzelPress::App do
3
+ describe Schnitzelpress::App do
10
4
  include Rack::Test::Methods
11
5
 
12
6
  def app
13
- TestApp
7
+ Schnitzelpress::App
14
8
  end
15
9
 
16
10
  describe 'the home page' do
@@ -23,7 +17,7 @@ describe SchnitzelPress::App do
23
17
  subject { last_response }
24
18
 
25
19
  it { should be_ok }
26
- its(:body) { should have_tag 'title', :text => "A Test Blog" }
20
+ its(:body) { should have_tag 'title', :text => Schnitzelpress::Config.instance.blog_title }
27
21
  its(:body) { should have_tag 'section.posts > article.post.published', :count => 5 }
28
22
  its(:body) { should_not have_tag 'section.posts > article.post.draft' }
29
23
  end
@@ -36,14 +30,14 @@ describe SchnitzelPress::App do
36
30
 
37
31
  describe 'the public feed url' do
38
32
  before do
39
- TestApp.set :feed_url, 'http://feeds.feedburner.com/example_org'
33
+ Schnitzelpress::Config.instance.blog_feed_url = 'http://feeds.feedburner.com/example_org'
40
34
  get '/feed'
41
35
  end
42
36
 
43
37
  subject { last_response }
44
38
  it { should be_redirect }
45
39
  its(:status) { should == 307 }
46
- specify { subject["Location"].should == 'http://feeds.feedburner.com/example_org' }
40
+ specify { subject["Location"].should == 'http://example.org/blog.atom' }
47
41
  end
48
42
 
49
43
  describe 'viewing a single post' do
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Schnitzelpress::Actions::Assets' do
4
+ include Rack::Test::Methods
5
+
6
+ def app
7
+ Schnitzelpress::App
8
+ end
9
+
10
+ describe '/assets/schnitzelpress.*.js' do
11
+ before do
12
+ Schnitzelpress::JavascriptPacker.should_receive(:pack_javascripts!).and_return('{123}')
13
+ get '/assets/schnitzelpress.123.js'
14
+ end
15
+ subject { last_response }
16
+ it { should be_ok }
17
+ its(:body) { should == '{123}' }
18
+ end
19
+
20
+ describe '/assets/schnitzelpress.*.css' do
21
+ before { get '/assets/schnitzelpress.123.css' }
22
+ subject { last_response }
23
+ it { should be_ok }
24
+ its(:content_type) { should == 'text/css;charset=utf-8' }
25
+ end
26
+ end
data/spec/factories.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  FactoryGirl.define do
2
- factory :post, :class => SchnitzelPress::Post do
2
+ factory :post, :class => Schnitzelpress::Post do
3
3
  title { Faker::Lorem.sentence }
4
4
  body { Faker::Lorem.paragraphs }
5
5
  published_at { Time.now }
data/spec/post_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe SchnitzelPress::Post do
3
+ describe Schnitzelpress::Post do
4
4
  subject do
5
5
  Factory.build(:post)
6
6
  end
@@ -81,7 +81,7 @@ describe SchnitzelPress::Post do
81
81
  it 'should return the latest published posts' do
82
82
  2.times { Factory :draft_post }
83
83
  5.times { Factory :published_post }
84
- SchnitzelPress::Post.latest.size.should == 5
84
+ Schnitzelpress::Post.latest.size.should == 5
85
85
  end
86
86
  end
87
87
 
@@ -91,4 +91,11 @@ describe SchnitzelPress::Post do
91
91
  its(:month) { should == 01 }
92
92
  its(:day) { should == 02 }
93
93
  end
94
+
95
+ context 'to_url' do
96
+ it 'should produce double-digit months and days' do
97
+ @post = Factory.build(:post, :published_at => '2012-1-1 12:00:00', :slug => 'test')
98
+ @post.to_url.should == '/2012/01/01/test/'
99
+ end
100
+ end
94
101
  end