schreihals 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -18,3 +18,4 @@ spec/reports
18
18
  test/tmp
19
19
  test/version_tmp
20
20
  tmp
21
+ *.sublime*
data/.watchr ADDED
@@ -0,0 +1,14 @@
1
+ def run(cmd)
2
+ puts "=== %s" % cmd
3
+ system cmd
4
+ puts "\n"
5
+ end
6
+
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" }
10
+
11
+ # Ctrl-\
12
+ Signal.trap('QUIT') { run("rake test") }
13
+ # Ctrl-C
14
+ Signal.trap('INT') { abort("\nQuitting.") }
data/README.md CHANGED
@@ -29,12 +29,24 @@ Just a list of keywords I need to write about:
29
29
  * pages (= posts without dates)
30
30
  * deployment (Heroku!)
31
31
  * code highlighting
32
- * `google_analytics_id`
32
+ * `google_analytics_id` and `gauges_id`
33
+ * `footer`
33
34
  * `link`
34
35
  * `read_more`
36
+ * `schreihals create NAME`
37
+ * `schreihals post TITLE`
38
+ * `documents_store`, `documents_source`
39
+ * `twitter_id`
40
+ * cascading views
35
41
 
36
42
  ## Version History
37
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
+
38
50
  ### 0.0.2
39
51
 
40
52
  * Fix various stupid bugs from the initial release, including the broken example app. Duh!
data/Rakefile CHANGED
@@ -1,7 +1,12 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
3
 
4
- # integrate rspec
5
- require 'rspec/core/rake_task'
6
- RSpec::Core::RakeTask.new('spec')
7
- task :default => :spec
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
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ require "rubygems"
3
+ require "schreihals/cli"
4
+
5
+ Schreihals::Cli.start
@@ -3,7 +3,3 @@ source 'https://rubygems.org'
3
3
  # In a real-world schreihals blog, remove the :path option, obviously.
4
4
  #
5
5
  gem 'schreihals', :path => '../'
6
-
7
- # For the time being, we need this fork of document_mapper.
8
- #
9
- gem 'document_mapper', :git => 'git://github.com/hmans/document_mapper.git', :branch => "after_load_hook"
@@ -1,155 +1,28 @@
1
1
  require 'schreihals/version'
2
+
2
3
  require 'sinatra'
3
4
  require 'haml'
4
5
  require 'sass'
5
6
  require 'redcarpet'
6
7
  require 'schnitzelstyle'
7
- require 'document_mapper'
8
8
  require 'rack-cache'
9
9
  require 'coderay'
10
10
  require 'rack/codehighlighter'
11
+ require 'yaml'
11
12
 
12
13
  require 'active_support/core_ext/string/inflections'
14
+ require 'active_support/core_ext/class'
15
+ require 'active_support/concern'
13
16
 
14
- module Schreihals
15
- class Post
16
- include DocumentMapper::Document
17
-
18
- def after_load
19
- # Set some defaults
20
- #
21
- self.attributes = {
22
- disqus: true,
23
- status: 'published',
24
- summary: nil,
25
- link: nil
26
- }.merge(attributes)
27
-
28
- # Set slug
29
- #
30
- if !attributes.has_key? :slug
31
- begin
32
- match = attributes[:file_name_without_extension].match(/(\d{4}-\d{1,2}-\d{1,2}[-_])?(.*)/)
33
- attributes[:slug] = match[2]
34
- rescue NoMethodError => err
35
- end
36
- end
37
- end
38
-
39
- def to_url
40
- date.present? ? "/#{year}/#{month}/#{day}/#{slug}/" : "/#{slug}/"
41
- end
42
-
43
- def disqus_identifier
44
- attributes[:disqus_identifier] || file_name_without_extension
45
- end
46
-
47
- def disqus?
48
- disqus && published?
49
- end
50
-
51
- def published?
52
- status == 'published'
53
- end
54
-
55
- def post?
56
- date.present?
57
- end
58
-
59
- def page?
60
- !post?
61
- end
62
-
63
- # load all posts.
64
- self.directory = 'posts'
65
- end
66
-
67
- class App < Sinatra::Application
68
- set :blog_title, "My Schreihals Blog"
69
- set :blog_url, ""
70
- set :blog_description, ""
71
- set :author_name, "Author"
72
- set :disqus_name, nil
73
- set :google_analytics_id, nil
74
- set :read_more, "Read Complete Article"
75
-
76
- use Rack::ShowExceptions
77
- use Rack::Cache
78
- use Rack::Static, :urls => ["/media"], :root => "public"
79
- use Rack::Codehighlighter, :coderay, :markdown => true, :element => "pre>code", :pattern => /\A:::(\w+)\s*\n/
80
-
81
- helpers do
82
- def partial(thing, locals = {})
83
- name = case thing
84
- when String then thing
85
- else thing.class.to_s.demodulize.underscore
86
- end
17
+ require 'schreihals/static'
18
+ require 'schreihals/helpers'
19
+ require 'schreihals/document'
20
+ require 'schreihals/post'
21
+ require 'schreihals/actions'
22
+ require 'schreihals/app'
87
23
 
88
- haml :"partials/_#{name}", :locals => { name.to_sym => thing }.merge(locals)
89
- end
24
+ Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("../views", __FILE__))
25
+ Sass::Engine::DEFAULT_OPTIONS[:load_paths].unshift(File.expand_path("./views"))
90
26
 
91
- def set_page_title(title)
92
- @page_title = title
93
- end
94
-
95
- def link_to(title, thing)
96
- haml "%a{href: '#{url_for thing}'} #{title}"
97
- end
98
-
99
- def url_for(thing, options = {})
100
- url = thing.respond_to?(:to_url) ? thing.to_url : thing.to_s
101
- url = "#{settings.blog_url}#{url}" if options[:absolute]
102
- url
103
- end
104
-
105
- def show_disqus?
106
- settings.disqus_name.present?
107
- end
108
-
109
- def production?
110
- settings.environment.to_sym == :production
111
- end
112
- end
113
-
114
- before do
115
- cache_control :public, :must_revalidate, :max_age => 60
116
- end
117
-
118
- get '/' do
119
- @posts = Post.order_by(:date => :desc)
120
- @posts = @posts.where(:status => 'published') if production?
121
- @posts = @posts.limit(10).all
122
- haml :index
123
- end
124
-
125
- get '/schreihals.css' do
126
- scss :schreihals
127
- end
128
-
129
- get '/atom.xml' do
130
- @posts = Post.where(:status => 'published').order_by(:date => :desc).limit(10).all
131
- content_type 'application/xml+atom'
132
- haml :atom, :layout => false
133
- end
134
-
135
- get '/:year/:month/:day/:slug/?' do |year, month, day, slug|
136
- render_page(slug)
137
- end
138
-
139
- get '/:slug/?' do |slug|
140
- render_page(slug)
141
- end
142
-
143
- def render_page(slug)
144
- if @post = Post.where(:slug => slug).first
145
- haml :post
146
- else
147
- halt 404
148
- end
149
- end
150
-
151
- not_found do
152
- haml :"404"
153
- end
154
- end
27
+ module Schreihals
155
28
  end
@@ -0,0 +1,45 @@
1
+ module Schreihals
2
+ module Actions
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before do
7
+ refresh_documents! if refresh_documents_now?
8
+ cache_control :public, :must_revalidate, :max_age => 60
9
+ end
10
+
11
+ get '/' do
12
+ @posts = Post.latest(published_only: production?)
13
+
14
+ @show_description = true
15
+ haml :index
16
+ end
17
+
18
+ get '/blog.css' do
19
+ scss :blog
20
+ end
21
+
22
+ get '/atom.xml' do
23
+ @posts = Post.latest(published_only: production?)
24
+
25
+ xml = haml :atom, :layout => false
26
+
27
+ doc = Nokogiri::XML(xml)
28
+ doc.css("content img").each do |node|
29
+ node['src'] = absolutionize(node['src'])
30
+ end
31
+
32
+ content_type 'application/xml+atom'
33
+ doc.to_xml
34
+ end
35
+
36
+ get '/:year/:month/:day/:slug/?' do |year, month, day, slug|
37
+ render_page(slug)
38
+ end
39
+
40
+ get '/:slug/?' do |slug|
41
+ render_page(slug)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,73 @@
1
+ module Schreihals
2
+ class App < Sinatra::Base
3
+ set :views, ['./views/', File.expand_path('../../views/', __FILE__)]
4
+ set :public_folder, File.expand_path('../../public/', __FILE__)
5
+
6
+ use Schreihals::Static
7
+ use Rack::ShowExceptions
8
+ use Rack::Cache
9
+ use Rack::Codehighlighter, :coderay, :markdown => true, :element => "pre>code", :pattern => /\A:::(\w+)\s*\n/
10
+
11
+ helpers Schreihals::Helpers
12
+ include Schreihals::Actions
13
+
14
+ configure do
15
+ set :blog_title, "My Schreihals Blog"
16
+ set :blog_url, ""
17
+ set :blog_description, ""
18
+ set :author_name, "Author"
19
+ set :disqus_name, nil
20
+ set :google_analytics_id, nil
21
+ set :gauges_id, nil
22
+ set :read_more, "Read Complete Article"
23
+ set :documents_store, :filesystem
24
+ set :documents_source, './posts'
25
+ set :documents_cache, nil
26
+ set :twitter_id, nil
27
+ set :footer, ""
28
+ end
29
+
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']}"
67
+ end
68
+
69
+ not_found do
70
+ haml :"404"
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ require "thor"
2
+ require 'active_support/core_ext/string/inflections'
3
+
4
+ module Schreihals
5
+ class Cli < Thor
6
+ include Thor::Actions
7
+
8
+ source_root(File.expand_path('../../templates', __FILE__))
9
+
10
+ desc "create NAME", "Creates a new Schreihals blog."
11
+
12
+ method_option :git, :aliases => "-g", :default => false,
13
+ :desc => "Initialize a git repository in your blog's directory."
14
+
15
+ method_option :bundle, :aliases => "-b", :default => false,
16
+ :desc => "Run 'bundle install' after generating your new blog."
17
+
18
+ def create(name)
19
+ @name = name
20
+ self.destination_root = name
21
+ directory 'new_blog', '.'
22
+ post('My First Post')
23
+
24
+ in_root do
25
+ run "bundle" if options[:bundle]
26
+ run "git init" if options[:git]
27
+ end
28
+ 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
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ require 'tilt'
2
+
3
+ module Schreihals
4
+ class Document
5
+ cattr_accessor :documents
6
+ @@documents = []
7
+
8
+ attr_accessor :attributes
9
+
10
+ def initialize(attrs = {})
11
+ @attributes = attrs
12
+ @@documents << self
13
+ end
14
+
15
+ def file_extension
16
+ File.extname(file_name).sub(/^\./, '')
17
+ end
18
+
19
+ def file_name_without_extension
20
+ File.basename(file_name, '.'+file_extension)
21
+ end
22
+
23
+ def method_missing(name, *args)
24
+ attributes.has_key?(name.to_s) ? attributes[name.to_s] : super
25
+ end
26
+
27
+ def to_html
28
+ Tilt.new(file_extension) { body }.render
29
+ end
30
+
31
+ class << self
32
+ def from_string(s, attrs = {})
33
+ frontmatter, body = split_original_document(s)
34
+ new(Psych.load(frontmatter).
35
+ merge('body' => body.strip).
36
+ merge(attrs))
37
+ end
38
+
39
+ def from_file(name)
40
+ from_string(open(name).read, 'file_name' => File.basename(name))
41
+ end
42
+
43
+ def load_from_directory(dir)
44
+ Dir[File.join(dir, "*")].collect { |f| from_file f }
45
+ end
46
+
47
+ def split_original_document(s)
48
+ s =~ /.*(---\s*\n.*)\n---\s*\n(.*)/m ? [$1, $2] : [nil, s]
49
+ end
50
+ end
51
+ end
52
+ end