ignoramos 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +18 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +8 -0
- data/bin/ignoramos +5 -0
- data/ignoramos.gemspec +22 -0
- data/lib/commands/build_command.rb +188 -0
- data/lib/commands/new_command.rb +34 -0
- data/lib/commands/tweet_command.rb +64 -0
- data/lib/ignoramos.rb +29 -0
- data/lib/models/app_config.rb +32 -0
- data/lib/models/page.rb +11 -0
- data/lib/models/post.rb +123 -0
- data/rebuild_gem +7 -0
- data/spec/commands/build_command_spec.rb +379 -0
- data/spec/commands/new_command_spec.rb +36 -0
- data/spec/commands/tweet_command_spec.rb +59 -0
- data/spec/models/app_config_spec.rb +66 -0
- data/spec/models/page_spec.rb +47 -0
- data/spec/models/post_spec.rb +156 -0
- data/spec/spec_helper.rb +2 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 33b3c974ee5d9c39f5917c0cfbf433aa443ff6ec
|
4
|
+
data.tar.gz: 5d636d2196ac7dbc69fe47c9112fc853a52a0d8a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f1e9153849efd1bfc9f0c217e54c8c58fba6b12e5c419792edc15ac10f06d0a8e3a4e6f325bb6e24405cae5403c27523fb649218eea0f63f58d1e7be8ac762bc
|
7
|
+
data.tar.gz: 3a96d8a332415d5531c8b41826910b25748533687f997045d848985e0356cf5d65fbec76b6a527566f64a9722b15a673410355e0c12be2fb4c2d7d530dfdfa8c
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.a
|
2
|
+
*.bundle
|
3
|
+
*.gem
|
4
|
+
*.o
|
5
|
+
*.rbc
|
6
|
+
*.so
|
7
|
+
.bundle
|
8
|
+
.config
|
9
|
+
.yardoc
|
10
|
+
Gemfile.lock
|
11
|
+
InstalledFiles
|
12
|
+
_yardoc
|
13
|
+
coverage
|
14
|
+
doc/
|
15
|
+
lib/bundler/man
|
16
|
+
mkmf.log
|
17
|
+
pkg
|
18
|
+
rdoc
|
19
|
+
spec/reports
|
20
|
+
test/tmp
|
21
|
+
test/version_tmp
|
22
|
+
tmp
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in ignoramos.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'liquid', '~> 2.6.1'
|
7
|
+
gem 'redcarpet', '~> 3.1.2'
|
8
|
+
gem 'rouge', '~> 1.6.1'
|
9
|
+
gem 'twitter', '~> 5.11.0'
|
10
|
+
|
11
|
+
group :development, :test do
|
12
|
+
gem 'rspec', '~> 3.0.0'
|
13
|
+
gem 'fakefs', '~> 0.5.0'
|
14
|
+
gem 'guard'
|
15
|
+
gem 'guard-rspec'
|
16
|
+
gem 'coveralls', require: false
|
17
|
+
gem 'timecop'
|
18
|
+
end
|
data/Guardfile
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
guard 'rspec', :version => 2 do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
4
|
+
watch(%r{^lib/commands/(.+)\.rb$}) { |m| "spec/commands/#{m[1]}_spec.rb" }
|
5
|
+
watch(%r{^lib/models/(.+)\.rb$}) { |m| "spec/models/#{m[1]}_spec.rb" }
|
6
|
+
watch('spec/spec_helper.rb') { "spec" }
|
7
|
+
end
|
8
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Amos Chan
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
Ignoramos: A static-site generator
|
2
|
+
==================================
|
3
|
+
|
4
|
+
[](https://travis-ci.org/achan/ignoramos)
|
6
|
+
[](https://codeclimate.com/github/achan/ignoramos)
|
8
|
+
[](https://coveralls.io/r/achan/ignoramos)
|
10
|
+
|
11
|
+
Getting started
|
12
|
+
===============
|
13
|
+
|
14
|
+
```
|
15
|
+
~ $ gem install ignoramos
|
16
|
+
~ $ ignoramos new mysite
|
17
|
+
```
|
18
|
+
|
19
|
+
The `new` command will create a directory with the name provided and the
|
20
|
+
directory structure below.
|
21
|
+
|
22
|
+
Directory structure
|
23
|
+
===================
|
24
|
+
|
25
|
+
```
|
26
|
+
.
|
27
|
+
├── _config.yml
|
28
|
+
├── _drafts
|
29
|
+
| ├── not-ready-to-be-published.md
|
30
|
+
| └── to-be-reviewed-by-editor.md
|
31
|
+
├── _includes
|
32
|
+
| ├── footer.html
|
33
|
+
| └── header.html
|
34
|
+
├── _layouts
|
35
|
+
| ├── default.html
|
36
|
+
| └── post.html
|
37
|
+
├── _posts
|
38
|
+
| ├── 2014-06-22-hello-world.md
|
39
|
+
| └── 2014-07-19-hello-world-pt-2.md
|
40
|
+
└── _site
|
41
|
+
└── <point web root here>
|
42
|
+
```
|
43
|
+
|
44
|
+
```
|
45
|
+
$ ignoramos build
|
46
|
+
```
|
47
|
+
|
48
|
+
The `build` command is expected to be run at the root directory of the
|
49
|
+
application. It will generate all posts in the `_posts` directory and copy
|
50
|
+
every file not prefixed with `_` into `_site`.
|
51
|
+
|
52
|
+
How posts are built
|
53
|
+
===================
|
54
|
+
|
55
|
+
- Load the markdown file
|
56
|
+
- Parse the post for YAML to determine layout
|
57
|
+
- Render layout as content
|
58
|
+
- Render liquid layout (header, footer, content)
|
59
|
+
|
60
|
+
After all posts and pages are generated, all remaining files that are not from a
|
61
|
+
folder prefixed with `_` will be copied over to `_site`. Custom files take
|
62
|
+
precedence, so if your files conflict with generated ones, yours will overwrite
|
63
|
+
the generated file.
|
data/Rakefile
ADDED
data/bin/ignoramos
ADDED
data/ignoramos.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "ignoramos"
|
7
|
+
spec.version = "1.0.0"
|
8
|
+
spec.authors = ["Amos Chan"]
|
9
|
+
spec.email = ["amosschan@gmail.com"]
|
10
|
+
spec.summary = %q{A static site generator for blogs and microposts.}
|
11
|
+
spec.description = spec.summary
|
12
|
+
spec.homepage = "http://rubygems.org/gems/ignoramos"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'liquid'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'models/post'
|
4
|
+
require 'models/page'
|
5
|
+
require 'models/app_config'
|
6
|
+
|
7
|
+
class BuildCommand
|
8
|
+
def initialize(dir = Dir.pwd)
|
9
|
+
@dir = dir
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
Liquid::Template.file_system = Liquid::LocalFileSystem.new("#{ @dir }/_includes")
|
14
|
+
FileUtils.rm_rf("#{ @dir }/_site")
|
15
|
+
mkdir_p("_site")
|
16
|
+
|
17
|
+
generate_pages
|
18
|
+
generate_posts
|
19
|
+
generate_tags_index
|
20
|
+
generate_homepage
|
21
|
+
|
22
|
+
copy_custom_files
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def config
|
27
|
+
@config ||= AppConfig.new(read_file("_config.yml"))
|
28
|
+
end
|
29
|
+
|
30
|
+
def copy_custom_files
|
31
|
+
custom_files.each { |fn| copy_into_site(fn) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def copy_into_site(filename)
|
35
|
+
destination = sandbox_filename(filename)
|
36
|
+
|
37
|
+
if File.directory?(filename)
|
38
|
+
destination = destination.slice(0..(destination.rindex('/')))
|
39
|
+
end
|
40
|
+
|
41
|
+
FileUtils.cp_r(filename, destination)
|
42
|
+
end
|
43
|
+
|
44
|
+
def sandbox_filename(filename)
|
45
|
+
filename.gsub(@dir, "#{ @dir }/_site")
|
46
|
+
end
|
47
|
+
|
48
|
+
def custom_files
|
49
|
+
@custom_files ||= Dir.glob("#{@dir}/[^_]*")
|
50
|
+
end
|
51
|
+
|
52
|
+
def posts
|
53
|
+
@posts ||= load_posts
|
54
|
+
end
|
55
|
+
|
56
|
+
def pages
|
57
|
+
@pages ||= load_posts('_pages') { |contents| Page.new(contents) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_posts(dir='_posts', &block)
|
61
|
+
posts = []
|
62
|
+
|
63
|
+
Dir.foreach("#{ @dir }/#{ dir }") do |item|
|
64
|
+
next if item == '.' || item == '..'
|
65
|
+
|
66
|
+
contents = read_file("#{ dir }/#{ item }")
|
67
|
+
posts << if block_given?
|
68
|
+
yield contents
|
69
|
+
else
|
70
|
+
Post.new(contents)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
posts
|
75
|
+
end
|
76
|
+
|
77
|
+
def generate_pages
|
78
|
+
generate_dir(pages, 'page') { |p| { 'page' => p } }
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate_posts
|
82
|
+
generate_dir(posts, 'post') { |p| { 'post' => p } }
|
83
|
+
end
|
84
|
+
|
85
|
+
def generate_dir(posts, template, &block)
|
86
|
+
posts.each do |post|
|
87
|
+
params = yield post
|
88
|
+
layout = read_file("_layouts/#{ post.vars['layout'] }/#{ template }.liquid")
|
89
|
+
post_params = { 'site' => site_config }.merge(params.merge(post.vars))
|
90
|
+
post_params['title'] = "#{ post_params['title'] } - #{ config.vars['site']['name'] }"
|
91
|
+
mkdir_p("_site#{ post.path }")
|
92
|
+
new_file("_site#{ post.permalink }",
|
93
|
+
Liquid::Template.parse(layout).render(post_params))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def tags
|
98
|
+
tags = {}
|
99
|
+
posts.each do |p|
|
100
|
+
p.tags.each do |t|
|
101
|
+
tags[t] ||= []
|
102
|
+
tags[t] << p
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
tags.sort.map do |name, posts|
|
107
|
+
{ 'name' => name, 'posts' => posts.sort_by { |p| p.title } }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def generate_tags_index
|
112
|
+
new_tags_index_file("_site/tags.html",
|
113
|
+
"Tag Index - #{ config.vars['site']['name'] }",
|
114
|
+
tags)
|
115
|
+
|
116
|
+
generate_index_per_tag
|
117
|
+
end
|
118
|
+
|
119
|
+
def tags_layout
|
120
|
+
@layout ||= read_file("_layouts/default/tags.liquid")
|
121
|
+
end
|
122
|
+
|
123
|
+
def new_tags_index_file(filename, title, tags)
|
124
|
+
new_file(filename,
|
125
|
+
Liquid::Template.parse(tags_layout).render({
|
126
|
+
'title' => title,
|
127
|
+
'tags' => tags,
|
128
|
+
'site' => site_config
|
129
|
+
}))
|
130
|
+
end
|
131
|
+
|
132
|
+
def generate_index_per_tag
|
133
|
+
mkdir_p("_site/tags")
|
134
|
+
|
135
|
+
tags.each do |tag|
|
136
|
+
new_tags_index_file("_site/tags/#{ tag['name'] }.html",
|
137
|
+
"##{ tag['name'] } - #{ config.vars['site']['name'] }",
|
138
|
+
[tag])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def generate_homepage
|
143
|
+
homepage_posts = posts.sort_by { |p| [p.timestamp, p.title] }.
|
144
|
+
reverse.
|
145
|
+
first(5)
|
146
|
+
|
147
|
+
layout = read_file("_layouts/default/posts.liquid")
|
148
|
+
|
149
|
+
new_file("_site/index.html",
|
150
|
+
Liquid::Template.parse(layout).render({
|
151
|
+
'posts' => homepage_posts,
|
152
|
+
'title' => config.vars['site']['name'],
|
153
|
+
'site' => site_config
|
154
|
+
}))
|
155
|
+
end
|
156
|
+
|
157
|
+
def site_config
|
158
|
+
{
|
159
|
+
"description" => config.site_description,
|
160
|
+
"site_map" => config.site_map,
|
161
|
+
"user" => config.user
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
def mkdir_p(dir)
|
166
|
+
FileUtils.mkdir_p("#{ @dir }/#{ dir }")
|
167
|
+
end
|
168
|
+
|
169
|
+
def new_file(filename, contents)
|
170
|
+
new_post_file = File.new("#{ @dir }/#{ filename }", 'w')
|
171
|
+
new_post_file.write(contents)
|
172
|
+
new_post_file.close
|
173
|
+
end
|
174
|
+
|
175
|
+
def read_file(filename)
|
176
|
+
File.open("#{ @dir }/#{ filename }", 'r') do |file|
|
177
|
+
file.read()
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def cache
|
182
|
+
@cache ||= {}
|
183
|
+
end
|
184
|
+
|
185
|
+
def template(layout)
|
186
|
+
cache[layout.to_sym] ||= read_file("_layouts/#{ layout }/post.liquid")
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class NewCommand
|
4
|
+
attr_accessor :dir
|
5
|
+
|
6
|
+
def initialize(dir)
|
7
|
+
@dir = dir.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
FileUtils.mkdir_p([
|
12
|
+
"#{ @dir }/_drafts",
|
13
|
+
"#{ @dir }/_includes",
|
14
|
+
"#{ @dir }/_layouts",
|
15
|
+
"#{ @dir }/_pages",
|
16
|
+
"#{ @dir }/_posts",
|
17
|
+
"#{ @dir }/_site"
|
18
|
+
])
|
19
|
+
|
20
|
+
blog_name = 'My First Blog'
|
21
|
+
tagline = 'A short description of my blog'
|
22
|
+
desc = 'Site description'
|
23
|
+
|
24
|
+
new_file("_config.yml",
|
25
|
+
"---\nsite:\n name: #{ blog_name }\n tagline: #{ tagline }\n description: #{ desc }")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def new_file(filename, contents)
|
30
|
+
new_post_file = File.new("#{ @dir }/#{ filename }", 'w')
|
31
|
+
new_post_file.write(contents)
|
32
|
+
new_post_file.close
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'liquid'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'models/post'
|
4
|
+
require 'models/app_config'
|
5
|
+
require 'twitter'
|
6
|
+
require 'twitter/tweet'
|
7
|
+
|
8
|
+
class TweetCommand
|
9
|
+
LAYOUT = <<-LAYOUT
|
10
|
+
---
|
11
|
+
title: tweet {{tweet.id}}
|
12
|
+
timestamp: {{tweet.timestamp}}
|
13
|
+
layout: tweet
|
14
|
+
tweet: {{tweet.url}}
|
15
|
+
---
|
16
|
+
|
17
|
+
{{tweet.content}}
|
18
|
+
LAYOUT
|
19
|
+
|
20
|
+
def initialize(tweet)
|
21
|
+
@tweet = tweet
|
22
|
+
@dir = Dir.pwd
|
23
|
+
end
|
24
|
+
|
25
|
+
def execute
|
26
|
+
tweet = twitter.update(@tweet)
|
27
|
+
|
28
|
+
new_file("_posts/tweet-#{tweet.id}.md",
|
29
|
+
Liquid::Template.parse(LAYOUT).render({
|
30
|
+
'tweet' => {
|
31
|
+
'content' => @tweet,
|
32
|
+
'id' => tweet.id,
|
33
|
+
'url' => tweet.uri.to_s,
|
34
|
+
'timestamp' => DateTime.now
|
35
|
+
}
|
36
|
+
}))
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def app_config
|
41
|
+
@config ||= AppConfig.new(read_file("_config.yml"))
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_file(filename, contents)
|
45
|
+
new_post_file = File.new("#{@dir}/#{filename}", 'w')
|
46
|
+
new_post_file.write(contents)
|
47
|
+
new_post_file.close
|
48
|
+
end
|
49
|
+
|
50
|
+
def read_file(filename)
|
51
|
+
File.open("#{@dir}/#{filename}", 'r') do |file|
|
52
|
+
file.read()
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def twitter
|
57
|
+
@twitter ||= Twitter::REST::Client.new do |config|
|
58
|
+
config.consumer_key = "RerlMuPVgYySMdqvuaBeSw"
|
59
|
+
config.consumer_secret = "Ccq3hS7fMplpjwCfvpVyPQXV6nPGGGonXSAdmi8ZIc"
|
60
|
+
config.access_token = app_config.vars['twitter']['access_token']
|
61
|
+
config.access_token_secret = app_config.vars['twitter']['access_token_secret']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|