usmu 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.rspec +1 -0
- data/.simplecov +4 -0
- data/.travis.yml +3 -0
- data/CONTRIBUTING.md +16 -0
- data/Guardfile +13 -0
- data/README.md +7 -6
- data/Rakefile +9 -10
- data/bin/usmu +5 -1
- data/lib/usmu.rb +73 -4
- data/lib/usmu/configuration.rb +40 -1
- data/lib/usmu/layout.rb +30 -7
- data/lib/usmu/page.rb +1 -0
- data/lib/usmu/plugin.rb +65 -0
- data/lib/usmu/plugin/core.rb +23 -0
- data/lib/usmu/site_generator.rb +17 -24
- data/lib/usmu/static_file.rb +10 -1
- data/lib/usmu/ui/console.rb +38 -10
- data/lib/usmu/version.rb +2 -2
- data/test/expected-site/default.html +14 -3
- data/test/site/layouts/html.meta.yml +1 -0
- data/test/{features/generator.feature → spec/acceptance/full_site_build.feature} +1 -0
- data/test/spec/acceptance/steps/full_site_build_steps.rb +24 -0
- data/test/spec/configuration_spec.rb +90 -1
- data/test/spec/layout_spec.rb +9 -17
- data/test/spec/mock/usmu/mock_plugin.rb +8 -0
- data/test/spec/page_spec.rb +9 -5
- data/test/spec/plugin/core_spec.rb +5 -0
- data/test/spec/plugin_spec.rb +49 -0
- data/test/spec/site_generator_spec.rb +0 -2
- data/test/spec/spec_helper.rb +12 -1
- data/test/spec/static_file_spec.rb +7 -1
- data/test/spec/support/shared_layout.rb +89 -5
- data/test/spec/ui/console_spec.rb +20 -0
- data/usmu-jruby.gemspec +6 -3
- data/usmu.gemspec +7 -6
- metadata +82 -27
- data/test/features/step_definitions/step_general.rb +0 -18
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
module Usmu
|
3
|
+
class Plugin
|
4
|
+
class Core
|
5
|
+
def commands(ui, c)
|
6
|
+
@log = Logging.logger[self]
|
7
|
+
@log.debug('Adding core console commands...')
|
8
|
+
@ui = ui
|
9
|
+
c.command(:generate) do |command|
|
10
|
+
command.syntax = 'usmu generate'
|
11
|
+
command.description = 'Generates your website using the configuration specified.'
|
12
|
+
command.action &method(:command_generate)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [void]
|
17
|
+
def command_generate(args, options)
|
18
|
+
@site_generator = Usmu::SiteGenerator.new(@ui.configuration)
|
19
|
+
@site_generator.generate
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/usmu/site_generator.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'usmu/configuration'
|
3
|
+
require 'usmu/page'
|
4
|
+
require 'usmu/static_file'
|
2
5
|
|
3
6
|
module Usmu
|
4
7
|
# This is the class that brings everything together to generate a new website.
|
5
8
|
class SiteGenerator
|
6
9
|
# @param [Usmu::Configuration] configuration The configuration to use for generating the website
|
7
10
|
def initialize(configuration)
|
11
|
+
@log = Logging.logger[self]
|
8
12
|
@configuration = configuration
|
9
13
|
end
|
10
14
|
|
11
15
|
# @!attribute [r] layouts
|
12
16
|
# @return [Array<Usmu::Layout>] a list of layouts available in this website.
|
13
17
|
def layouts
|
14
|
-
|
18
|
+
@configuration.layouts_files.map {|l| Usmu::Layout.new(@configuration, l) }
|
15
19
|
end
|
16
20
|
|
17
21
|
# @!attribute [r] renderables
|
@@ -24,7 +28,13 @@ module Usmu
|
|
24
28
|
# The only guarantee made for individual files is that they will conform to the interface defined by
|
25
29
|
# Usmu::StaticFile and thus be renderable, however most files will be one of the subclasses of that class.
|
26
30
|
def renderables
|
27
|
-
|
31
|
+
@configuration.source_files.map do |filename|
|
32
|
+
if Usmu::Layout.is_valid_file? 'source', filename
|
33
|
+
Usmu::Page.new(@configuration, filename)
|
34
|
+
else
|
35
|
+
Usmu::StaticFile.new(@configuration, filename)
|
36
|
+
end
|
37
|
+
end
|
28
38
|
end
|
29
39
|
|
30
40
|
# @!attribute [r] pages
|
@@ -44,7 +54,11 @@ module Usmu
|
|
44
54
|
#
|
45
55
|
# @return [void]
|
46
56
|
def generate
|
57
|
+
@log.info("Source: #{@configuration.source_path}")
|
58
|
+
@log.info("Destination: #{@configuration.destination_path}")
|
59
|
+
|
47
60
|
renderables.each do |page|
|
61
|
+
@log.success("creating #{page.output_filename}...")
|
48
62
|
file = File.join(@configuration.destination_path, page.output_filename)
|
49
63
|
directory = File.dirname(file)
|
50
64
|
|
@@ -53,30 +67,9 @@ module Usmu
|
|
53
67
|
end
|
54
68
|
|
55
69
|
File.write file, page.render
|
70
|
+
FileUtils.touch file, :mtime => File.stat(page.input_path).mtime
|
56
71
|
end
|
57
72
|
nil
|
58
73
|
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
# Helper function to search a directory recursively and return a list of files that are renderable.
|
63
|
-
#
|
64
|
-
# @param [String] directory the directory to search
|
65
|
-
# @param [Boolean] layout is this directory a layouts_path
|
66
|
-
# @return [Array<Usmu::Layout>, Array<Usmu::StaticFile>] Either an array of Layouts or StaticFiles in the directory
|
67
|
-
def get_renderables(directory, layout)
|
68
|
-
Dir["#{directory}/**/*"].select {|f| !f.match(/\.meta.yml$/) }.map do |f|
|
69
|
-
filename = f[(directory.length + 1)..f.length]
|
70
|
-
if layout
|
71
|
-
Usmu::Layout.new(@configuration, filename)
|
72
|
-
else
|
73
|
-
if Usmu::Layout.is_valid_file? 'source', filename
|
74
|
-
Usmu::Page.new(@configuration, filename)
|
75
|
-
else
|
76
|
-
Usmu::StaticFile.new(@configuration, filename)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
74
|
end
|
82
75
|
end
|
data/lib/usmu/static_file.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
require 'usmu/configuration'
|
1
2
|
|
2
3
|
module Usmu
|
3
4
|
# Represents a static file which should be transferred to the destination unchanged. This also acts as the base
|
4
5
|
# class for all layouts and page types. The basic interface defined here is used to process all types of files.
|
5
6
|
class StaticFile
|
7
|
+
@log = Logging.logger[self]
|
8
|
+
|
6
9
|
# @!attribute [r] name
|
7
10
|
# @return [String] the name of the file in the source directory
|
8
11
|
attr_reader :name
|
@@ -25,7 +28,13 @@ module Usmu
|
|
25
28
|
# @param variables [Hash] Variables to be used in the template.
|
26
29
|
# @return [String] The rendered file
|
27
30
|
def render(variables = {})
|
28
|
-
@content || File.read(
|
31
|
+
@content || File.read(input_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @!attribute [r] input_path
|
35
|
+
# @return [String] the full path to the file in the source directory
|
36
|
+
def input_path
|
37
|
+
File.join(@configuration.source_path, @name)
|
29
38
|
end
|
30
39
|
|
31
40
|
# @!attribute [r] output_filename
|
data/lib/usmu/ui/console.rb
CHANGED
@@ -1,25 +1,53 @@
|
|
1
1
|
require 'usmu'
|
2
|
-
require '
|
2
|
+
require 'commander'
|
3
3
|
|
4
4
|
module Usmu
|
5
5
|
module Ui
|
6
6
|
# This is the CLI UI controller. This is initialised by the usmu binary to control the generation process.
|
7
7
|
class Console
|
8
|
+
# @!attribute [r] site_generator
|
9
|
+
# @return [Usmu::SiteGenerator]
|
10
|
+
attr_reader :site_generator
|
11
|
+
|
8
12
|
# @!attribute [r] configuration
|
13
|
+
# Do not access this till your command starts running, eg. in Hooks#commands, otherwise you may not get the right
|
14
|
+
# value for the configuration as option parsing may not have happened yet.
|
9
15
|
# @return [Usmu::Configuration] the configuration for the site we will generate.
|
10
|
-
|
16
|
+
def configuration
|
17
|
+
@configuration || load_configuration('usmu.yml')
|
18
|
+
end
|
11
19
|
|
12
|
-
# @param [Array<String>] args Command line arguments. Typically ARGV should be passed here.
|
13
20
|
def initialize(args)
|
14
|
-
@
|
15
|
-
@
|
21
|
+
@log = Logging.logger[self]
|
22
|
+
@commander = Commander::Runner.new args
|
23
|
+
|
24
|
+
@commander.program :version, Usmu::VERSION
|
25
|
+
@commander.program :description, 'Static site generator powered by Tilt'
|
26
|
+
@commander.program :int_message, 'Interrupt received, closing...'
|
27
|
+
|
28
|
+
@commander.global_option('-v', '--verbose') { Usmu.verbose_logging }
|
29
|
+
@commander.global_option('-q', '--quiet') { Usmu.quiet_logging }
|
30
|
+
@commander.global_option('--log STRING', String) {|log| Usmu.add_file_logger(log) }
|
31
|
+
@commander.global_option('--config STRING', String, &method(:load_configuration))
|
32
|
+
|
33
|
+
Usmu.plugins.load_plugins
|
34
|
+
Usmu.plugins.invoke :commands, self, @commander
|
35
|
+
|
36
|
+
@commander.default_command :generate
|
37
|
+
@commander.run!
|
16
38
|
end
|
17
39
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
40
|
+
def load_configuration(config)
|
41
|
+
@log.info("Usmu v#{Usmu::VERSION}")
|
42
|
+
@log.info('')
|
43
|
+
|
44
|
+
if File.readable? config
|
45
|
+
@configuration = Usmu::Configuration.from_file(config)
|
46
|
+
@log.info("Configuration: #{config}")
|
47
|
+
else
|
48
|
+
@log.fatal("Unable to find configuration file at #{config}")
|
49
|
+
raise
|
50
|
+
end
|
23
51
|
end
|
24
52
|
end
|
25
53
|
end
|
data/lib/usmu/version.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
<
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Default Title | Testing website</title>
|
5
|
+
</head>
|
6
|
+
<body>
|
7
|
+
<div id="content">
|
8
|
+
<h1>Default output</h1>
|
9
|
+
|
10
|
+
<p>No metadata for this file. Uh oh!</p>
|
11
|
+
|
12
|
+
</div>
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'usmu/ui/console'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
step 'I have a site at :location' do |location|
|
5
|
+
@location = "#{location}/usmu.yml"
|
6
|
+
end
|
7
|
+
|
8
|
+
step 'I generate the site' do
|
9
|
+
@site = Usmu::Ui::Console.new(['generate', '--config', @location])
|
10
|
+
end
|
11
|
+
|
12
|
+
step 'the destination directory should match :test_folder' do |test_folder|
|
13
|
+
run = %W{diff -qr #{@site.configuration.destination_path} #{test_folder}}
|
14
|
+
Open3.popen2e(*run) do |i, o, t|
|
15
|
+
output = run.join(' ') + "\n" + o.read
|
16
|
+
fail output if t.value != 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
step 'the modification time for the input file :input should match the output file :output' do |input, output|
|
21
|
+
input_mtime = File.stat(File.join(@site.configuration.source_path, input)).mtime
|
22
|
+
output_mtime = File.stat(File.join(@site.configuration.destination_path, output)).mtime
|
23
|
+
expect(input_mtime).to eq(output_mtime)
|
24
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rspec'
|
2
1
|
require 'usmu/configuration'
|
3
2
|
|
4
3
|
RSpec.describe Usmu::Configuration do
|
@@ -44,8 +43,98 @@ RSpec.describe Usmu::Configuration do
|
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
46
|
+
it 'should have a list of source files' do
|
47
|
+
@configuration = Usmu::Configuration.from_hash({})
|
48
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/test.md))
|
49
|
+
expect(@configuration.source_files).to eq(%w(index.md test.md))
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should ignore metadata files in the source folder' do
|
53
|
+
@configuration = Usmu::Configuration.from_hash({})
|
54
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/index.meta.yml src/test.md))
|
55
|
+
expect(@configuration.source_files).to eq(%w(index.md test.md))
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should have a list of layouts files' do
|
59
|
+
@configuration = Usmu::Configuration.from_hash({})
|
60
|
+
allow(Dir).to receive(:'[]').with('layouts/**/*').and_return(%w(layouts/html.slim layouts/page.slim))
|
61
|
+
expect(@configuration.layouts_files).to eq(%w(html.slim page.slim))
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should ignore metadata files in the layouts folder' do
|
65
|
+
@configuration = Usmu::Configuration.from_hash({})
|
66
|
+
allow(Dir).to receive(:'[]').with('layouts/**/*').and_return(%w(layouts/html.slim layouts/html.meta.yml layouts/page.slim))
|
67
|
+
expect(@configuration.layouts_files).to eq(%w(html.slim page.slim))
|
68
|
+
end
|
69
|
+
|
47
70
|
it 'should remember arbitrary configuration' do
|
48
71
|
configuration = Usmu::Configuration.from_hash({:test => 'foo'})
|
49
72
|
expect(configuration[:test]).to eq('foo')
|
50
73
|
end
|
74
|
+
|
75
|
+
context 'should exclude files from source' do
|
76
|
+
it 'as specified' do
|
77
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['foo.md']})
|
78
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/foo.md))
|
79
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'in ignored folders if trailing "/" is used' do
|
83
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['test/']})
|
84
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/test/foo/test.md src/test/foo.md))
|
85
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'and honor *' do
|
89
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['*/foo.md']})
|
90
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/test/foo/foo.md src/test/foo.md))
|
91
|
+
expect(@configuration.source_files).to eq(%w(index.md test/foo/foo.md))
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'and * ignores folders without a trailing /' do
|
95
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['*']})
|
96
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/test/foo.md src/test.md))
|
97
|
+
expect(@configuration.source_files).to eq(%w(test/foo.md))
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'and honor **' do
|
101
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['**/foo.md']})
|
102
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/test/foo/foo.md src/test/foo.md))
|
103
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'and honor []' do
|
107
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['[ab].md']})
|
108
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/a.md src/b.md))
|
109
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
110
|
+
end
|
111
|
+
|
112
|
+
# FNM_EXTGLOB is supported from MRI 2.0 onwards. We also support the 1.9.3 ABI for JRuby and Rubinius sake. Only
|
113
|
+
# run this test if it's possible for it to pass.
|
114
|
+
if defined?(File::FNM_EXTGLOB)
|
115
|
+
it 'and honor {a,b}' do
|
116
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['{a,b}.md']})
|
117
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/a.md src/b.md))
|
118
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'and honor \\ as an escape' do
|
123
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['\*.md']})
|
124
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/*.md))
|
125
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'and honor ?' do
|
129
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['?.md']})
|
130
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/a.md src/b.md))
|
131
|
+
expect(@configuration.source_files).to eq(%w(index.md))
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'and ignore files inside folders specified via globs with trailing "/"' do
|
135
|
+
@configuration = Usmu::Configuration.from_hash({'exclude' => ['test/*/']})
|
136
|
+
allow(Dir).to receive(:'[]').with('src/**/*').and_return(%w(src/index.md src/test/foo/foo.md src/test/foo.md))
|
137
|
+
expect(@configuration.source_files).to eq(%w(index.md test/foo.md))
|
138
|
+
end
|
139
|
+
end
|
51
140
|
end
|
data/test/spec/layout_spec.rb
CHANGED
@@ -1,27 +1,19 @@
|
|
1
|
-
require 'rspec'
|
2
1
|
require 'support/shared_layout'
|
3
2
|
require 'usmu/layout'
|
4
3
|
|
5
4
|
RSpec.describe Usmu::Layout do
|
6
5
|
it_behaves_like 'an embeddable layout'
|
7
6
|
|
8
|
-
let(:configuration) { Usmu::Configuration.
|
7
|
+
let(:configuration) { Usmu::Configuration.from_hash({}) }
|
9
8
|
|
10
9
|
it 'uses the \'layouts\' folder' do
|
11
|
-
layout = Usmu::Layout.new(configuration, 'html.slim')
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
<body>
|
20
|
-
<div id="content">
|
21
|
-
test
|
22
|
-
</div>
|
23
|
-
</body>
|
24
|
-
</html>
|
25
|
-
EOF
|
10
|
+
layout = Usmu::Layout.new(configuration, 'html.slim', 'slim', "head\nbody", {})
|
11
|
+
expect(layout.send :content_path).to eq('layouts')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has an input path' do
|
15
|
+
layout = Usmu::Layout.new(configuration, 'html.slim', 'slim', "head\nbody", {})
|
16
|
+
expect(layout.respond_to? :input_path).to eq(true)
|
17
|
+
expect(layout.input_path).to eq('layouts/html.slim')
|
26
18
|
end
|
27
19
|
end
|
data/test/spec/page_spec.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
|
-
require 'rspec'
|
2
1
|
require 'support/shared_layout'
|
3
2
|
require 'usmu/page'
|
4
3
|
|
5
4
|
RSpec.describe Usmu::Page do
|
6
5
|
it_behaves_like 'an embeddable layout'
|
7
6
|
|
8
|
-
let(:configuration) { Usmu::Configuration.
|
7
|
+
let(:configuration) { Usmu::Configuration.from_hash({}) }
|
9
8
|
|
10
9
|
it 'uses the \'source\' folder' do
|
11
|
-
page = Usmu::Page.new(configuration, 'index.md')
|
12
|
-
|
13
|
-
|
10
|
+
page = Usmu::Page.new(configuration, 'index.md', 'md', '# test', {})
|
11
|
+
expect(page.send :content_path).to eq('src')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has an input path' do
|
15
|
+
page = Usmu::Page.new(configuration, 'index.md', 'md', '# test', {})
|
16
|
+
expect(page.respond_to? :input_path).to eq(true)
|
17
|
+
expect(page.input_path).to eq('src/index.md')
|
14
18
|
end
|
15
19
|
end
|