hawkins 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +30 -0
- data/.rubocop.yml +54 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +19 -0
- data/Rakefile +46 -0
- data/bin/hawkins +5 -0
- data/hawkins.gemspec +40 -0
- data/lib/hawkins.rb +45 -0
- data/lib/hawkins/cli.rb +190 -0
- data/lib/hawkins/guard.rb +268 -0
- data/lib/hawkins/isolation.rb +116 -0
- data/lib/hawkins/version.rb +3 -0
- data/test/resources/test_site/_config.yml +21 -0
- data/test/resources/test_site/_includes/footer.html +5 -0
- data/test/resources/test_site/_includes/header.html +8 -0
- data/test/resources/test_site/_includes/top.html +6 -0
- data/test/resources/test_site/_layouts/default.html +10 -0
- data/test/resources/test_site/_posts/2015-10-08-test-post.md +4 -0
- data/test/resources/test_site/_posts/2015-10-09-another-post.md +4 -0
- data/test/resources/test_site/assets/hawkins.css +7 -0
- data/test/resources/test_site/index.html +21 -0
- data/test/spec_helper.rb +59 -0
- data/test/test_hawkins.rb +134 -0
- metadata +319 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3860e1fbd32bdc4502dcebb3bc21595486177226
|
4
|
+
data.tar.gz: 728296eae31ec40dc27a892cd7c4a7ad906bd41b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a1a4138fe80b645d1676295480f997150f72906c5863350145827ee93cd871952f451b5050cd45722f75fa7aaf17e57d6ec15a342864f7c5df966277e36d132d
|
7
|
+
data.tar.gz: 17b7dc16253c30272ba6162983479a974c24949e16f481b9bd6a01b132e16c1d08b640340d27812452f2f1c3ec05666df104bc31b97e0f18adf113a0922dc8fa
|
data/.document
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
test/resources/test_site/_site
|
2
|
+
|
3
|
+
# rcov generated
|
4
|
+
coverage
|
5
|
+
coverage.data
|
6
|
+
|
7
|
+
# rdoc generated
|
8
|
+
rdoc
|
9
|
+
|
10
|
+
# yard generated
|
11
|
+
doc
|
12
|
+
.yardoc
|
13
|
+
|
14
|
+
# bundler
|
15
|
+
.bundle
|
16
|
+
|
17
|
+
# jeweler generated
|
18
|
+
pkg
|
19
|
+
|
20
|
+
# ruby environment manager
|
21
|
+
.rvmrc
|
22
|
+
.ruby-version
|
23
|
+
.ruby-gemset
|
24
|
+
|
25
|
+
# bundler
|
26
|
+
Gemfile.lock
|
27
|
+
|
28
|
+
# For vim:
|
29
|
+
*.swp
|
30
|
+
tags
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
ClassLength:
|
2
|
+
# It's not worth bending over backwards to avoid long classes.
|
3
|
+
Enabled: false
|
4
|
+
CollectionMethods:
|
5
|
+
PreferredMethods:
|
6
|
+
collect: 'map'
|
7
|
+
reduce: 'inject'
|
8
|
+
detect: 'find'
|
9
|
+
find_all: 'select'
|
10
|
+
CyclomaticComplexity:
|
11
|
+
Enabled: false
|
12
|
+
Documentation:
|
13
|
+
# TODO: We need to add a bunch of docs before we can enable this.
|
14
|
+
Enabled: false
|
15
|
+
HashSyntax:
|
16
|
+
EnforcedStyle: hash_rockets
|
17
|
+
IfUnlessModifier:
|
18
|
+
Enabled: false
|
19
|
+
LineLength:
|
20
|
+
Enabled: true
|
21
|
+
Max: 100
|
22
|
+
MethodLength:
|
23
|
+
Max: 50
|
24
|
+
CountComments: false
|
25
|
+
ModuleFunction:
|
26
|
+
# The module_function declaration makes methods private so it means you can't use the module as a module.
|
27
|
+
Enabled: false
|
28
|
+
ParenthesesAroundCondition:
|
29
|
+
AllowSafeAssignment: false
|
30
|
+
PerlBackrefs:
|
31
|
+
Enabled: false
|
32
|
+
RaiseArgs:
|
33
|
+
EnforcedStyle: compact
|
34
|
+
RedundantReturn:
|
35
|
+
AllowMultipleReturnValues: true
|
36
|
+
RegexpLiteral:
|
37
|
+
Enabled: false
|
38
|
+
SignalException:
|
39
|
+
EnforcedStyle: only_raise
|
40
|
+
SpaceAroundEqualsInParameterDefault:
|
41
|
+
EnforcedStyle: no_space
|
42
|
+
SpaceInsideHashLiteralBraces:
|
43
|
+
EnforcedStyle: no_space
|
44
|
+
StringLiterals:
|
45
|
+
# They say to not use double quoted strings unless there is interpolation.
|
46
|
+
# While this gives a marginal parse time speedup, it makes working with
|
47
|
+
# strings annoying.
|
48
|
+
Enabled: false
|
49
|
+
TrailingComma:
|
50
|
+
Enabled: true
|
51
|
+
UnneededPercentX:
|
52
|
+
Enabled: false
|
53
|
+
WhileUntilModifier:
|
54
|
+
Enabled: false
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Alex Wood
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= hawkins
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to hawkins
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2014 Alex Wood. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
require 'bundler/gem_tasks'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require './lib/hawkins/version'
|
15
|
+
|
16
|
+
require 'rake'
|
17
|
+
require 'rdoc/task'
|
18
|
+
require 'rubocop/rake_task'
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'rspec/core/rake_task'
|
22
|
+
RSpec::Core::RakeTask.new(:spec, :tag) do |t, task_args|
|
23
|
+
t.rspec_opts = "--tag #{task_args[:tag]}" if task_args.key?(:tag)
|
24
|
+
t.pattern = "test/**/*.rb"
|
25
|
+
end
|
26
|
+
task :default => :spec
|
27
|
+
task :test => :spec
|
28
|
+
rescue LoadError
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Code coverage detail"
|
32
|
+
task :simplecov do
|
33
|
+
ENV['COVERAGE'] = "true"
|
34
|
+
Rake::Task['test'].execute
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::RDocTask.new do |rdoc|
|
38
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
39
|
+
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = "hawkins #{version}"
|
42
|
+
rdoc.rdoc_files.include('README*')
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
45
|
+
|
46
|
+
RuboCop::RakeTask.new
|
data/bin/hawkins
ADDED
data/hawkins.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hawkins/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hawkins"
|
8
|
+
spec.version = Hawkins::VERSION
|
9
|
+
spec.authors = ["Alex Wood"]
|
10
|
+
spec.email = ["awood@redhat.com"]
|
11
|
+
spec.summary = "A Jekyll extension that adds in Live Reload and page isolation"
|
12
|
+
spec.homepage = "http://github.com/awood/hawkins"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = %x(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_runtime_dependency("guard", "~> 2.8")
|
21
|
+
spec.add_runtime_dependency("guard-livereload")
|
22
|
+
spec.add_runtime_dependency("jekyll", "~> 2.5")
|
23
|
+
spec.add_runtime_dependency("rack")
|
24
|
+
spec.add_runtime_dependency("rack-livereload")
|
25
|
+
spec.add_runtime_dependency("safe_yaml")
|
26
|
+
spec.add_runtime_dependency("stringex")
|
27
|
+
spec.add_runtime_dependency("thin")
|
28
|
+
spec.add_runtime_dependency("thor")
|
29
|
+
|
30
|
+
spec.add_development_dependency("bundler", "~> 1.6")
|
31
|
+
spec.add_development_dependency("rspec-core")
|
32
|
+
spec.add_development_dependency("rspec-expectations")
|
33
|
+
spec.add_development_dependency("rspec-mocks")
|
34
|
+
spec.add_development_dependency("rake")
|
35
|
+
spec.add_development_dependency("rdoc", "~> 3.12")
|
36
|
+
# Rubocop can issue new releases with new checks which can result
|
37
|
+
# in errors we don't want. We'll manage the version very strictly.
|
38
|
+
spec.add_development_dependency("rubocop", "= 0.24.1")
|
39
|
+
spec.add_development_dependency("simplecov")
|
40
|
+
end
|
data/lib/hawkins.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'guard'
|
3
|
+
require 'jekyll'
|
4
|
+
require 'rack'
|
5
|
+
require 'safe_yaml/load'
|
6
|
+
require 'stringex_lite'
|
7
|
+
require 'thor'
|
8
|
+
|
9
|
+
require 'hawkins/cli'
|
10
|
+
require 'hawkins/guard'
|
11
|
+
require 'hawkins/version'
|
12
|
+
|
13
|
+
module Hawkins
|
14
|
+
DEFAULT_EXTENSIONS = %w(
|
15
|
+
md
|
16
|
+
mkd
|
17
|
+
mkdn
|
18
|
+
markdown
|
19
|
+
textile
|
20
|
+
html
|
21
|
+
haml
|
22
|
+
slim
|
23
|
+
xml
|
24
|
+
yml
|
25
|
+
)
|
26
|
+
|
27
|
+
# When using pagination, Jekyll wants an index.html
|
28
|
+
DEFAULT_INCLUDES = %w(
|
29
|
+
*.less
|
30
|
+
*.js
|
31
|
+
*.css
|
32
|
+
*.png
|
33
|
+
*.jpg
|
34
|
+
*.gif
|
35
|
+
*.jpeg
|
36
|
+
*.eot
|
37
|
+
*.svg
|
38
|
+
*.ttf
|
39
|
+
*.woff
|
40
|
+
404.html
|
41
|
+
index.*
|
42
|
+
)
|
43
|
+
|
44
|
+
ISOLATION_FILE = ".isolation_config.yml"
|
45
|
+
end
|
data/lib/hawkins/cli.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'jekyll'
|
2
|
+
require 'guard'
|
3
|
+
require 'guard/runner'
|
4
|
+
require 'listen'
|
5
|
+
|
6
|
+
module Hawkins
|
7
|
+
class Cli < Thor
|
8
|
+
include Thor::Actions
|
9
|
+
|
10
|
+
attr_accessor :jekyll_config
|
11
|
+
|
12
|
+
def self.exit_on_failure?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
super
|
18
|
+
Jekyll.logger.log_level = :warn
|
19
|
+
@jekyll_config = Jekyll::Configuration[Jekyll::Configuration::DEFAULTS]
|
20
|
+
@jekyll_config.read_config_files(@jekyll_config.config_files({}))
|
21
|
+
end
|
22
|
+
|
23
|
+
desc "post TITLE", "create a post"
|
24
|
+
option :editor, :default => ENV['VISUAL'] || ENV['EDITOR'] || 'vi'
|
25
|
+
option :date, :default => Time.now.to_s
|
26
|
+
def post(title)
|
27
|
+
begin
|
28
|
+
date = Date.parse(options[:date])
|
29
|
+
rescue
|
30
|
+
# If an exception is not descended from the Thor::Error class, Thor
|
31
|
+
# prints the stacktrace. We don't want a stacktrace in this instance,
|
32
|
+
# so we lie and use one of Thor's error classes in the call to fail().
|
33
|
+
fail(Thor::InvocationError, "ERROR: Could not parse '#{options[:date]}' as a date")
|
34
|
+
end
|
35
|
+
slug = title.to_url
|
36
|
+
dest = path_to(:_posts)
|
37
|
+
|
38
|
+
shell.mute { empty_directory(dest) }
|
39
|
+
|
40
|
+
filename = "#{date.strftime('%Y-%m-%d')}-#{slug}.md"
|
41
|
+
content = <<-CONTENT.gsub(/^\s*/, '')
|
42
|
+
---
|
43
|
+
title: #{title}
|
44
|
+
---
|
45
|
+
CONTENT
|
46
|
+
|
47
|
+
create_file(path_to(dest, filename), content)
|
48
|
+
|
49
|
+
case options[:editor]
|
50
|
+
when /g?vim/
|
51
|
+
editor_args = "+"
|
52
|
+
when /x?emacs/
|
53
|
+
editor_args = "+#{content.lines.count}"
|
54
|
+
else
|
55
|
+
editor_args = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
exec(*[options[:editor], editor_args, path_to(dest, filename)].compact)
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "isolate FILE ...", "work on a file or files in isolation (globs are allowed)"
|
62
|
+
long_desc <<-LONGDESC
|
63
|
+
Jekyll's regeneration capability is limited to regenerating the
|
64
|
+
entire site. This option will ignore all files except the ones you
|
65
|
+
specify in order to speed regeneration.
|
66
|
+
|
67
|
+
Keep in mind that Jekyll's inclusion mechanism is not aware of
|
68
|
+
subdirectories so this command operates on the basename of all files
|
69
|
+
that match the file or glob you provide.
|
70
|
+
LONGDESC
|
71
|
+
option :future, :type => :boolean, :default => false, :desc => "publish future dated posts"
|
72
|
+
option :drafts, :type => :boolean, :default => false, :desc => "publish draft posts"
|
73
|
+
def isolate(*files)
|
74
|
+
SafeYAML::OPTIONS[:default_mode] = :safe
|
75
|
+
|
76
|
+
pages = []
|
77
|
+
pages << Hawkins::DEFAULT_EXTENSIONS.map do |ext|
|
78
|
+
Dir.glob("**/*.#{ext}")
|
79
|
+
end
|
80
|
+
pages.flatten!.reject! { |f| File.fnmatch?('_site/*', f) }
|
81
|
+
|
82
|
+
pages.select! do |p|
|
83
|
+
content = File.read(p)
|
84
|
+
# Jekyll only renders pages with YAML frontmatter. See Jekyll's
|
85
|
+
# convertible.rb read_yaml method.
|
86
|
+
content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
87
|
+
end
|
88
|
+
|
89
|
+
isolation_config = {}
|
90
|
+
isolation_config['exclude'] = pages.concat(jekyll_config['exclude'])
|
91
|
+
isolation_config['include'] = Hawkins::DEFAULT_INCLUDES
|
92
|
+
|
93
|
+
files.each do |glob|
|
94
|
+
matches = Dir.glob(glob)
|
95
|
+
matches.map! do |f|
|
96
|
+
ext = File.extname(f)
|
97
|
+
# If we have to add to this list from Rack, then Rack will have no
|
98
|
+
# idea what the original file extension was. Use a wildcard to
|
99
|
+
# avoid this issue.
|
100
|
+
"#{File.basename(f, ext)}.*"
|
101
|
+
end
|
102
|
+
if matches.empty?
|
103
|
+
raise Thor::Error.new("Could not find any matches for #{glob}.")
|
104
|
+
end
|
105
|
+
isolation_config['include'] += matches
|
106
|
+
end
|
107
|
+
|
108
|
+
isolation_config['include'].uniq!
|
109
|
+
|
110
|
+
# We have to write the isolation config out to a file so that the Guard
|
111
|
+
# can detect when the configuration changes.
|
112
|
+
create_file(Hawkins::ISOLATION_FILE, YAML.dump(isolation_config))
|
113
|
+
|
114
|
+
begin
|
115
|
+
invoke(:serve, [], options)
|
116
|
+
ensure
|
117
|
+
remove_file(Hawkins::ISOLATION_FILE)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
desc "serve", "render and serve the site"
|
122
|
+
option :future, :type => :boolean, :default => false, :desc => "publish future dated posts"
|
123
|
+
option :drafts, :type => :boolean, :default => false, :desc => "publish draft posts"
|
124
|
+
def serve
|
125
|
+
config_files = jekyll_config.config_files({}) || []
|
126
|
+
if File.exist?(Hawkins::ISOLATION_FILE)
|
127
|
+
config_files << Hawkins::ISOLATION_FILE
|
128
|
+
end
|
129
|
+
|
130
|
+
# By default Jekyll uses the absolute path and Guard doesn't so we need to fix that.
|
131
|
+
dest = Pathname.new(jekyll_config['destination']).relative_path_from(Pathname.new(Dir.pwd))
|
132
|
+
|
133
|
+
contents = <<-GUARDFILE.gsub(/^\s*/, '')
|
134
|
+
interactor :off
|
135
|
+
notification :off
|
136
|
+
guard 'hawkins',
|
137
|
+
:config => #{config_files},
|
138
|
+
:drafts => #{options[:drafts]},
|
139
|
+
:future => #{options[:future]} do
|
140
|
+
watch %r{.*}
|
141
|
+
ignore %r{^#{dest}}
|
142
|
+
end
|
143
|
+
|
144
|
+
guard 'livereload',
|
145
|
+
:grace_period => 5 do
|
146
|
+
watch %r{.*}
|
147
|
+
end
|
148
|
+
GUARDFILE
|
149
|
+
guard_start(contents)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Methods in the no_tasks block are not exposed to users
|
153
|
+
no_tasks do
|
154
|
+
def guard_start(guardfile_contents)
|
155
|
+
Guard.setup(:guardfile_contents => guardfile_contents)
|
156
|
+
Guard::Runner.new.run(:start)
|
157
|
+
Guard.listener.start
|
158
|
+
|
159
|
+
exitcode = 0
|
160
|
+
begin
|
161
|
+
while Guard.interactor.foreground != :exit
|
162
|
+
Guard.queue.process while Guard.queue.pending?
|
163
|
+
end
|
164
|
+
rescue Interrupt
|
165
|
+
rescue SystemExit => e
|
166
|
+
exitcode = e.status
|
167
|
+
end
|
168
|
+
|
169
|
+
guard_stop
|
170
|
+
exitcode
|
171
|
+
end
|
172
|
+
|
173
|
+
def guard_stop
|
174
|
+
Guard.listener.stop
|
175
|
+
Guard.interactor.background
|
176
|
+
Guard::Runner.new.run(:stop)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.source_root
|
180
|
+
File.expand_path("..", File.dirname(__FILE__))
|
181
|
+
end
|
182
|
+
|
183
|
+
# Builds a path within the source_root
|
184
|
+
def path_to(*args)
|
185
|
+
args = args.map(&:to_s)
|
186
|
+
File.join(*args)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|