usmu 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -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
- get_renderables @configuration.layouts_path, true
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
- get_renderables @configuration.source_path, false
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
@@ -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(File.join(@configuration.source_path, @name))
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
@@ -1,25 +1,53 @@
1
1
  require 'usmu'
2
- require 'trollop'
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
- attr_reader :configuration
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
- @args = args
15
- @configuration = Usmu::Configuration.from_file(File.join(args[0] || '.', 'usmu.yml'))
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
- # This will run the command as setup in `#initialize`
19
- #
20
- # @return [void]
21
- def execute
22
- Usmu::SiteGenerator.new(@configuration).generate
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,5 +1,5 @@
1
1
 
2
2
  module Usmu
3
- # The current versions string for the gem
4
- VERSION = '0.1.0'
3
+ # The current version string for the gem
4
+ VERSION = '0.2.0'
5
5
  end
@@ -1,3 +1,14 @@
1
- <h1>Default output</h1>
2
-
3
- <p>No metadata for this file. Uh oh!</p>
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>
@@ -1,2 +1,3 @@
1
1
  ---
2
2
  title: Default Title
3
+ layout: none
@@ -8,3 +8,4 @@ Feature:
8
8
  Given I have a site at "test/site"
9
9
  When I generate the site
10
10
  Then the destination directory should match "test/expected-site"
11
+ And the modification time for the input file "index.md" should match the output file "index.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
@@ -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.from_file('test/site/usmu.yml') }
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
- rendered = layout.render({'content' => 'test'})
13
- expect(rendered).to eq(<<-EOF)
14
- <!DOCTYPE html>
15
- <html>
16
- <head>
17
- <title>Default Title | Testing website</title>
18
- </head>
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
@@ -0,0 +1,8 @@
1
+
2
+ module Usmu
3
+ class MockPlugin
4
+ # This will be stubbed in with a mock if important. This is simply a placeholder to make sure #respond_to? replies
5
+ # correctly
6
+ def test(*args); end
7
+ end
8
+ end
@@ -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.from_file('test/site/usmu.yml') }
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
- rendered = page.render({})
13
- expect(rendered).to eq(File.read('test/expected-site/index.html'))
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
@@ -0,0 +1,5 @@
1
+ require 'usmu/plugin/core'
2
+
3
+ RSpec.describe Usmu::Plugin::Core do
4
+
5
+ end