hightouch 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/Gemfile +12 -0
  2. data/Gemfile.lock +121 -0
  3. data/README.md +10 -0
  4. data/bin/hightouch +6 -0
  5. data/bin/ht +6 -0
  6. data/config.rb +26 -0
  7. data/lib/hightouch.rb +89 -0
  8. data/lib/hightouch/app_generator.rb +25 -0
  9. data/lib/hightouch/archive.rb +30 -0
  10. data/lib/hightouch/archive_page_generator.rb +10 -0
  11. data/lib/hightouch/blog.rb +40 -0
  12. data/lib/hightouch/blog_posting.rb +95 -0
  13. data/lib/hightouch/category.rb +7 -0
  14. data/lib/hightouch/cli.rb +10 -0
  15. data/lib/hightouch/tag.rb +7 -0
  16. data/lib/hightouch/version.rb +3 -0
  17. data/rvmrc.example +1 -0
  18. data/source/blog/2012/02/02/setup-your-rails-dev-env-with-rda.html.markdown +150 -0
  19. data/source/blog/2012/03/01/example.html.markdown +11 -0
  20. data/source/images/adventure/background.jpg +0 -0
  21. data/source/images/adventure/bars.png +0 -0
  22. data/source/images/adventure/blacktrans.png +0 -0
  23. data/source/images/adventure/blog_posting_bg.png +0 -0
  24. data/source/index.html.haml +2 -0
  25. data/source/javascripts/all.js +1 -0
  26. data/source/layouts/blog_postings.html.haml +31 -0
  27. data/source/layouts/layout.html.haml +31 -0
  28. data/source/partials/_blog_posting.html.haml +14 -0
  29. data/source/partials/_blog_posting_summary.html.haml +16 -0
  30. data/source/partials/_bottombar.html.haml +9 -0
  31. data/source/partials/_sidebar.html.haml +17 -0
  32. data/source/partials/_tags.html.haml +6 -0
  33. data/source/stylesheets/adventure.css.scss +198 -0
  34. data/source/stylesheets/coderay.css +136 -0
  35. data/source/stylesheets/default.css.scss +3 -0
  36. data/source/stylesheets/pygments-github.css +70 -0
  37. data/source/stylesheets/pygments.css +62 -0
  38. data/source/templates/archive.html.haml +4 -0
  39. data/source/templates/category.html.haml +5 -0
  40. data/source/templates/tag.html.haml +4 -0
  41. data/spec/spec_helper.rb +12 -0
  42. metadata +145 -0
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'middleman', git: 'https://github.com/middleman/middleman.git'
4
+ gem 'rygments'
5
+ gem 'rack-codehighlighter'
6
+ gem 'redcarpet'
7
+ gem 'virtus'
8
+
9
+ # Development dependencies
10
+ gem 'pry'
11
+ gem 'rspec'
12
+ gem 'factory_girl'
data/Gemfile.lock ADDED
@@ -0,0 +1,121 @@
1
+ GIT
2
+ remote: https://github.com/middleman/middleman.git
3
+ revision: 3c78d9f171e44806165bf2a5c710b376a23bcd7b
4
+ specs:
5
+ middleman (3.0.0.beta.2)
6
+ middleman-core (= 3.0.0.beta.2)
7
+ middleman-more (= 3.0.0.beta.2)
8
+ middleman-core (3.0.0.beta.2)
9
+ activesupport (~> 3.2.0)
10
+ i18n (~> 0.6.0)
11
+ listen (~> 0.3.3)
12
+ rack (~> 1.4.0)
13
+ rack-test (~> 0.6.1)
14
+ thin (~> 1.3.1)
15
+ thor (~> 0.14.0)
16
+ tilt (~> 1.3.1)
17
+ middleman-more (3.0.0.beta.2)
18
+ coffee-script (~> 2.2.0)
19
+ compass (~> 0.12.0)
20
+ execjs (~> 1.2)
21
+ haml (~> 3.1.0)
22
+ middleman-core (= 3.0.0.beta.2)
23
+ redcarpet (~> 2.1.0)
24
+ sass (>= 3.1.7)
25
+ sprockets (~> 2.1)
26
+ sprockets-sass (~> 0.7.0)
27
+ uglifier (~> 1.2.0)
28
+
29
+ GEM
30
+ remote: http://rubygems.org/
31
+ specs:
32
+ activesupport (3.2.2)
33
+ i18n (~> 0.6)
34
+ multi_json (~> 1.0)
35
+ chunky_png (1.2.5)
36
+ coderay (1.0.5)
37
+ coffee-script (2.2.0)
38
+ coffee-script-source
39
+ execjs
40
+ coffee-script-source (1.2.0)
41
+ compass (0.12.0)
42
+ chunky_png (~> 1.2)
43
+ fssm (>= 0.2.7)
44
+ sass (~> 3.1)
45
+ daemons (1.1.8)
46
+ diff-lcs (1.1.3)
47
+ eventmachine (0.12.10)
48
+ execjs (1.3.0)
49
+ multi_json (~> 1.0)
50
+ factory_girl (2.6.3)
51
+ activesupport (>= 2.3.9)
52
+ ffi (1.0.11)
53
+ fssm (0.2.8.1)
54
+ haml (3.1.4)
55
+ hike (1.2.1)
56
+ i18n (0.6.0)
57
+ listen (0.3.3)
58
+ rb-fchange (~> 0.0.5)
59
+ rb-fsevent (~> 0.9.0)
60
+ rb-inotify (~> 0.8.8)
61
+ method_source (0.7.1)
62
+ multi_json (1.1.0)
63
+ nokogiri (1.5.2)
64
+ pry (0.9.8.4)
65
+ coderay (~> 1.0.5)
66
+ method_source (~> 0.7.1)
67
+ slop (>= 2.4.4, < 3)
68
+ rack (1.4.1)
69
+ rack-codehighlighter (0.5.0)
70
+ nokogiri (>= 1.4.1)
71
+ rack (>= 1.0.0)
72
+ rack-test (0.6.1)
73
+ rack (>= 1.0)
74
+ rb-fchange (0.0.5)
75
+ ffi
76
+ rb-fsevent (0.9.0)
77
+ rb-inotify (0.8.8)
78
+ ffi (>= 0.5.0)
79
+ redcarpet (2.1.0)
80
+ rspec (2.8.0)
81
+ rspec-core (~> 2.8.0)
82
+ rspec-expectations (~> 2.8.0)
83
+ rspec-mocks (~> 2.8.0)
84
+ rspec-core (2.8.0)
85
+ rspec-expectations (2.8.0)
86
+ diff-lcs (~> 1.1.2)
87
+ rspec-mocks (2.8.0)
88
+ rygments (0.2.0)
89
+ sass (3.1.15)
90
+ slop (2.4.4)
91
+ sprockets (2.3.1)
92
+ hike (~> 1.2)
93
+ multi_json (~> 1.0)
94
+ rack (~> 1.0)
95
+ tilt (~> 1.1, != 1.3.0)
96
+ sprockets-sass (0.7.0)
97
+ sprockets (~> 2.0)
98
+ tilt (~> 1.1)
99
+ thin (1.3.1)
100
+ daemons (>= 1.0.9)
101
+ eventmachine (>= 0.12.6)
102
+ rack (>= 1.0.0)
103
+ thor (0.14.6)
104
+ tilt (1.3.3)
105
+ uglifier (1.2.3)
106
+ execjs (>= 0.3.0)
107
+ multi_json (>= 1.0.2)
108
+ virtus (0.3.0)
109
+
110
+ PLATFORMS
111
+ ruby
112
+
113
+ DEPENDENCIES
114
+ factory_girl
115
+ middleman!
116
+ pry
117
+ rack-codehighlighter
118
+ redcarpet
119
+ rspec
120
+ rygments
121
+ virtus
data/README.md ADDED
@@ -0,0 +1,10 @@
1
+ # Intro
2
+
3
+ hightouch (HT) is a static website generator based on
4
+ [middleman](https://github.com/middleman/middleman) and
5
+ inspired by
6
+ [middleman-blog](https://github.com/middleman/middleman-blog).
7
+
8
+ ### Start to write a blog posting
9
+
10
+
data/bin/hightouch ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../lib/hightouch.rb', File.dirname(__FILE__))
4
+
5
+ Hightouch::CLI.start
6
+
data/bin/ht ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../lib/hightouch.rb', File.dirname(__FILE__))
4
+
5
+ Hightouch::CLI.start
6
+
data/config.rb ADDED
@@ -0,0 +1,26 @@
1
+ Bundler.require
2
+ require 'i18n'
3
+ require 'hightouch'
4
+
5
+ activate :hightouch
6
+
7
+ Encoding.default_external = 'utf-8'
8
+
9
+ require "redcarpet"
10
+ set :markdown_engine, :redcarpet
11
+
12
+ require 'rygments'
13
+ require 'rack/codehighlighter'
14
+ page "/blog/*", layout: :blog_postings
15
+
16
+ use Rack::Codehighlighter,
17
+ :pygments,
18
+ element: 'code',
19
+ markdown: true,
20
+ pattern: /\A:::([-_+\w]+)\s*\n/
21
+
22
+ configure :build do; end
23
+
24
+ ready do
25
+ generate_archive_pages
26
+ end
data/lib/hightouch.rb ADDED
@@ -0,0 +1,89 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__))
2
+
3
+ require 'middleman'
4
+ require 'virtus'
5
+ require 'thor/group'
6
+
7
+ require 'hightouch/app_generator'
8
+ require 'hightouch/cli'
9
+ require 'hightouch/archive_page_generator'
10
+
11
+ module Hightouch
12
+ autoload :BlogPosting, 'hightouch/blog_posting'
13
+ autoload :Archive, 'hightouch/archive'
14
+ autoload :Category, 'hightouch/category'
15
+ autoload :Tag, 'hightouch/tag'
16
+ autoload :Blog, 'hightouch/blog'
17
+
18
+ class << self
19
+ def registered(app)
20
+ app.helpers HelperMethods
21
+ app.helpers ActiveSupport::Inflector
22
+
23
+ app.after_configuration do
24
+ frontmatter_changed /blog\/(\d{4})\/(\d{2})\/(\d{2})\/(.*)\.html/ do |file|
25
+ blog.touch_blog_posting(self.sitemap.page(self.sitemap.file_to_path(file)))
26
+ end
27
+ end
28
+ end
29
+
30
+ alias :included :registered
31
+ end
32
+
33
+ module HelperMethods
34
+ include ArchivePageGenerator
35
+
36
+ def blog
37
+ @blog ||= Blog.new(self)
38
+ end
39
+
40
+ def generate_archive_pages
41
+ [:categories, :tags, :archives].each do |i|
42
+ blog.send(i).each { |k, v| generate_archive_page(v) }
43
+ end
44
+ end
45
+
46
+ def font_size_for_tag(tag, opts = {})
47
+ max_font_size = opts[:max_font_size] || 36
48
+ min_font_size = opts[:min_font_size] || 11
49
+
50
+ max_count = max_tag.count
51
+ min_count = min_tag.count
52
+
53
+ size = begin
54
+ if max_count == min_count
55
+ min_font_size
56
+ else
57
+ min_font_size + (max_count - (max_count - (tag.count - min_count))) * (max_font_size - min_font_size).to_f/(max_count - min_count).to_f
58
+ end
59
+ end
60
+ end
61
+
62
+ private
63
+ def generate_category_page(category)
64
+ name = category.is_a?(String) ? category : category.name
65
+ page "/blog/#{name}.html", proxy: "/templates/category.html", ignore: true do
66
+ @category_name = name
67
+ end
68
+ end
69
+
70
+ def generate_tag_page(tag)
71
+ name = tag.is_a?(String) ? tag : tag.name
72
+ page "/blog/tags/#{name}.html", proxy: "/templates/tag.html", ignore: true do
73
+ @tag_name = name
74
+ end
75
+ end
76
+
77
+ def max_tag
78
+ blog.tags.values.max { |x, y| x.count <=> y.count }
79
+ end
80
+
81
+ def min_tag
82
+ blog.tags.values.min { |x, y| x.count <=> y.count }
83
+ end
84
+
85
+
86
+ end
87
+ end
88
+
89
+ Middleman::Extensions.register(:hightouch) { Hightouch }
@@ -0,0 +1,25 @@
1
+ module Hightouch
2
+ class AppGenerator < ::Thor::Group
3
+ include ::Thor::Actions
4
+
5
+ argument :path
6
+
7
+ def self.source_root
8
+ File.join(File.dirname(__FILE__), '../../')
9
+ end
10
+
11
+ def create_app_dir
12
+ empty_directory(path)
13
+ end
14
+
15
+ def create_files
16
+ directory 'source', "#{path}/source"
17
+
18
+ copy_file 'config.rb', "#{path}/config.rb"
19
+ copy_file 'Gemfile', "#{path}/Gemfile"
20
+ copy_file 'Gemfile.lock', "#{path}/Gemfile.lock"
21
+ copy_file 'README.md', "#{path}/README.md"
22
+ copy_file 'rvmrc.example', "#{path}/rvmrc.example"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module Hightouch
2
+ class Archive
3
+ include Virtus
4
+
5
+ attribute :name, String
6
+ attribute :blog, Blog
7
+ attribute :blog_postings, Hash, default: {}
8
+
9
+ def add_blog_posting(blog_posting)
10
+ blog_postings[blog_posting.name] = blog_posting
11
+ end
12
+
13
+ def remove_blog_posting(blog_posting)
14
+ blog_postings.delete(blog_posting.name)
15
+ blog.remove_archive(self) if empty?
16
+ end
17
+
18
+ def empty?
19
+ blog_postings.empty?
20
+ end
21
+
22
+ def count
23
+ blog_postings.size
24
+ end
25
+
26
+ def path
27
+ "/blog/#{name}/index.html"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ module Hightouch
2
+ module ArchivePageGenerator
3
+ def generate_archive_page(archive)
4
+ page archive.path, proxy: "/templates/archive.html", ignore: true do
5
+ @collection = archive.class.name.split(/::/).last.downcase.pluralize.to_sym
6
+ @archive_name = archive.name
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,40 @@
1
+ module Hightouch
2
+ class Blog
3
+ include Virtus
4
+
5
+ attribute :archives, Hash, default: {}
6
+ attribute :categories, Hash, default: {}
7
+ attribute :tags, Hash, default: {}
8
+ attribute :blog_postings, Hash, default: {}
9
+
10
+ attr_reader :app
11
+
12
+ def initialize(app = nil)
13
+ @app = app
14
+ end
15
+
16
+ def create_archive(type, attrs)
17
+ send(type.name.split(/::/).last.downcase.pluralize.to_sym)[attrs[:name]] = type.new(attrs)
18
+ end
19
+
20
+ def remove_archive(archive)
21
+ key = archive.name
22
+ send(archive.class.name.split(/::/).last.downcase.pluralize.to_sym).delete(key)
23
+ end
24
+
25
+ def touch_blog_posting(page)
26
+ key = page.data.title
27
+ blog_posting = blog_postings[key]
28
+
29
+ if blog_posting
30
+ blog_posting.update
31
+ else
32
+ blog_postings[key] = BlogPosting.new(page, self)
33
+ end
34
+ end
35
+
36
+ def blog_posting(path)
37
+ blog_postings.values.select { |v| v.url == path }.first
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,95 @@
1
+ module Hightouch
2
+ class BlogPosting
3
+ include Virtus
4
+
5
+ DESC_SEPARATOR = /READMORE/
6
+
7
+ attribute :blog, Blog
8
+
9
+ attribute :name, String
10
+ attribute :url, String
11
+ attribute :description, String
12
+ attribute :author, String
13
+ attribute :date_created, Date
14
+ attribute :tags, Hash, default: {}
15
+ attribute :categories, Hash, default: {}
16
+ attribute :archive, Archive
17
+
18
+ attr_reader :raw, :page
19
+
20
+ def initialize(page, blog)
21
+ @blog = blog
22
+ @page = page
23
+
24
+ update
25
+ end
26
+
27
+ def update
28
+ app = page.store.app
29
+ path = page.source_file.sub(app.source_dir, '')
30
+
31
+ @name = page.data.title
32
+
33
+ @date_created = Date.strptime(page.data.date_created, '%Y/%m/%d') if page.data.date_created
34
+ @author = page.data.author
35
+ @url = '/' + page.path
36
+ @raw = app.frontmatter(path).last
37
+
38
+ update_association(Category, page.data.categories) if page.data.categories
39
+ update_association(Tag, page.data.tags) if page.data.tags
40
+ update_archive(@date_created)
41
+
42
+ @description = nil
43
+ @article_body = nil
44
+ end
45
+
46
+ def article_body
47
+ @article_body ||= begin
48
+ body = page.render(layout: false)
49
+ body.sub!(DESC_SEPARATOR, '')
50
+ end
51
+ end
52
+
53
+ def description
54
+ @description ||= begin
55
+ desc = if raw =~ DESC_SEPARATOR
56
+ raw.split(DESC_SEPARATOR).first
57
+ else
58
+ raw.match(/(.{1,200}.*?)(\n|\Z)/m).to_s
59
+ end
60
+ engine = ::Tilt[page.source_file].new { desc }
61
+ engine.render
62
+ end
63
+
64
+ end
65
+
66
+ private
67
+ def update_association(type, updated)
68
+ association_name = type.name.split(/::/).last.downcase.pluralize.to_sym
69
+ association = send(association_name)
70
+ association.each do |k, v|
71
+ unless updated.include? k
72
+ association.delete(k).remove_blog_posting(self)
73
+ end
74
+ end
75
+
76
+ updated.each do |u|
77
+ a = association[u]
78
+ next if a
79
+
80
+ a = blog.send(association_name)[u] || blog.send(:create_archive, type, name: u, blog: blog)
81
+
82
+ a.add_blog_posting(self)
83
+ association[u] = a
84
+ end
85
+ end
86
+
87
+ def update_archive(date_created)
88
+ name = date_created.strftime('%Y/%m')
89
+ return if archive && archive.name == name
90
+
91
+ archive = blog.archives[name] || blog.create_archive(Archive, name: name, blog: blog)
92
+ archive.add_blog_posting(self)
93
+ end
94
+ end
95
+ end