brandish 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rubocop.yml +41 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +9 -0
- data/bin/brandish +16 -0
- data/brandish.gemspec +39 -0
- data/defaults/templates/html.liquid +39 -0
- data/lib/brandish.rb +51 -0
- data/lib/brandish/application.rb +163 -0
- data/lib/brandish/application/bench_command.rb +96 -0
- data/lib/brandish/application/build_command.rb +73 -0
- data/lib/brandish/application/initialize_command.rb +83 -0
- data/lib/brandish/application/serve_command.rb +150 -0
- data/lib/brandish/configure.rb +196 -0
- data/lib/brandish/configure/dsl.rb +135 -0
- data/lib/brandish/configure/dsl/form.rb +136 -0
- data/lib/brandish/configure/form.rb +32 -0
- data/lib/brandish/errors.rb +65 -0
- data/lib/brandish/execute.rb +26 -0
- data/lib/brandish/markup.rb +10 -0
- data/lib/brandish/markup/redcarpet.rb +14 -0
- data/lib/brandish/markup/redcarpet/format.rb +127 -0
- data/lib/brandish/markup/redcarpet/html.rb +95 -0
- data/lib/brandish/parser.rb +26 -0
- data/lib/brandish/parser/main.rb +237 -0
- data/lib/brandish/parser/node.rb +89 -0
- data/lib/brandish/parser/node/block.rb +98 -0
- data/lib/brandish/parser/node/command.rb +102 -0
- data/lib/brandish/parser/node/pair.rb +42 -0
- data/lib/brandish/parser/node/root.rb +83 -0
- data/lib/brandish/parser/node/string.rb +18 -0
- data/lib/brandish/parser/node/text.rb +114 -0
- data/lib/brandish/path_set.rb +163 -0
- data/lib/brandish/processor.rb +47 -0
- data/lib/brandish/processor/base.rb +144 -0
- data/lib/brandish/processor/block.rb +47 -0
- data/lib/brandish/processor/command.rb +47 -0
- data/lib/brandish/processor/context.rb +169 -0
- data/lib/brandish/processor/descend.rb +32 -0
- data/lib/brandish/processor/inline.rb +49 -0
- data/lib/brandish/processor/name_filter.rb +67 -0
- data/lib/brandish/processor/pair_filter.rb +96 -0
- data/lib/brandish/processors.rb +26 -0
- data/lib/brandish/processors/all.rb +19 -0
- data/lib/brandish/processors/all/comment.rb +29 -0
- data/lib/brandish/processors/all/embed.rb +56 -0
- data/lib/brandish/processors/all/if.rb +109 -0
- data/lib/brandish/processors/all/import.rb +95 -0
- data/lib/brandish/processors/all/literal.rb +42 -0
- data/lib/brandish/processors/all/verify.rb +47 -0
- data/lib/brandish/processors/common.rb +20 -0
- data/lib/brandish/processors/common/asset.rb +118 -0
- data/lib/brandish/processors/common/asset/paths.rb +93 -0
- data/lib/brandish/processors/common/group.rb +67 -0
- data/lib/brandish/processors/common/header.rb +86 -0
- data/lib/brandish/processors/common/markup.rb +127 -0
- data/lib/brandish/processors/common/output.rb +73 -0
- data/lib/brandish/processors/html.rb +18 -0
- data/lib/brandish/processors/html/group.rb +33 -0
- data/lib/brandish/processors/html/header.rb +46 -0
- data/lib/brandish/processors/html/markup.rb +131 -0
- data/lib/brandish/processors/html/output.rb +62 -0
- data/lib/brandish/processors/html/output/document.rb +127 -0
- data/lib/brandish/processors/html/script.rb +64 -0
- data/lib/brandish/processors/html/script/babel.rb +48 -0
- data/lib/brandish/processors/html/script/coffee.rb +47 -0
- data/lib/brandish/processors/html/script/vanilla.rb +45 -0
- data/lib/brandish/processors/html/style.rb +82 -0
- data/lib/brandish/processors/html/style/highlight.rb +89 -0
- data/lib/brandish/processors/html/style/sass.rb +64 -0
- data/lib/brandish/processors/html/style/vanilla.rb +71 -0
- data/lib/brandish/processors/latex.rb +15 -0
- data/lib/brandish/processors/latex/markup.rb +47 -0
- data/lib/brandish/scanner.rb +64 -0
- data/lib/brandish/version.rb +9 -0
- data/templates/initialize/Gemfile.tt +14 -0
- data/templates/initialize/brandish.config.rb.tt +49 -0
- metadata +296 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
class Application
|
6
|
+
# The bench command. This builds the project in the set directory,
|
7
|
+
# and benchmarks. This is for debugging.
|
8
|
+
class BenchCommand
|
9
|
+
include Commander::Methods
|
10
|
+
|
11
|
+
# Defines the command on the given application. This sets the important
|
12
|
+
# data information for the command, for use for the help output.
|
13
|
+
#
|
14
|
+
# @param application [Application]
|
15
|
+
# @param command [Commander::Command]
|
16
|
+
# @return [void]
|
17
|
+
def self.define(application, command)
|
18
|
+
command.syntax = "brandish build"
|
19
|
+
command.option "-o", "--only NAMES", [::String]
|
20
|
+
command.option "-p", "--path PATH", ::String
|
21
|
+
command.option "-n", "--name NAME", ::String
|
22
|
+
|
23
|
+
command.action { |_, o| call(application, o.__hash__) }
|
24
|
+
end
|
25
|
+
|
26
|
+
# The default options for the build command.
|
27
|
+
#
|
28
|
+
# @return [{::Symbol => ::Object}]
|
29
|
+
DEFAULTS = { only: :all, path: "profile", name: "default" }.freeze
|
30
|
+
|
31
|
+
# Performs the build command. This initializes the command, and
|
32
|
+
# calls {#call}.
|
33
|
+
#
|
34
|
+
# @param application [Application] The application.
|
35
|
+
# @param options [{::Symbol => ::Object}] The options for the command.
|
36
|
+
#
|
37
|
+
# @return [void]
|
38
|
+
def self.call(application, options)
|
39
|
+
new(application, options).call
|
40
|
+
end
|
41
|
+
|
42
|
+
# Initialize the build command.
|
43
|
+
#
|
44
|
+
# @params (see {.call})
|
45
|
+
def initialize(application, options)
|
46
|
+
@application = application
|
47
|
+
@options = DEFAULTS.merge(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Performs the build. First, it loads the configuration file for the
|
51
|
+
# build. Then, it performs the build, calling {Configure#build} with
|
52
|
+
# the `:only` filter provided by the options. This uses a Commander
|
53
|
+
# native called `progress` to make it look nice on the output.
|
54
|
+
#
|
55
|
+
# @return [void]
|
56
|
+
def call
|
57
|
+
require "ruby-prof"
|
58
|
+
require "benchmark"
|
59
|
+
|
60
|
+
@configure = @application.load_configuration_file
|
61
|
+
@path = ::Pathname.new(@options[:path]).expand_path(Dir.pwd)
|
62
|
+
@path.mkpath
|
63
|
+
say "=> Beginning build..."
|
64
|
+
|
65
|
+
result = nil
|
66
|
+
time = Benchmark.measure { result = RubyProf.profile { perform_build } }
|
67
|
+
say "-> Build ended, time: #{time}"
|
68
|
+
say "=> Outputting profile..."
|
69
|
+
|
70
|
+
result.eliminate_methods!(method_eleminations)
|
71
|
+
output_profile(result)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def output_profile(result)
|
77
|
+
printer = RubyProf::MultiPrinter.new(result)
|
78
|
+
printer.print(path: @path, profile: @options[:name])
|
79
|
+
say "-> Profile output to `#{@options[:path]}'!"
|
80
|
+
end
|
81
|
+
|
82
|
+
def perform_build
|
83
|
+
@configure.build(@options[:only]).each(&:call)
|
84
|
+
rescue => e
|
85
|
+
say_error "!> Error while building!"
|
86
|
+
say_error "-> Received exception: #{e.class}: #{e.message}"
|
87
|
+
e.backtrace.each { |l| say_warning "\t-> in #{l}" } if @options[:trace]
|
88
|
+
exit!
|
89
|
+
end
|
90
|
+
|
91
|
+
def method_eleminations
|
92
|
+
[/\A(Set|Class|Array|Enumerable)#/]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
class Application
|
6
|
+
# The build command. This just builds the project in the set directory.
|
7
|
+
class BuildCommand
|
8
|
+
include Commander::Methods
|
9
|
+
|
10
|
+
# The description for the build command.
|
11
|
+
#
|
12
|
+
# @return [::String]
|
13
|
+
COMMAND_DESCRIPTION =
|
14
|
+
"Builds an existing Brandish project. If no directory is specified " \
|
15
|
+
" using --directory or -d, it defaults to the current directory."
|
16
|
+
|
17
|
+
# Defines the command on the given application. This sets the important
|
18
|
+
# data information for the command, for use for the help output.
|
19
|
+
#
|
20
|
+
# @param application [Application]
|
21
|
+
# @param command [Commander::Command]
|
22
|
+
# @return [void]
|
23
|
+
def self.define(application, command)
|
24
|
+
command.syntax = "brandish build"
|
25
|
+
command.description = COMMAND_DESCRIPTION
|
26
|
+
command.option "-o", "--only NAMES", [::String],
|
27
|
+
"Which forms to build. If this is omitted, it defaults to all."
|
28
|
+
|
29
|
+
command.action { |_, o| call(application, o.__hash__) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# The default options for the build command.
|
33
|
+
#
|
34
|
+
# @return [{::Symbol => ::Object}]
|
35
|
+
DEFAULTS = { only: :all }.freeze
|
36
|
+
|
37
|
+
# Performs the build command. This initializes the command, and
|
38
|
+
# calls {#call}.
|
39
|
+
#
|
40
|
+
# @param application [Application] The application.
|
41
|
+
# @param options [{::Symbol => ::Object}] The options for the command.
|
42
|
+
#
|
43
|
+
# @return [void]
|
44
|
+
def self.call(application, options)
|
45
|
+
new(application, options).call
|
46
|
+
end
|
47
|
+
|
48
|
+
# Initialize the build command.
|
49
|
+
#
|
50
|
+
# @params (see {.call})
|
51
|
+
def initialize(application, options)
|
52
|
+
@application = application
|
53
|
+
@options = DEFAULTS.merge(options)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Performs the build. First, it loads the configuration file for the
|
57
|
+
# build. Then, it performs the build, calling {Configure#build} with
|
58
|
+
# the `:only` filter provided by the options. This uses a Commander
|
59
|
+
# native called `progress` to make it look nice on the output.
|
60
|
+
#
|
61
|
+
# @return [void]
|
62
|
+
def call
|
63
|
+
configure = @application.load_configuration_file
|
64
|
+
@application.progress(configure.build(@options[:only]).to_a, &:call)
|
65
|
+
rescue => e
|
66
|
+
say_error "\n!> Error while building!"
|
67
|
+
say_error "!> #{e.location}" if e.respond_to?(:location)
|
68
|
+
say_error "!> Received exception: #{e.class}: #{e.message}"
|
69
|
+
e.backtrace.each { |l| say_warning "\t~> in #{l}" } if @options[:trace]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "thor"
|
5
|
+
require "rubygems"
|
6
|
+
|
7
|
+
module Brandish
|
8
|
+
class Application
|
9
|
+
# The initialize command for the application. This creates a new project
|
10
|
+
# at a given directory. The Brandish project is placed at
|
11
|
+
# `directory`/`name`.
|
12
|
+
class InitializeCommand
|
13
|
+
include Thor::Base
|
14
|
+
include Thor::Actions
|
15
|
+
|
16
|
+
# The description for the initialize command.
|
17
|
+
#
|
18
|
+
# @return [::String]
|
19
|
+
COMMAND_DESCRIPTION =
|
20
|
+
"Creates a new Brandish project. The name given should not contain " \
|
21
|
+
"any path seperators - if a brandish project needs to be placed in " \
|
22
|
+
"a seperate directory, use the --directory (or -d) option."
|
23
|
+
|
24
|
+
# The source root for the templates used by Thor for initialization.
|
25
|
+
#
|
26
|
+
# @return [::String]
|
27
|
+
def self.source_root
|
28
|
+
File.expand_path("../../../../templates/initialize", __FILE__)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Defines the command on the given application. This sets the important
|
32
|
+
# data information for the command, for use for the help output.
|
33
|
+
#
|
34
|
+
# @param application [Application]
|
35
|
+
# @param command [Commander::Command]
|
36
|
+
# @return [void]
|
37
|
+
def self.define(application, command)
|
38
|
+
command.syntax = "brandish initialize NAME"
|
39
|
+
command.description = COMMAND_DESCRIPTION
|
40
|
+
|
41
|
+
command.action { |a, o| call(application, a, o.__hash__) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Performs the initialize command. Since this class uses Thor, this
|
45
|
+
# performs some setup to interface with the Thor class.
|
46
|
+
#
|
47
|
+
# @param application [Application]
|
48
|
+
# @param arguments [<::String>] The arguments passed to the initialize
|
49
|
+
# command. This should contain one value - the name.
|
50
|
+
# @param _options [Hash] The options for the command. Since this command
|
51
|
+
# takes no specific options, this is ignored.
|
52
|
+
# @return [void]
|
53
|
+
def self.call(application, arguments, _options)
|
54
|
+
name = arguments[0]
|
55
|
+
directory = application.directory / name
|
56
|
+
new([], { name: name }, destination_root: directory).call
|
57
|
+
end
|
58
|
+
|
59
|
+
# Performs the initialize command, setting up the project.
|
60
|
+
#
|
61
|
+
# @return [void]
|
62
|
+
def call
|
63
|
+
template "brandish.config.rb"
|
64
|
+
%w(source source/assets source/assets/styles source/assets/scripts
|
65
|
+
templates output).each { |d| empty_directory(d) }
|
66
|
+
template "Gemfile"
|
67
|
+
inside(".") { run "bundle install" }
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
# The approximate recommendation for the current running version of
|
73
|
+
# Brandish. This is used to set up a requirement for Brandish in both
|
74
|
+
# the Gemfile and the `brandish.config.rb` file.
|
75
|
+
#
|
76
|
+
# @return [::String]
|
77
|
+
def approx
|
78
|
+
@approx ||=
|
79
|
+
Gem::Version.new(Brandish::VERSION).approximate_recommendation
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Brandish
|
5
|
+
class Application
|
6
|
+
# The serve command. This builds, and then serves, an existing Brandish
|
7
|
+
# project. This watches the source. If it detects a change, it rebuilds.
|
8
|
+
class ServeCommand
|
9
|
+
include Commander::Methods
|
10
|
+
|
11
|
+
# The description for the serve command.
|
12
|
+
#
|
13
|
+
# @return [::String]
|
14
|
+
COMMAND_DESCRIPTION = "Builds, and serves, an existing Brandish project."
|
15
|
+
|
16
|
+
# Defines the command on the given application. This sets the important
|
17
|
+
# data information for the command, for use for the help output.
|
18
|
+
#
|
19
|
+
# @param application [Application]
|
20
|
+
# @param command [Commander::Command]
|
21
|
+
# @return [void]
|
22
|
+
def self.define(application, command)
|
23
|
+
command.syntax = "branish serve"
|
24
|
+
command.description = COMMAND_DESCRIPTION
|
25
|
+
command.option "-p", "--port PORT", ::Integer, "The port to listen on"
|
26
|
+
command.option "-o", "--only NAMES", [::String], "The forms to build"
|
27
|
+
command.option "--verbose", "Whether or not to be verbose in the output"
|
28
|
+
|
29
|
+
command.action { |_, o| call(application, o.__hash__) }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Performs the serve command. This initializes the command, and
|
33
|
+
# calls {#call}.
|
34
|
+
#
|
35
|
+
# @param application [Application] The application.
|
36
|
+
# @param options [{::Symbol => ::Object}] The options for the command.
|
37
|
+
#
|
38
|
+
# @return [void]
|
39
|
+
def self.call(application, options)
|
40
|
+
puts "Running with options #{options.inspect}..."
|
41
|
+
new(application, options).call
|
42
|
+
end
|
43
|
+
|
44
|
+
# The default options for the serve command.
|
45
|
+
#
|
46
|
+
# @return [{::Symbol => ::Object}]
|
47
|
+
DEFAULTS = { only: :all, show_build: false }.freeze
|
48
|
+
|
49
|
+
# Initialize the serve command.
|
50
|
+
#
|
51
|
+
# @params (see {.call})
|
52
|
+
def initialize(application, options)
|
53
|
+
@application = application
|
54
|
+
@options = DEFAULTS.merge(options)
|
55
|
+
@port = @options.fetch(:port, (ENV["PORT"] || "3000").to_i)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Performs the serve command. It first loads the configuration file,
|
59
|
+
# then calls {#start_webserver}, followed by {#start_buildserver}. Once
|
60
|
+
# both servers are setup, it calls {#wait_on_servers}.
|
61
|
+
#
|
62
|
+
# @return [void]
|
63
|
+
def call
|
64
|
+
@configuration = @application.load_configuration_file
|
65
|
+
say "=> Beginning serve..."
|
66
|
+
start_webserver
|
67
|
+
start_buildserver
|
68
|
+
color "\r=> Ready and waiting! ", :erase_line, :green
|
69
|
+
wait_on_servers
|
70
|
+
rescue StandardError, ScriptError => e
|
71
|
+
# Whenever we receive a general error, which only occurs while setup,
|
72
|
+
# we complain, and pass up the exception.
|
73
|
+
say_error "\n!> Received exception: #{e.class}: #{e.message}"
|
74
|
+
e.backtrace.each { |l| say_warning "\t-> in #{l}" } if @options[:trace]
|
75
|
+
fail
|
76
|
+
rescue SignalException, NoMemoryError, SystemExit, SystemStackError => e
|
77
|
+
# Whenever we receive a signal, or an unrecoverable error, we kill
|
78
|
+
# the servers and complain. These exceptions occur on the main thread,
|
79
|
+
# and so we handle them here.
|
80
|
+
say_warning "\n!> Received exception: #{e.class}: #{e.message}"
|
81
|
+
say_ok "\n-> Received termination, shutting down..."
|
82
|
+
kill_webserver
|
83
|
+
kill_buildserver
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def start_webserver
|
89
|
+
say "-> Setting up web server on port #{@port}..."
|
90
|
+
log_file = @options[:verbose] ? $stdout : StringIO.new
|
91
|
+
log = WEBrick::Log.new(log_file)
|
92
|
+
access_log = [[log_file, WEBrick::AccessLog::COMBINED_LOG_FORMAT]]
|
93
|
+
data = { Port: @port, DocumentRoot: @configuration.output.to_s,
|
94
|
+
Logger: log, AccessLog: access_log }
|
95
|
+
|
96
|
+
@webserver = Thread.start { WEBrick::HTTPServer.new(data).start }
|
97
|
+
end
|
98
|
+
|
99
|
+
def print(a, *)
|
100
|
+
fail if a.is_a?(::IO)
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
104
|
+
def start_buildserver
|
105
|
+
perform_build
|
106
|
+
say "\n"
|
107
|
+
say "-> Setting up listen server..."
|
108
|
+
source_build_server = Listen.to(*listen_paths) { perform_build }
|
109
|
+
config_build_server = Listen.to(@application.directory.to_s) do
|
110
|
+
say "-> Configuration file changed, updating..."
|
111
|
+
@configuration = @application.load_configuration_file!
|
112
|
+
perform_build
|
113
|
+
end
|
114
|
+
config_build_server.only(/#{Regexp.escape(@application.config_file.to_s)}\z/)
|
115
|
+
|
116
|
+
@buildservers = [source_build_server, config_build_server].each(&:start)
|
117
|
+
end
|
118
|
+
|
119
|
+
def perform_build
|
120
|
+
builds = @configuration.build!(@options[:only])
|
121
|
+
color "\r~> Building... ", :erase_line, :clear
|
122
|
+
builds.each(&:call)
|
123
|
+
color "\r=> Build completed at #{Time.now.strftime('%T.%L')}! ", :erase_line, :green
|
124
|
+
|
125
|
+
rescue => e
|
126
|
+
say_error "\n!> Error while building!"
|
127
|
+
say_error "!> #{e.location}" if e.respond_to?(:location)
|
128
|
+
say_error "-> Received exception: #{e.class}: #{e.message}"
|
129
|
+
e.backtrace.each { |l| say_warning "\t-> in #{l}" } if @options[:trace]
|
130
|
+
end
|
131
|
+
|
132
|
+
def kill_webserver
|
133
|
+
@webserver&.kill
|
134
|
+
end
|
135
|
+
|
136
|
+
def kill_buildserver
|
137
|
+
@buildservers&.each { |b| b&.stop }
|
138
|
+
end
|
139
|
+
|
140
|
+
def wait_on_servers
|
141
|
+
@webserver.join
|
142
|
+
end
|
143
|
+
|
144
|
+
def listen_paths
|
145
|
+
(@configuration.sources.to_a + @configuration.templates.to_a)
|
146
|
+
.select(&:directory?).map(&:to_s)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rubygems" # for Gem::Requirement
|
5
|
+
require "securerandom"
|
6
|
+
require "forwardable"
|
7
|
+
|
8
|
+
require "brandish/configure/dsl"
|
9
|
+
require "brandish/configure/form"
|
10
|
+
|
11
|
+
module Brandish
|
12
|
+
# This provides a central location for all configuration options. For the
|
13
|
+
# DSL, see {DSL}.
|
14
|
+
class Configure
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
# The options. These can be anything; predefined options are `:root`,
|
18
|
+
# `:source`, `:output`, and `:templates`; for more information on those,
|
19
|
+
# see {#root}, {#sources}, {#output}, and {#templates} for more information.
|
20
|
+
# These options should not be accessed directly, but rather through their
|
21
|
+
# accessors. Other options must be accessed through this.
|
22
|
+
#
|
23
|
+
# @return [{::Symbol => ::Object}]
|
24
|
+
attr_reader :options
|
25
|
+
|
26
|
+
# The forms that are defined on this configuration instance.
|
27
|
+
#
|
28
|
+
# @return [Set<Configure::Form>]
|
29
|
+
attr_reader :forms
|
30
|
+
|
31
|
+
# @!method [](key)
|
32
|
+
# Sets a key on the options. This gets an option that is used for all
|
33
|
+
# processors on the options.
|
34
|
+
#
|
35
|
+
# @param key [::Symbol, ::String] The key.
|
36
|
+
# @return [::Object]
|
37
|
+
# @!method []=(key, value)
|
38
|
+
# Sets a key on the options. This sets an option that is used for all
|
39
|
+
# processors on the options.
|
40
|
+
#
|
41
|
+
# @param key [::Symbol, ::String] The key.
|
42
|
+
# @param value [::Object] The value.
|
43
|
+
# @return [::Object]
|
44
|
+
# @!method fetch(key, default = CANARY, &block)
|
45
|
+
# Fetches a value at the given key, or provides a default if the key
|
46
|
+
# doesn't exist. If both a block and a default argument are given,
|
47
|
+
# the block form takes precedence.
|
48
|
+
#
|
49
|
+
# @overload fetch(key)
|
50
|
+
# Attempts to retrieve a value at the given key. If there is no
|
51
|
+
# key-value pair at the given key, it raises an error.
|
52
|
+
#
|
53
|
+
# @raise [KeyError] if the key isn't on the options.
|
54
|
+
# @param key [::Symbol, ::String] The key.
|
55
|
+
# @return [::Object] The value.
|
56
|
+
#
|
57
|
+
# @overload fetch(key, default)
|
58
|
+
# Attempts to retrieve a value at the given key. If there is no
|
59
|
+
# key-value pair at the given key, it returns the value given by
|
60
|
+
# `default`.
|
61
|
+
#
|
62
|
+
# @param key [::Symbol, ::String] The key.
|
63
|
+
# @param default [::Object] The default value.
|
64
|
+
# @return [::Object] The value, or the default value if there isn't
|
65
|
+
# one.
|
66
|
+
#
|
67
|
+
# @overload fetch(key, &block)
|
68
|
+
# attempts to retrieve a value at the given key. If there is no
|
69
|
+
# key-value pair at the given key, it yields.
|
70
|
+
#
|
71
|
+
# @yield if there is no corresponding key-value pair.
|
72
|
+
# @param key [::Symbol, ::String] The key.
|
73
|
+
# @return [::Object] The value, or the result of the block if there
|
74
|
+
# isn't one.
|
75
|
+
delegate [:[], :fetch] => :options
|
76
|
+
|
77
|
+
# Initializes the configure instance.
|
78
|
+
#
|
79
|
+
# @param root [::String, ::Pathname] The root for the project. This is
|
80
|
+
# used to determine the correct paths for the output directory, the
|
81
|
+
# sources directory, and the templates directory.
|
82
|
+
def initialize(root = Dir.pwd)
|
83
|
+
root = ::Pathname.new(root)
|
84
|
+
@options = { root: root, sources: PathSet.new, templates: PathSet.new }
|
85
|
+
@forms = ::Set.new
|
86
|
+
default_paths
|
87
|
+
end
|
88
|
+
|
89
|
+
# Retrieves the root path. This is where all of the other directories
|
90
|
+
# should be located, and where the configuration file should be
|
91
|
+
# located.
|
92
|
+
#
|
93
|
+
# @return [::Pathname]
|
94
|
+
def root
|
95
|
+
fetch(:root)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Retrieves the output path. This is where the outputs for all of the forms
|
99
|
+
# should be located.
|
100
|
+
#
|
101
|
+
# @return [::Pathname]
|
102
|
+
def output
|
103
|
+
fetch(:output) { root / "output" }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Retrieves the source path. This is where the sources for all of the
|
107
|
+
# documents in the Brandish project are located.
|
108
|
+
#
|
109
|
+
# @return [PathSet]
|
110
|
+
def sources
|
111
|
+
fetch(:sources)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Retrieves the templates path. This is where all of the templates for
|
115
|
+
# all of the forms should be located.
|
116
|
+
#
|
117
|
+
# @return [PathSet]
|
118
|
+
def templates
|
119
|
+
fetch(:templates)
|
120
|
+
end
|
121
|
+
|
122
|
+
# Given a set of forms to build, it yields blocks that can be called to
|
123
|
+
# build a form.
|
124
|
+
#
|
125
|
+
# @param which [::Symbol, <::Symbol>] If this is `:all`, all of the forms
|
126
|
+
# available are built; otherwise, it only builds the forms whose names
|
127
|
+
# are listed.
|
128
|
+
# @yield [build] Yields for each form that can be built.
|
129
|
+
# @yieldparam build [::Proc<void>] A block that can be called to build
|
130
|
+
# a form.
|
131
|
+
# @return [void]
|
132
|
+
def build(which = :all)
|
133
|
+
return to_enum(:build, which) unless block_given?
|
134
|
+
select_forms(which).each { |f| yield proc { f.build(self) } }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Given a set of forms to build, it yields blocks that can be called to
|
138
|
+
# build a form.
|
139
|
+
#
|
140
|
+
# This first clears the cache for file nodes.
|
141
|
+
#
|
142
|
+
# @param which [::Symbol, <::Symbol>] If this is `:all`, all of the forms
|
143
|
+
# available are built; otherwise, it only builds the forms whose names
|
144
|
+
# are listed.
|
145
|
+
# @yield [build] Yields for each form that can be built.
|
146
|
+
# @yieldparam build [::Proc<void>] A block that can be called to build
|
147
|
+
# a form.
|
148
|
+
# @return [void]
|
149
|
+
def build!(which = :all)
|
150
|
+
return to_enum(:build!, which) unless block_given?
|
151
|
+
@_roots = nil
|
152
|
+
select_forms(which).each { |f| yield proc { f.build(self) } }
|
153
|
+
end
|
154
|
+
|
155
|
+
# A cache for all of the root nodes. This is a regular hash; however, upon
|
156
|
+
# attempt to access an item that isn't already in the hash, it first
|
157
|
+
# parses the file at that item, and stores the result in the hash, returning
|
158
|
+
# the root node in the file. This is to cache files so that they do not
|
159
|
+
# get reparsed multiple times.
|
160
|
+
#
|
161
|
+
# @return [{::Pathname => Parser::Root}]
|
162
|
+
def roots
|
163
|
+
@_roots ||= ::Hash.new { |h, k| h[k] = parse_from(k) }
|
164
|
+
end
|
165
|
+
|
166
|
+
# Parses a file. This bypasses the cache.
|
167
|
+
#
|
168
|
+
# @param path [::Pathname] The path to the actual file. This should
|
169
|
+
# respond to `#read`. If this isn't a pathname, the short should be
|
170
|
+
# provided.
|
171
|
+
# @param short [::String] The short name of the file. This is used for
|
172
|
+
# location information.
|
173
|
+
# @return [Parser::Root]
|
174
|
+
def parse_from(path, short = path.relative_path_from(root))
|
175
|
+
Parser.new(Scanner.new(path.read, short, options).call).call
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def default_paths
|
181
|
+
sources <<
|
182
|
+
File.expand_path("../../../defaults/source", __FILE__) <<
|
183
|
+
(root / "source")
|
184
|
+
templates <<
|
185
|
+
File.expand_path("../../../defaults/templates", __FILE__) <<
|
186
|
+
(root / "templates")
|
187
|
+
end
|
188
|
+
|
189
|
+
def select_forms(which)
|
190
|
+
which = @forms.map(&:name) if which == :all || !which.is_a?(::Array) ||
|
191
|
+
which.empty?
|
192
|
+
which = which.to_set
|
193
|
+
@forms.select { |f| which.include?(f.name) }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|