schreihals 0.0.2 → 0.0.3

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.
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