usmu 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.cane +4 -0
  3. data/.gitignore +17 -0
  4. data/.rspec +5 -0
  5. data/.travis.yml +34 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +4 -0
  8. data/Gemfile-jruby +4 -0
  9. data/LICENSE.md +22 -0
  10. data/README.md +50 -0
  11. data/Rakefile +61 -0
  12. data/bin/usmu +10 -0
  13. data/cucumber.yml +2 -0
  14. data/lib/usmu.rb +12 -0
  15. data/lib/usmu/configuration.rb +83 -0
  16. data/lib/usmu/layout.rb +166 -0
  17. data/lib/usmu/page.rb +19 -0
  18. data/lib/usmu/site_generator.rb +82 -0
  19. data/lib/usmu/static_file.rb +39 -0
  20. data/lib/usmu/ui.rb +7 -0
  21. data/lib/usmu/ui/console.rb +26 -0
  22. data/lib/usmu/version.rb +5 -0
  23. data/test/expected-site/default.html +3 -0
  24. data/test/expected-site/embedded.html +15 -0
  25. data/test/expected-site/index.html +14 -0
  26. data/test/expected-site/robots.txt +1 -0
  27. data/test/features/generator.feature +10 -0
  28. data/test/features/step_definitions/step_general.rb +18 -0
  29. data/test/site/content/default.md +3 -0
  30. data/test/site/content/embedded.md +1 -0
  31. data/test/site/content/embedded.meta.yml +2 -0
  32. data/test/site/content/index.md +3 -0
  33. data/test/site/content/index.meta.yml +3 -0
  34. data/test/site/content/robots.txt +1 -0
  35. data/test/site/layouts/embedded.meta.yml +2 -0
  36. data/test/site/layouts/embedded.slim +2 -0
  37. data/test/site/layouts/html.meta.yml +2 -0
  38. data/test/site/layouts/html.slim +8 -0
  39. data/test/site/usmu.yml +15 -0
  40. data/test/spec/configuration_spec.rb +51 -0
  41. data/test/spec/layout_spec.rb +27 -0
  42. data/test/spec/page_spec.rb +15 -0
  43. data/test/spec/site_generator_spec.rb +32 -0
  44. data/test/spec/spec_helper.rb +85 -0
  45. data/test/spec/static_file_spec.rb +20 -0
  46. data/test/spec/support/shared_layout.rb +111 -0
  47. data/usmu-jruby.gemspec +34 -0
  48. data/usmu.gemspec +35 -0
  49. metadata +291 -0
@@ -0,0 +1,19 @@
1
+
2
+ module Usmu
3
+ # Represents a page in the source directory of the website.
4
+ class Page < Layout
5
+ protected
6
+
7
+ # @!attribute [r] content_path
8
+ # @return [string] the base path to the files used by this class.
9
+ #
10
+ # Returns the base path to the files used by this class.
11
+ #
12
+ # This folder should be the parent folder for the file named by the name attribute.
13
+ #
14
+ # @see #name
15
+ def content_path
16
+ @configuration.source_path
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,82 @@
1
+ require 'fileutils'
2
+
3
+ module Usmu
4
+ # This is the class that brings everything together to generate a new website.
5
+ class SiteGenerator
6
+ # @param [Usmu::Configuration] configuration The configuration to use for generating the website
7
+ def initialize(configuration)
8
+ @configuration = configuration
9
+ end
10
+
11
+ # @!attribute [r] layouts
12
+ # @return [Array<Usmu::Layout>] a list of layouts available in this website.
13
+ def layouts
14
+ get_renderables @configuration.layouts_path, true
15
+ end
16
+
17
+ # @!attribute [r] renderables
18
+ # @return [Array<Usmu::StaticFile>] a list of renderable files from the source folder.
19
+ # will be a subclass of this class.
20
+ # @see Usmu::StaticFile
21
+ #
22
+ # Returns a list of renderable files from the source folder.
23
+ #
24
+ # The only guarantee made for individual files is that they will conform to the interface defined by
25
+ # Usmu::StaticFile and thus be renderable, however most files will be one of the subclasses of that class.
26
+ def renderables
27
+ get_renderables @configuration.source_path, false
28
+ end
29
+
30
+ # @!attribute [r] pages
31
+ # @return [Array<Usmu::Page>] a list of pages from the source folder. This is any file in the source folder which
32
+ # is not static.
33
+ def pages
34
+ renderables.select {|r| r.class.name != 'Usmu::StaticFile'}
35
+ end
36
+
37
+ # @!attribute [r] files
38
+ # @return [Array<Usmu::StaticFile>] a list of static files from the source folder.
39
+ def files
40
+ renderables.select {|r| r.class.name == 'Usmu::StaticFile'}
41
+ end
42
+
43
+ # Generate the website according to the configuration given.
44
+ #
45
+ # @return [void]
46
+ def generate
47
+ renderables.each do |page|
48
+ file = File.join(@configuration.destination_path, page.output_filename)
49
+ directory = File.dirname(file)
50
+
51
+ unless File.directory?(directory)
52
+ FileUtils.mkdir_p(directory)
53
+ end
54
+
55
+ File.write file, page.render
56
+ end
57
+ nil
58
+ 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
+ end
82
+ end
@@ -0,0 +1,39 @@
1
+
2
+ module Usmu
3
+ # Represents a static file which should be transferred to the destination unchanged. This also acts as the base
4
+ # class for all layouts and page types. The basic interface defined here is used to process all types of files.
5
+ class StaticFile
6
+ # @!attribute [r] name
7
+ # @return [String] the name of the file in the source directory
8
+ attr_reader :name
9
+
10
+ # @param configuration [Usmu::Configuration] The configuration for the website we're generating.
11
+ # @param name [String] The name of the file in the source directory.
12
+ # @param type [String] The type of template to use with the file. Not used for StaticFile.
13
+ # Used for testing purposes.
14
+ # @param content [String] The content of the file. Used for testing purposes.
15
+ # @param metadata [String] The metadata for the file. Used for testing purposes.
16
+ def initialize(configuration, name, type = nil, content = nil, metadata = nil)
17
+ @configuration = configuration
18
+ @name = name
19
+ @type = type
20
+ @content = content
21
+ end
22
+
23
+ # Renders the file with any templating language required and returns the result
24
+ #
25
+ # @param variables [Hash] Variables to be used in the template.
26
+ # @return [String] The rendered file
27
+ def render(variables = {})
28
+ @content || File.read(File.join(@configuration.source_path, @name))
29
+ end
30
+
31
+ # @!attribute [r] output_filename
32
+ # @return [String] the filename to use in the output directory.
33
+ #
34
+ # Returns the filename to use for the output directory with any modifications to the input filename required.
35
+ def output_filename
36
+ @name
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ require 'usmu'
2
+
3
+ module Usmu
4
+ # This module is for all the different UI's. If you implement a custom UI it should be added here.
5
+ module Ui
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ require 'usmu'
2
+ require 'trollop'
3
+
4
+ module Usmu
5
+ module Ui
6
+ # This is the CLI UI controller. This is initialised by the usmu binary to control the generation process.
7
+ class Console
8
+ # @!attribute [r] configuration
9
+ # @return [Usmu::Configuration] the configuration for the site we will generate.
10
+ attr_reader :configuration
11
+
12
+ # @param [Array<String>] args Command line arguments. Typically ARGV should be passed here.
13
+ def initialize(args)
14
+ @args = args
15
+ @configuration = Usmu::Configuration.from_file(File.join(args[0] || '.', 'usmu.yml'))
16
+ end
17
+
18
+ # This will run the command as setup in `#initialize`
19
+ #
20
+ # @return [void]
21
+ def execute
22
+ Usmu::SiteGenerator.new(@configuration).generate
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+
2
+ module Usmu
3
+ # The current versions string for the gem
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,3 @@
1
+ <h1>Default output</h1>
2
+
3
+ <p>No metadata for this file. Uh oh!</p>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Default Title | Testing website</title>
5
+ </head>
6
+ <body>
7
+ <div id="content">
8
+ <div class="embedded">
9
+ <h1>Embedded test</h1>
10
+
11
+ </div>
12
+
13
+ </div>
14
+ </body>
15
+ </html>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Default Title | Testing website</title>
5
+ </head>
6
+ <body>
7
+ <div id="content">
8
+ <h1>Test index</h1>
9
+
10
+ <p>This is some test content</p>
11
+
12
+ </div>
13
+ </body>
14
+ </html>
@@ -0,0 +1 @@
1
+ User-agent: *
@@ -0,0 +1,10 @@
1
+
2
+ Feature:
3
+ In order to create a website
4
+ As a user
5
+ I would like to generate a static website
6
+
7
+ Scenario:
8
+ Given I have a site at "test/site"
9
+ When I generate the site
10
+ Then the destination directory should match "test/expected-site"
@@ -0,0 +1,18 @@
1
+ require 'usmu/ui/console'
2
+ require 'open3'
3
+
4
+ Given(/^I have a site at "([^"]*)"$/) do |location|
5
+ @site = Usmu::Ui::Console.new([location])
6
+ end
7
+
8
+ When(/^I generate the site$/) do
9
+ @site.execute
10
+ end
11
+
12
+ Then(/^the destination directory should match "([^"]*)"$/) 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
@@ -0,0 +1,3 @@
1
+ # Default output
2
+
3
+ No metadata for this file. Uh oh!
@@ -0,0 +1 @@
1
+ # Embedded test
@@ -0,0 +1,2 @@
1
+ ---
2
+ layout: embedded
@@ -0,0 +1,3 @@
1
+ # Test index
2
+
3
+ This is some test content
@@ -0,0 +1,3 @@
1
+ ---
2
+ title: Index page
3
+ layout: html
@@ -0,0 +1 @@
1
+ User-agent: *
@@ -0,0 +1,2 @@
1
+ ---
2
+ layout: html
@@ -0,0 +1,2 @@
1
+ .embedded
2
+ | #{{content}}
@@ -0,0 +1,2 @@
1
+ ---
2
+ title: Default Title
@@ -0,0 +1,8 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title #{title} | #{site['title']}
5
+
6
+ body
7
+ #content
8
+ | #{{content}}
@@ -0,0 +1,15 @@
1
+ ---
2
+
3
+ source: content
4
+ destination: site
5
+ title: Testing website
6
+
7
+ default meta:
8
+ title: Test website
9
+ layout: html
10
+
11
+ slim:
12
+ :pretty: true
13
+
14
+ kramdown:
15
+ :auto_ids: false
@@ -0,0 +1,51 @@
1
+ require 'rspec'
2
+ require 'usmu/configuration'
3
+
4
+ RSpec.describe Usmu::Configuration do
5
+ context 'should prepend configuration folder' do
6
+ before do
7
+ hash = {
8
+ 'source' => 'source',
9
+ 'destination' => 'destination',
10
+ 'layouts' => 'templates',
11
+ }
12
+ @configuration = Usmu::Configuration.from_hash(hash, 'test/usmu.yaml')
13
+ end
14
+
15
+ it 'to source' do
16
+ expect(@configuration.source_path).to eq('test/source')
17
+ end
18
+
19
+ it 'to destination' do
20
+ expect(@configuration.destination_path).to eq('test/destination')
21
+ end
22
+
23
+ it 'to layouts' do
24
+ expect(@configuration.layouts_path).to eq('test/templates')
25
+ end
26
+ end
27
+
28
+ context 'should have a default path' do
29
+ before do
30
+ hash = {}
31
+ @configuration = Usmu::Configuration.from_hash(hash)
32
+ end
33
+
34
+ it 'for source' do
35
+ expect(@configuration.source_path).to eq('src')
36
+ end
37
+
38
+ it 'for destination' do
39
+ expect(@configuration.destination_path).to eq('site')
40
+ end
41
+
42
+ it 'for layouts' do
43
+ expect(@configuration.layouts_path).to eq('layouts')
44
+ end
45
+ end
46
+
47
+ it 'should remember arbitrary configuration' do
48
+ configuration = Usmu::Configuration.from_hash({:test => 'foo'})
49
+ expect(configuration[:test]).to eq('foo')
50
+ end
51
+ end
@@ -0,0 +1,27 @@
1
+ require 'rspec'
2
+ require 'support/shared_layout'
3
+ require 'usmu/layout'
4
+
5
+ RSpec.describe Usmu::Layout do
6
+ it_behaves_like 'an embeddable layout'
7
+
8
+ let(:configuration) { Usmu::Configuration.from_file('test/site/usmu.yml') }
9
+
10
+ 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
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ require 'rspec'
2
+ require 'support/shared_layout'
3
+ require 'usmu/page'
4
+
5
+ RSpec.describe Usmu::Page do
6
+ it_behaves_like 'an embeddable layout'
7
+
8
+ let(:configuration) { Usmu::Configuration.from_file('test/site/usmu.yml') }
9
+
10
+ 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'))
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ require 'rspec'
2
+ require 'usmu/site_generator'
3
+
4
+ RSpec.describe Usmu::SiteGenerator do
5
+ let(:configuration) { Usmu::Configuration.from_file('test/site/usmu.yml') }
6
+ let(:generator) { Usmu::SiteGenerator.new(configuration) }
7
+
8
+ it 'should have layouts' do
9
+ expect(generator.respond_to? :layouts).to eq(true)
10
+ expect(generator.layouts.map {|l| l.name}.sort).to eq(%w{embedded.slim html.slim})
11
+ end
12
+
13
+ it 'should have a list of renderable items' do
14
+ expect(generator.respond_to? :renderables).to eq(true)
15
+ expect(generator.renderables.map {|r| r.name}.sort).to eq(%w{default.md embedded.md index.md robots.txt})
16
+ end
17
+
18
+ it 'should have pages' do
19
+ expect(generator.respond_to? :pages).to eq(true)
20
+ expect(generator.pages.map {|p| p.name}.sort).to eq(%w{default.md embedded.md index.md})
21
+ end
22
+
23
+ it 'should have files' do
24
+ expect(generator.respond_to? :files).to eq(true)
25
+ expect(generator.files.map {|f| f.name}.sort).to eq(%w{robots.txt})
26
+ end
27
+
28
+ it 'should be able to generate a site' do
29
+ expect(generator.respond_to? :generate).to eq(true)
30
+ # Further testing is exercised at the system integration level in the cukes.
31
+ end
32
+ end