hightouch 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +12 -0
- data/Gemfile.lock +121 -0
- data/README.md +10 -0
- data/bin/hightouch +6 -0
- data/bin/ht +6 -0
- data/config.rb +26 -0
- data/lib/hightouch.rb +89 -0
- data/lib/hightouch/app_generator.rb +25 -0
- data/lib/hightouch/archive.rb +30 -0
- data/lib/hightouch/archive_page_generator.rb +10 -0
- data/lib/hightouch/blog.rb +40 -0
- data/lib/hightouch/blog_posting.rb +95 -0
- data/lib/hightouch/category.rb +7 -0
- data/lib/hightouch/cli.rb +10 -0
- data/lib/hightouch/tag.rb +7 -0
- data/lib/hightouch/version.rb +3 -0
- data/rvmrc.example +1 -0
- data/source/blog/2012/02/02/setup-your-rails-dev-env-with-rda.html.markdown +150 -0
- data/source/blog/2012/03/01/example.html.markdown +11 -0
- data/source/images/adventure/background.jpg +0 -0
- data/source/images/adventure/bars.png +0 -0
- data/source/images/adventure/blacktrans.png +0 -0
- data/source/images/adventure/blog_posting_bg.png +0 -0
- data/source/index.html.haml +2 -0
- data/source/javascripts/all.js +1 -0
- data/source/layouts/blog_postings.html.haml +31 -0
- data/source/layouts/layout.html.haml +31 -0
- data/source/partials/_blog_posting.html.haml +14 -0
- data/source/partials/_blog_posting_summary.html.haml +16 -0
- data/source/partials/_bottombar.html.haml +9 -0
- data/source/partials/_sidebar.html.haml +17 -0
- data/source/partials/_tags.html.haml +6 -0
- data/source/stylesheets/adventure.css.scss +198 -0
- data/source/stylesheets/coderay.css +136 -0
- data/source/stylesheets/default.css.scss +3 -0
- data/source/stylesheets/pygments-github.css +70 -0
- data/source/stylesheets/pygments.css +62 -0
- data/source/templates/archive.html.haml +4 -0
- data/source/templates/category.html.haml +5 -0
- data/source/templates/tag.html.haml +4 -0
- data/spec/spec_helper.rb +12 -0
- 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
data/bin/hightouch
ADDED
data/bin/ht
ADDED
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
|