schreihals 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/.gitignore +1 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +9 -0
  4. data/.watchr +6 -5
  5. data/LICENSE +2 -2
  6. data/README.md +2 -54
  7. data/Rakefile +4 -9
  8. data/lib/schreihals.rb +10 -9
  9. data/lib/schreihals/actions/admin.rb +49 -0
  10. data/lib/schreihals/actions/auth.rb +29 -0
  11. data/lib/schreihals/actions/blog.rb +68 -0
  12. data/lib/schreihals/app.rb +15 -43
  13. data/lib/schreihals/cli.rb +0 -14
  14. data/lib/schreihals/helpers.rb +38 -5
  15. data/lib/schreihals/post.rb +126 -44
  16. data/lib/schreihals/rake.rb +21 -0
  17. data/lib/schreihals/version.rb +1 -1
  18. data/lib/templates/new_blog/Gemfile +17 -1
  19. data/lib/templates/new_blog/Rakefile +2 -0
  20. data/lib/templates/new_blog/app.rb.tt +28 -0
  21. data/lib/templates/new_blog/config.ru.tt +2 -14
  22. data/{example/public/media → lib/templates/new_blog/public}/.gitkeep +0 -0
  23. data/lib/views/admin/admin.haml +12 -0
  24. data/lib/views/admin/edit.haml +3 -0
  25. data/lib/views/admin/new.haml +3 -0
  26. data/lib/views/atom.haml +12 -10
  27. data/lib/views/index.haml +3 -0
  28. data/lib/views/layout.haml +4 -3
  29. data/lib/views/partials/_admin_post_list.haml +11 -0
  30. data/lib/views/partials/_form_field.haml +27 -0
  31. data/lib/views/partials/_post.haml +16 -12
  32. data/lib/views/partials/_post_form.haml +12 -0
  33. data/lib/views/schreihals.scss +61 -25
  34. data/schreihals.gemspec +27 -11
  35. data/spec/app_spec.rb +56 -0
  36. data/spec/factories.rb +15 -0
  37. data/spec/post_spec.rb +60 -0
  38. data/spec/spec_helper.rb +35 -0
  39. data/{test → test.old}/app_test.rb +0 -0
  40. data/{test → test.old}/files/simple_document.md +0 -0
  41. data/test.old/post_test.rb +62 -0
  42. data/{test → test.old}/posts/2011-12-23-first-post.md +0 -0
  43. data/{test → test.old}/posts/2011-12-24-second-post.md +0 -0
  44. data/{test → test.old}/posts/static-page.md +0 -0
  45. data/{test → test.old}/test_helper.rb +0 -0
  46. metadata +158 -87
  47. data/example/Gemfile +0 -5
  48. data/example/config.ru +0 -8
  49. data/example/posts/2010-06-17-pythton-is-great.md +0 -9
  50. data/example/posts/2011-12-22-ruby-is-great.md +0 -16
  51. data/lib/schreihals/actions.rb +0 -45
  52. data/lib/schreihals/document.rb +0 -52
  53. data/lib/templates/first-post.md.tt +0 -1
  54. data/lib/templates/new-post.md.tt +0 -13
  55. data/lib/templates/new_blog/posts/.gitkeep +0 -0
  56. data/lib/templates/new_blog/public/media/.gitkeep +0 -0
  57. data/test/document_test.rb +0 -41
data/.gitignore CHANGED
@@ -6,7 +6,6 @@
6
6
  .rbfu-version
7
7
  .powenv
8
8
  .sass-cache
9
- Gemfile.lock
10
9
  InstalledFiles
11
10
  _yardoc
12
11
  coverage
@@ -19,3 +18,4 @@ test/tmp
19
18
  test/version_tmp
20
19
  tmp
21
20
  *.sublime*
21
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -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("test/.*_test\.rb") { |m| run("bundle exec ruby %s" % m[0]) }
8
- watch("lib/schreihals/(.*)\.rb") { |m| run("bundle exec ruby test/%s_test.rb" % m[1]) }
9
- watch('^test/test_helper\.rb') { run "rake test" }
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 test") }
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) 2011 Hendrik Mans
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
- Everybody should be developing their own blogging engine for hackers, so here's mine.
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
- ## Overall Design Goals
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 riot
5
- require 'rake/testtask'
6
- Rake::TestTask.new(:test) do |test|
7
- test.libs << 'lib' << 'test'
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
@@ -6,23 +6,24 @@ require 'sass'
6
6
  require 'redcarpet'
7
7
  require 'schnitzelstyle'
8
8
  require 'rack-cache'
9
- require 'coderay'
10
- require 'rack/codehighlighter'
11
- require 'yaml'
9
+ require 'mongoid'
12
10
 
13
- require 'active_support/core_ext/string/inflections'
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
@@ -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::Codehighlighter, :coderay, :markdown => true, :element => "pre>code", :pattern => /\A:::(\w+)\s*\n/
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 refresh_documents_now?
31
- !Post.documents.any?
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
@@ -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
@@ -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 = "#{settings.blog_url}#{url}" if options[:absolute]
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