hawkins 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
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
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
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
@@ -0,0 +1,5 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'hawkins'
4
+
5
+ Hawkins::Cli.start(ARGV)
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
@@ -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