postmarkdown 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/.gitignore +5 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +3 -0
  5. data/Rakefile +24 -0
  6. data/app/controllers/application_controller.rb +2 -0
  7. data/app/controllers/posts_controller.rb +31 -0
  8. data/app/models/post.rb +149 -0
  9. data/app/views/layouts/postmarkdown.html.haml +16 -0
  10. data/app/views/posts/_feed_link.html.haml +2 -0
  11. data/app/views/posts/_post.html.haml +15 -0
  12. data/app/views/posts/feed.xml.builder +29 -0
  13. data/app/views/posts/index.html.haml +8 -0
  14. data/app/views/posts/show.html.haml +3 -0
  15. data/lib/generators/postmarkdown/install_generator.rb +19 -0
  16. data/lib/generators/postmarkdown/override_generator.rb +44 -0
  17. data/lib/generators/postmarkdown/post_generator.rb +33 -0
  18. data/lib/generators/postmarkdown/templates/example-post.markdown +67 -0
  19. data/lib/generators/postmarkdown/usage/install.txt +10 -0
  20. data/lib/generators/postmarkdown/usage/override.txt +9 -0
  21. data/lib/generators/postmarkdown/usage/post.txt +14 -0
  22. data/lib/postmarkdown.rb +5 -0
  23. data/lib/postmarkdown/config.rb +22 -0
  24. data/lib/postmarkdown/engine.rb +12 -0
  25. data/lib/postmarkdown/railtie.rb +18 -0
  26. data/lib/postmarkdown/routes.rb +29 -0
  27. data/lib/postmarkdown/util.rb +12 -0
  28. data/lib/postmarkdown/version.rb +3 -0
  29. data/postmarkdown.gemspec +35 -0
  30. data/public/stylesheets/postmarkdown/postmarkdown.css +67 -0
  31. data/readme.md +143 -0
  32. data/spec/acceptance/posts_spec.rb +167 -0
  33. data/spec/models/posts_spec.rb +72 -0
  34. data/spec/routing/posts_routing_spec.rb +80 -0
  35. data/spec/spec_helper.rb +12 -0
  36. data/spec/support/data/posts/2011-04-01-first-post.markdown +3 -0
  37. data/spec/support/data/posts/2011-04-28-image.markdown +3 -0
  38. data/spec/support/data/posts/2011-04-28-summary.markdown +8 -0
  39. data/spec/support/data/posts/2011-05-01-full-metadata.markdown +10 -0
  40. data/spec/support/data/posts/2011-05-02-md-file-extension.md +1 -0
  41. data/spec/support/data/posts/2011-05-02-mdown-file-extension.mdown +1 -0
  42. data/spec/support/data/posts/2011-05-02-mkd-file-extension.mkd +1 -0
  43. data/spec/support/data/posts/2015-02-13-custom-title.markdown +5 -0
  44. data/spec/support/data/posts/missing-date-from-filename.markdown +1 -0
  45. data/spec/support/rails_app/.gitignore +4 -0
  46. data/spec/support/rails_app/README +256 -0
  47. data/spec/support/rails_app/Rakefile +7 -0
  48. data/spec/support/rails_app/app/controllers/application_controller.rb +3 -0
  49. data/spec/support/rails_app/app/helpers/application_helper.rb +2 -0
  50. data/spec/support/rails_app/app/posts/2011-04-01-first-post.markdown +3 -0
  51. data/spec/support/rails_app/app/posts/2011-04-28-image.markdown +3 -0
  52. data/spec/support/rails_app/app/posts/2011-04-28-summary.markdown +8 -0
  53. data/spec/support/rails_app/app/posts/2011-05-01-full-metadata.markdown +13 -0
  54. data/spec/support/rails_app/app/posts/2015-02-13-custom-title.markdown +5 -0
  55. data/spec/support/rails_app/app/views/layouts/application.html.erb +14 -0
  56. data/spec/support/rails_app/config.ru +4 -0
  57. data/spec/support/rails_app/config/application.rb +42 -0
  58. data/spec/support/rails_app/config/boot.rb +10 -0
  59. data/spec/support/rails_app/config/database.yml +22 -0
  60. data/spec/support/rails_app/config/environment.rb +5 -0
  61. data/spec/support/rails_app/config/environments/development.rb +26 -0
  62. data/spec/support/rails_app/config/environments/production.rb +49 -0
  63. data/spec/support/rails_app/config/environments/test.rb +35 -0
  64. data/spec/support/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  65. data/spec/support/rails_app/config/initializers/inflections.rb +10 -0
  66. data/spec/support/rails_app/config/initializers/mime_types.rb +5 -0
  67. data/spec/support/rails_app/config/initializers/secret_token.rb +7 -0
  68. data/spec/support/rails_app/config/initializers/session_store.rb +8 -0
  69. data/spec/support/rails_app/config/locales/en.yml +5 -0
  70. data/spec/support/rails_app/config/routes.rb +3 -0
  71. data/spec/support/rails_app/db/seeds.rb +7 -0
  72. data/spec/support/rails_app/doc/README_FOR_APP +2 -0
  73. data/spec/support/rails_app/lib/tasks/.gitkeep +0 -0
  74. data/spec/support/rails_app/public/404.html +26 -0
  75. data/spec/support/rails_app/public/422.html +26 -0
  76. data/spec/support/rails_app/public/500.html +26 -0
  77. data/spec/support/rails_app/public/favicon.ico +0 -0
  78. data/spec/support/rails_app/public/images/rails.png +0 -0
  79. data/spec/support/rails_app/public/index.html +239 -0
  80. data/spec/support/rails_app/public/javascripts/application.js +2 -0
  81. data/spec/support/rails_app/public/javascripts/controls.js +965 -0
  82. data/spec/support/rails_app/public/javascripts/dragdrop.js +974 -0
  83. data/spec/support/rails_app/public/javascripts/effects.js +1123 -0
  84. data/spec/support/rails_app/public/javascripts/prototype.js +6001 -0
  85. data/spec/support/rails_app/public/javascripts/rails.js +191 -0
  86. data/spec/support/rails_app/public/robots.txt +5 -0
  87. data/spec/support/rails_app/public/stylesheets/.gitkeep +0 -0
  88. data/spec/support/rails_app/script/rails +6 -0
  89. data/spec/support/rails_app/test/performance/browsing_test.rb +9 -0
  90. data/spec/support/rails_app/test/test_helper.rb +13 -0
  91. data/spec/support/rails_app/vendor/plugins/.gitkeep +0 -0
  92. data/todo.txt +25 -0
  93. metadata +315 -0
@@ -0,0 +1,5 @@
1
+ .bundle
2
+ Gemfile.lock
3
+ db/*.sqlite3
4
+ log/*.log
5
+ tmp/
data/.rspec ADDED
@@ -0,0 +1 @@
1
+
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create ruby-1.9.2-p180@postmarkdown
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ require 'rake'
9
+ require 'rake/rdoctask'
10
+
11
+ require 'rspec/core'
12
+ require 'rspec/core/rake_task'
13
+
14
+ RSpec::Core::RakeTask.new(:spec)
15
+
16
+ task :default => :spec
17
+
18
+ Rake::RDocTask.new(:rdoc) do |rdoc|
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = 'ContactForm'
21
+ rdoc.options << '--line-numbers' << '--inline-source'
22
+ rdoc.rdoc_files.include('README.rdoc')
23
+ rdoc.rdoc_files.include('lib/**/*.rb')
24
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,31 @@
1
+ class PostsController < ApplicationController
2
+ layout lambda { |controller| Postmarkdown::Config.options[:use_theme] ? 'postmarkdown' : 'application' }
3
+
4
+ def show
5
+ resource
6
+ end
7
+
8
+ def feed
9
+ max_age = 4.hours
10
+ response.headers['Cache-Control'] = 'public, max-age=' + max_age.to_i.to_s
11
+ response.headers['Expires'] = max_age.from_now.httpdate
12
+ response.content_type = 'application/atom+xml'
13
+ fresh_when :last_modified => Post.feed_last_modified
14
+ end
15
+
16
+ protected
17
+
18
+ def resource
19
+ @resource ||= Post.find(params[:id])
20
+ end
21
+ helper_method :resource
22
+
23
+ def collection
24
+ @collection ||= begin
25
+ posts = Post.where(params.slice(:year, :month, :day))
26
+ posts = Kaminari.paginate_array(posts).page(params[:page]).per(10)
27
+ posts
28
+ end
29
+ end
30
+ helper_method :collection
31
+ end
@@ -0,0 +1,149 @@
1
+ class Post
2
+ extend ActiveModel::Naming
3
+
4
+ include Gravtastic
5
+ is_gravtastic
6
+
7
+ attr_reader :slug
8
+
9
+ FILENAME_FORMAT = /^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
10
+
11
+ def initialize(path)
12
+ @path = path
13
+ @date_str, @slug = File.basename(path).match(FILENAME_FORMAT).captures
14
+ end
15
+
16
+ def to_param
17
+ case permalink_format
18
+ when :day then "%04d/%02d/%02d/%s" % [year, month, day, slug]
19
+ when :month then "%04d/%02d/%s" % [year, month, slug]
20
+ when :year then "%04d/%s" % [year, slug]
21
+ when :slug then slug
22
+ end
23
+ end
24
+
25
+ def permalink_format
26
+ Postmarkdown::Config.options[:permalink_format]
27
+ end
28
+
29
+ def to_key
30
+ [slug]
31
+ end
32
+
33
+ def metadata
34
+ load_content
35
+ @metadata
36
+ end
37
+
38
+ def content
39
+ load_content
40
+ @content
41
+ end
42
+
43
+ def title
44
+ metadata[:title] || slug.titleize
45
+ end
46
+
47
+ def author
48
+ metadata[:author]
49
+ end
50
+
51
+ def email
52
+ metadata[:email]
53
+ end
54
+
55
+ def date
56
+ @date ||= Time.zone.parse(metadata[:date] || @date_str).to_date
57
+ end
58
+
59
+ delegate :year, :month, :day, :to => :date
60
+
61
+ def timestamp
62
+ date.to_time_in_current_zone
63
+ end
64
+ alias_method :last_modified, :timestamp
65
+
66
+ def visible?
67
+ timestamp <= Time.zone.now
68
+ end
69
+
70
+ def to_s
71
+ "#{title.inspect} (#{slug})"
72
+ end
73
+
74
+ def content_html
75
+ RDiscount.new(content).to_html.html_safe
76
+ end
77
+
78
+ class Sanitizer < HTML::WhiteListSanitizer
79
+ self.allowed_tags -= %w(img a)
80
+ end
81
+
82
+ TagHelper = Class.new.extend ActionView::Helpers::TagHelper
83
+
84
+ def summary_html
85
+ if metadata[:summary].present?
86
+ TagHelper.content_tag :p, metadata[:summary]
87
+ else
88
+ html = Sanitizer.new.sanitize(content_html)
89
+ doc = Nokogiri::HTML.fragment(html)
90
+ para = doc.search('p').detect { |p| p.text.present? }
91
+ para.try(:to_html).try(:html_safe)
92
+ end
93
+ end
94
+
95
+ class << self
96
+ def all
97
+ file_extensions = Postmarkdown::Config.options[:markdown_file_extensions].join(',')
98
+ @@posts ||= Dir.glob(Rails.root + "app/posts/*.{#{file_extensions}}").map do |filename|
99
+ Post.new filename
100
+ end.select(&:visible?).sort_by(&:date).reverse
101
+ end
102
+
103
+ def where(conditions = {})
104
+ conditions = conditions.symbolize_keys
105
+ conditions.assert_valid_keys :year, :month, :day, :slug, :to_param
106
+ [:year, :month, :day].each do |key|
107
+ conditions[key] = conditions[key].to_i if conditions[key].present?
108
+ end
109
+ all.select do |post|
110
+ conditions.all? { |key, value| post.send(key) == value }
111
+ end
112
+ end
113
+
114
+ def find(id)
115
+ where(:to_param => id).first or raise ActiveRecord::RecordNotFound, "Could not find post with ID #{id.inspect}"
116
+ end
117
+
118
+ def first
119
+ all.first
120
+ end
121
+
122
+ def last
123
+ all.last
124
+ end
125
+
126
+ def feed
127
+ all.first(10)
128
+ end
129
+
130
+ def feed_last_modified
131
+ feed.first.try(:last_modified) || Time.now.utc
132
+ end
133
+
134
+ def reset!
135
+ @@posts = nil
136
+ end
137
+ end
138
+
139
+ protected
140
+
141
+ def load_content
142
+ @content = File.read(@path)
143
+ if @content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
144
+ @content = @content[($1.size + $2.size)..-1]
145
+ @metadata = YAML.load($1)
146
+ end
147
+ @metadata = (@metadata || {}).with_indifferent_access
148
+ end
149
+ end
@@ -0,0 +1,16 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %title= Postmarkdown::Util.app_name
5
+ = stylesheet_link_tag 'postmarkdown/postmarkdown'
6
+ = yield :head
7
+ %body
8
+ %header#header
9
+ %h1= link_to Postmarkdown::Util.app_name, '/'
10
+ %p
11
+ A #{link_to 'postmarkdown', 'https://github.com/getencode/postmarkdown'} blog
12
+ - if Post.last.try(:author)
13
+ = "by #{Post.last.author}"
14
+ %section#content= yield
15
+ %footer
16
+ %small= "Copyright &copy; #{Date.today.year}".html_safe
@@ -0,0 +1,2 @@
1
+ - content_for :head do
2
+ %link{:href => posts_feed_path, :type => 'application/atom+xml', :rel => :alternate, :title => Postmarkdown::Config.options[:feed_title]}
@@ -0,0 +1,15 @@
1
+ = content_tag_for :article, post do
2
+ %header
3
+ %h1= link_to post.title, post
4
+ = image_tag post.gravatar_url(:size => 32, :default => 'identicon'), :height => 32, :width => 32
5
+ .meta
6
+ %time{:pubdate => 'true', :datetime => post.timestamp.iso8601}
7
+ Posted on #{post.date.strftime '%e %B %Y'}
8
+ %span
9
+ by
10
+ = post.author
11
+ - if summary
12
+ = post.summary_html
13
+ %p.more= link_to 'Read more&hellip;'.html_safe, post
14
+ - else
15
+ ~ post.content_html
@@ -0,0 +1,29 @@
1
+ xml.instruct!
2
+ xml.feed :xmlns => 'http://www.w3.org/2005/Atom' do
3
+ xml.title Postmarkdown::Config.options[:feed_title]
4
+ xml.link :href => posts_feed_url, :rel => :self, :type => 'application/atom+xml'
5
+ xml.link :href => posts_url, :rel => :alternate, :type => 'text/html'
6
+ xml.id posts_url
7
+ xml.updated Post.feed_last_modified.xmlschema
8
+
9
+ Post.feed.each do |post|
10
+ xml.entry do
11
+ xml.title post.title, :type => :text
12
+ xml.link :href => post_url(post), :rel => :alternate, :type => 'text/html'
13
+ xml.published post.timestamp.xmlschema
14
+ xml.updated post.last_modified.xmlschema
15
+
16
+ if post.author.present?
17
+ xml.author do
18
+ xml.name post.author
19
+ xml.email post.email if post.email.present?
20
+ end
21
+ end
22
+
23
+ xml.id post_url(post)
24
+ xml.content :type => :html, 'xml:base' => post_url(post) do
25
+ xml.cdata! post.content_html
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ = render 'feed_link'
2
+
3
+ %section#posts
4
+ - if collection.present?
5
+ = render collection, :summary => true
6
+ = paginate
7
+ - else
8
+ No posts found.
@@ -0,0 +1,3 @@
1
+ = render 'feed_link'
2
+
3
+ = render resource, :summary => false
@@ -0,0 +1,19 @@
1
+ module Postmarkdown
2
+ class InstallGenerator < Rails::Generators::Base
3
+ desc File.read(File.expand_path('../usage/install.txt', __FILE__)).gsub('{{CURRENT_DATE}}', Time.zone.now.strftime('%Y-%m-%d'))
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ class_option :skip_example, :type => :boolean, :group => :runtime, :desc => 'Skip generating an example post'
6
+
7
+ def create_directory
8
+ empty_directory 'app/posts'
9
+ end
10
+
11
+ def generate_example_post
12
+ generate 'postmarkdown:post', 'example-post' unless options.skip_example?
13
+ end
14
+
15
+ def add_routes
16
+ insert_into_file 'config/routes.rb', " postmarkdown :as => :posts\n\n", :after => "::Application.routes.draw do\n"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ module Postmarkdown
2
+ class OverrideGenerator < Rails::Generators::Base
3
+ desc File.read(File.expand_path('../usage/override.txt', __FILE__))
4
+ source_root File.expand_path('../../../../app', __FILE__)
5
+
6
+ class_option :all, :type => :boolean, :group => :override, :desc => 'Override all of the things'
7
+ class_option :views, :type => :boolean, :group => :override, :desc => 'Override the Post views'
8
+ class_option :model, :type => :boolean, :group => :override, :desc => 'Override the Post model'
9
+ class_option :controller, :type => :boolean, :group => :override, :desc => 'Override the Posts controller'
10
+ class_option :theme, :type => :boolean, :group => :override, :desc => 'Override the layout and stylesheet'
11
+
12
+ def check_class_options
13
+ if options.blank?
14
+ exec 'rails g postmarkdown:override --help'
15
+ exit
16
+ end
17
+ end
18
+
19
+ def override_views
20
+ if options.views || options.all
21
+ directory 'views/posts', 'app/views/posts'
22
+ end
23
+ end
24
+
25
+ def override_model
26
+ if options.model || options.all
27
+ copy_file 'models/post.rb', 'app/models/post.rb'
28
+ end
29
+ end
30
+
31
+ def override_controller
32
+ if options.controller || options.all
33
+ copy_file 'controllers/posts_controller.rb', 'app/controllers/posts_controller.rb'
34
+ end
35
+ end
36
+
37
+ def override_theme
38
+ if options.theme || options.all
39
+ directory 'views/layouts', 'views/layouts'
40
+ directory '../public/stylesheets', 'public/stylesheets'
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ module Postmarkdown
2
+ class PostGenerator < Rails::Generators::Base
3
+ desc File.read(File.expand_path('../usage/post.txt', __FILE__)).gsub('{{CURRENT_DATE}}', Time.zone.now.strftime('%Y-%m-%d'))
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ argument :slug, :type => :string, :required => true
6
+ class_option :date, :type => :string, :group => :runtime, :desc => 'Publish date for the post'
7
+
8
+ def check_slug
9
+ unless slug =~ /^[A-Za-z0-9\-]+$/
10
+ puts 'Invalid slug - valid characters include letters, digits and dashes.'
11
+ exit
12
+ end
13
+ end
14
+
15
+ def check_date
16
+ if options.date && options.date !~ /^\d{4}-\d{2}-\d{2}$/
17
+ puts 'Invalid date - please use the following format: YYYY-MM-DD, eg. 2011-01-01.'
18
+ exit
19
+ end
20
+ end
21
+
22
+ def generate_post
23
+ template 'example-post.markdown', "app/posts/#{publish_date}-#{slug.downcase}.markdown"
24
+ end
25
+
26
+ private
27
+
28
+ def publish_date
29
+ date = options.date.present? ? Time.zone.parse(options.date) : Time.zone.now
30
+ date.strftime('%Y-%m-%d')
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,67 @@
1
+ ---
2
+ title: <%= slug.underscore.humanize %>
3
+ <%- if author = Postmarkdown::Util.git_config('user.name') -%>
4
+ author: <%= author %>
5
+ <%- end -%>
6
+ <%- if email = Postmarkdown::Util.git_config('user.email') -%>
7
+ email: <%= email %>
8
+ <%- end -%>
9
+ ---
10
+
11
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
12
+
13
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
14
+
15
+ # Markdown Formatting
16
+
17
+ [Reference](http://daringfireball.net/projects/markdown/syntax)
18
+
19
+ ---
20
+
21
+ # Headers
22
+
23
+ ## Header 2
24
+
25
+ ### Header 3
26
+
27
+ ---
28
+
29
+ # Emphasis
30
+
31
+ *italic* **bold**
32
+ _italic_ __bold__
33
+
34
+ ---
35
+
36
+ # Links and Images
37
+
38
+ An [example](http://url.com/ "Title") Reference-style labels (titles are optional):
39
+
40
+ ![Placeholder Kitten](http://placekitten.com/200/300)
41
+
42
+ ---
43
+
44
+ # Lists
45
+
46
+ 1. Foo
47
+ 2. Bar
48
+
49
+ Unordered, with nesting:
50
+
51
+ * Abacus
52
+ * answer
53
+ * Bubbles
54
+ 1. bunk
55
+ 2. bupkis
56
+ * BELITTLER
57
+ 3. burper
58
+ * Cunning
59
+
60
+ ---
61
+
62
+ # Preformatted Code Blocks
63
+
64
+ Indent every line of a code block by at least 4 spaces or 1 tab.
65
+
66
+ This is a preformatted
67
+ code block.