brandish 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +41 -0
  5. data/.travis.yml +5 -0
  6. data/.yardopts +1 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +10 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +41 -0
  11. data/Rakefile +9 -0
  12. data/bin/brandish +16 -0
  13. data/brandish.gemspec +39 -0
  14. data/defaults/templates/html.liquid +39 -0
  15. data/lib/brandish.rb +51 -0
  16. data/lib/brandish/application.rb +163 -0
  17. data/lib/brandish/application/bench_command.rb +96 -0
  18. data/lib/brandish/application/build_command.rb +73 -0
  19. data/lib/brandish/application/initialize_command.rb +83 -0
  20. data/lib/brandish/application/serve_command.rb +150 -0
  21. data/lib/brandish/configure.rb +196 -0
  22. data/lib/brandish/configure/dsl.rb +135 -0
  23. data/lib/brandish/configure/dsl/form.rb +136 -0
  24. data/lib/brandish/configure/form.rb +32 -0
  25. data/lib/brandish/errors.rb +65 -0
  26. data/lib/brandish/execute.rb +26 -0
  27. data/lib/brandish/markup.rb +10 -0
  28. data/lib/brandish/markup/redcarpet.rb +14 -0
  29. data/lib/brandish/markup/redcarpet/format.rb +127 -0
  30. data/lib/brandish/markup/redcarpet/html.rb +95 -0
  31. data/lib/brandish/parser.rb +26 -0
  32. data/lib/brandish/parser/main.rb +237 -0
  33. data/lib/brandish/parser/node.rb +89 -0
  34. data/lib/brandish/parser/node/block.rb +98 -0
  35. data/lib/brandish/parser/node/command.rb +102 -0
  36. data/lib/brandish/parser/node/pair.rb +42 -0
  37. data/lib/brandish/parser/node/root.rb +83 -0
  38. data/lib/brandish/parser/node/string.rb +18 -0
  39. data/lib/brandish/parser/node/text.rb +114 -0
  40. data/lib/brandish/path_set.rb +163 -0
  41. data/lib/brandish/processor.rb +47 -0
  42. data/lib/brandish/processor/base.rb +144 -0
  43. data/lib/brandish/processor/block.rb +47 -0
  44. data/lib/brandish/processor/command.rb +47 -0
  45. data/lib/brandish/processor/context.rb +169 -0
  46. data/lib/brandish/processor/descend.rb +32 -0
  47. data/lib/brandish/processor/inline.rb +49 -0
  48. data/lib/brandish/processor/name_filter.rb +67 -0
  49. data/lib/brandish/processor/pair_filter.rb +96 -0
  50. data/lib/brandish/processors.rb +26 -0
  51. data/lib/brandish/processors/all.rb +19 -0
  52. data/lib/brandish/processors/all/comment.rb +29 -0
  53. data/lib/brandish/processors/all/embed.rb +56 -0
  54. data/lib/brandish/processors/all/if.rb +109 -0
  55. data/lib/brandish/processors/all/import.rb +95 -0
  56. data/lib/brandish/processors/all/literal.rb +42 -0
  57. data/lib/brandish/processors/all/verify.rb +47 -0
  58. data/lib/brandish/processors/common.rb +20 -0
  59. data/lib/brandish/processors/common/asset.rb +118 -0
  60. data/lib/brandish/processors/common/asset/paths.rb +93 -0
  61. data/lib/brandish/processors/common/group.rb +67 -0
  62. data/lib/brandish/processors/common/header.rb +86 -0
  63. data/lib/brandish/processors/common/markup.rb +127 -0
  64. data/lib/brandish/processors/common/output.rb +73 -0
  65. data/lib/brandish/processors/html.rb +18 -0
  66. data/lib/brandish/processors/html/group.rb +33 -0
  67. data/lib/brandish/processors/html/header.rb +46 -0
  68. data/lib/brandish/processors/html/markup.rb +131 -0
  69. data/lib/brandish/processors/html/output.rb +62 -0
  70. data/lib/brandish/processors/html/output/document.rb +127 -0
  71. data/lib/brandish/processors/html/script.rb +64 -0
  72. data/lib/brandish/processors/html/script/babel.rb +48 -0
  73. data/lib/brandish/processors/html/script/coffee.rb +47 -0
  74. data/lib/brandish/processors/html/script/vanilla.rb +45 -0
  75. data/lib/brandish/processors/html/style.rb +82 -0
  76. data/lib/brandish/processors/html/style/highlight.rb +89 -0
  77. data/lib/brandish/processors/html/style/sass.rb +64 -0
  78. data/lib/brandish/processors/html/style/vanilla.rb +71 -0
  79. data/lib/brandish/processors/latex.rb +15 -0
  80. data/lib/brandish/processors/latex/markup.rb +47 -0
  81. data/lib/brandish/scanner.rb +64 -0
  82. data/lib/brandish/version.rb +9 -0
  83. data/templates/initialize/Gemfile.tt +14 -0
  84. data/templates/initialize/brandish.config.rb.tt +49 -0
  85. metadata +296 -0
@@ -0,0 +1,135 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "pathname"
5
+ require "brandish/configure/dsl/form"
6
+
7
+ module Brandish
8
+ class Configure
9
+ # The DSL for configuration files for Brandish. This is used to construct
10
+ # a configure object.
11
+ class DSL
12
+ # Creates a new DSL object with the given configuration object, and
13
+ # yields it.
14
+ #
15
+ # @param configure [Configure] The configuration instance.
16
+ # @return [DSL]
17
+ def self.call(configure = Configure.new)
18
+ DSL.new(configure).tap { |t| yield t }
19
+ end
20
+
21
+ # Creates a DSL object with the given configuration object.
22
+ #
23
+ # @param configure [Configure]
24
+ def initialize(configure = Configure.new)
25
+ @configure = configure
26
+ end
27
+
28
+ # Sets a given option key to a value. The name is interned, making it
29
+ # a symbol.
30
+ #
31
+ # @param name [::Symbol, #intern] The name of the option key.
32
+ # @param value [::Object] The option value.
33
+ # @return [void]
34
+ def set(name, value)
35
+ @configure.options[name.intern] = value
36
+ end
37
+ alias_method :[]=, :set
38
+
39
+ # Retrives a given option key. The name is interned, making it a symbol.
40
+ #
41
+ # @param name [::Symbol, #intern] The name of the option key.
42
+ # @return [::Object] The option value.
43
+ def get(name)
44
+ @configure.options.fetch(name)
45
+ end
46
+ alias_method :[], :get
47
+
48
+ # Sets the root path of the Brandish project. This is where all of the
49
+ # important files are located. Very rarely should this be set to
50
+ # anything other than `"."`.
51
+ #
52
+ # @param root [::String, ::Pathname] The new root.
53
+ # @return [void]
54
+ def root=(root)
55
+ path = _expand_path(root, Dir.pwd)
56
+ self[:root] = path
57
+ end
58
+
59
+ # Retrieves the root path of the Brandish project. This is where all of
60
+ # the important files are located. Very rarely should this be set to
61
+ # anything other than `"."`.
62
+ #
63
+ # @return [::Pathname] The full path to the root.
64
+ def root
65
+ self[:root]
66
+ end
67
+
68
+ alias_method :root_path=, :root=
69
+ alias_method :root_path, :root
70
+
71
+ # Sets the output directory of the Brandish project. This is where all
72
+ # of the outputs are placed. This is normally `"./output"`.
73
+ #
74
+ # @param output [::String, ::Pathname] The path to the output directory.
75
+ # @return [void]
76
+ def output=(output)
77
+ path = _expand_path(output, root)
78
+ self[:output] = path
79
+ end
80
+
81
+ # Retrieves the output directory of the Brandish project. This is where all
82
+ # of the outputs are placed. This is normally `"./output"`.
83
+ #
84
+ # @return [::Pathname] The full path to the output.
85
+ def output
86
+ self[:output]
87
+ end
88
+
89
+ alias_method :output_path=, :output=
90
+ alias_method :output_path, :output
91
+
92
+ # Retrives the source directories of the Brandish project. This is where
93
+ # all of the sources are located. This is normally `"./source"`.
94
+ #
95
+ # @return [::Pathname] The full path to the sources.
96
+ def sources
97
+ self[:sources]
98
+ end
99
+
100
+ alias_method :source_paths, :sources
101
+
102
+ # Retrieves the template directory of the Brandish project. This is
103
+ # where all of the templates are placed. This is normally `"./template"`.
104
+ #
105
+ # @return [::Pathname] The full path to the template.
106
+ def templates
107
+ self[:templates]
108
+ end
109
+
110
+ alias_method :template_paths, :templates
111
+
112
+ # Creates a new form for the configuration object. This takes arguments
113
+ # and a block. The block is yielded the form instance.
114
+ #
115
+ # @see DSL::Form
116
+ # @see Configure::Form
117
+ # @param (see DSL::Form#instance)
118
+ # @yield [form]
119
+ # @return [void]
120
+ def form(*arguments)
121
+ instance = DSL::Form.new(*arguments)
122
+ yield instance
123
+ form = Configure::Form.new(*instance.data)
124
+ @configure.forms << form
125
+ form
126
+ end
127
+
128
+ private
129
+
130
+ def _expand_path(path, directory)
131
+ ::Pathname.new(path).expand_path(::Pathname.new(directory))
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,136 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ class Configure
6
+ class DSL
7
+ # Constructs a form for use with the configure instance. A form is
8
+ # basically a format and name pair; different forms can have different
9
+ # entries, formats, and processors.
10
+ class Form
11
+ # The name of the form. This defaults to a random value. Do not
12
+ # rely on the name unless it is set.
13
+ #
14
+ # @return [::String]
15
+ attr_writer :name
16
+
17
+ # The entry for the form. This is the file that begins the parsing
18
+ # for the form.
19
+ #
20
+ # @return [::String]
21
+ attr_writer :entry
22
+
23
+ # Initialize the form DSL. This sets up the initial format and name,
24
+ # and intializes the instance variables.
25
+ #
26
+ # @param format [::Symbol] The format.
27
+ # @param name [::String, nil] The name of the form.
28
+ def initialize(format, name = nil)
29
+ @name = name || _generate_form_name
30
+ @format = format
31
+ @entry = "index.br"
32
+ @processors = []
33
+ end
34
+
35
+ # Adds a processor for use for the form. The order that this is called
36
+ # is important.
37
+ #
38
+ # @example
39
+ # config.form :html do |form|
40
+ # form.use :literal # Can expand to either "html:literal" or "all:literal"
41
+ # # in that order.
42
+ # form.use "all:literal" # Only expands to "all:literal"
43
+ # form.use Brandish::Processors::All::Literal # allowed too
44
+ # form.use do
45
+ # # also allowed
46
+ # end
47
+ # end
48
+ # @overload use(name, options = {})
49
+ # @param name [::String, ::Symbol, <::Symbol>, ::Class] The name of the
50
+ # processor. If the processor is a symbol, it is assumed to be the
51
+ # name of the processor; the format is guessed to be either
52
+ # the format of the form, or `:all`. If the processor is a string,
53
+ # and it contains a colon, it is assumed to be the name of the
54
+ # processor in the form `<format>:<name>`; if it doesn't contain
55
+ # the colon, it is treated as a symbol. If it is an array, it is
56
+ # assumed to be a pair containing the name of the process. If it
57
+ # is a class, it is assumed to be a processor, and is used directly.
58
+ # @param options [{::Object => ::Object}] The options for the
59
+ # processor. The allowed values for the options are dependant on
60
+ # the processor.
61
+ # @overload use(options = {}, &block)
62
+ # @param options [{::Object => ::Object}] The options for the
63
+ # processor. The allowed values for the options are dependant on
64
+ # the processor.
65
+ # @yield into a new class; this allows a processor to be defined
66
+ # without having a named class.
67
+ # @return [void]
68
+ def use(*arguments)
69
+ processor = options = nil
70
+
71
+ if block_given?
72
+ options = arguments[0]
73
+ processor = Class.new(Brandish::Processor::Base, &::Proc.new)
74
+ else
75
+ name = arguments[0]
76
+ options = arguments[1]
77
+ processor = _guess_processor_name(name)
78
+ end
79
+
80
+ @processors << [processor, options || {}]
81
+ end
82
+ alias_method :process, :use
83
+ alias_method :processor, :use
84
+ alias_method :define, :use
85
+
86
+ # The data from this form. This is used to create the actual form that
87
+ # is used in the configuration.
88
+ #
89
+ # @return [(::String, ::Symbol, ::String, <(::String, ::String, ::Hash)>]
90
+ def data
91
+ [@name, @format, @entry, @processors]
92
+ end
93
+
94
+ private
95
+
96
+ def _generate_form_name
97
+ processor_names = @processors.map { |p| p[0..1].join(":") }.join(".")
98
+ "#{@format}.#{processor_names}"
99
+ end
100
+
101
+ def _guess_processor_name(name)
102
+ case name
103
+ when ::String then _guess_processor_name_string(name)
104
+ when ::Symbol then _guess_processor_name_symbol(name)
105
+ when ::Array then _guess_processor_name_array(name)
106
+ when ::Class then name # They're providing a class that can be used.
107
+ else
108
+ fail ::ArgumentError, "Unknown type given for name `#{name}`"
109
+ end
110
+ end
111
+
112
+ def _guess_processor_name_string(name)
113
+ parts = name.split(":")
114
+
115
+ case parts
116
+ when 1 then _guess_processor_name_symbol(name)
117
+ when 2 then _guess_processor_name_array(parts)
118
+ else
119
+ _guess_processor_name_array([parts[0], parts[1..-1].join(":")])
120
+ end
121
+ end
122
+
123
+ def _guess_processor_name_symbol(name)
124
+ default = [@format, name]
125
+ allowed = [default, [:all, name]]
126
+ which = allowed.find { |a| Processor.all.key?(a) } || default
127
+ _guess_processor_name_array(which)
128
+ end
129
+
130
+ def _guess_processor_name_array(name)
131
+ name.map(&:intern)
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "pry"
5
+ require "pry-rescue"
6
+
7
+ module Brandish
8
+ class Configure
9
+ # A form used for building.
10
+ Form = Struct.new(:name, :format, :entry, :processors) do
11
+ # Builds the form. This takes a configure object, and builds the
12
+ # form based on that.
13
+ #
14
+ # @see Configure#roots
15
+ # @see Processor::Context
16
+ # @see Processor
17
+ # @param configure [Configure] The configuration object for this build.
18
+ # @return [void]
19
+ def build(configure)
20
+ context = Processor::Context.new(configure, self)
21
+ root = configure.roots[configure.sources.find(entry)]
22
+
23
+ processors.each do |(processor, options)|
24
+ klass = processor.is_a?(::Array) ? Processor.all.fetch(processor) : processor
25
+ klass.new(context, options)
26
+ end
27
+
28
+ context.process(root)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ # An error that originates from the library for a library-specific reason.
6
+ # All libraries errors inherit from this class.
7
+ class Error < ::StandardError; end
8
+
9
+ # The file could not be found.
10
+ class NoFileError < Error; end
11
+
12
+ # An error that is created when there is a problem with scanning the
13
+ # document.
14
+ class ScanError < Error; end
15
+
16
+ # An error that occurs with setting up a processor. This has no location
17
+ # information because this error occurs independant of a document.
18
+ class ProcessorError < Error; end
19
+
20
+ # The processor has not been implemented.
21
+ class ProcessorNotImplementedError < ProcessorError; end
22
+
23
+ # This should never be used directly. This is an error that is tied to
24
+ # a location; as such, it provides an initalizer for providing a location.
25
+ #
26
+ # @api private
27
+ class LocationError < Error
28
+ # The location of the error in a file.
29
+ #
30
+ # @return [Location]
31
+ attr_reader :location
32
+
33
+ # Initialize the error with the given location and message.
34
+ def initialize(message, location = Location.default, bt = caller[1..-1])
35
+ @location = location
36
+ super(message)
37
+ set_backtrace(bt)
38
+ end
39
+ end
40
+
41
+ # An error that occurs when parsing. This is for unexpected tokens.
42
+ class ParseError < LocationError; end
43
+
44
+ # An error that is created when there is an issue interacting with a parser
45
+ # node.
46
+ class NodeError < ParseError; end
47
+
48
+ # An error occured during a build.
49
+ class BuildError < LocationError; end
50
+
51
+ # An error occurred while building in a processor. This includes location
52
+ # information from the original node.
53
+ class ProcessorBuildError < BuildError; end
54
+
55
+ # An error occurred while verifying the build. This includes location
56
+ # information for the invalid node.
57
+ class VerificationBuildError < ProcessorBuildError; end
58
+
59
+ # An error occurred with a part of a command or block node, in which a pair
60
+ # was not included.
61
+ class PairError < ProcessorBuildError; end
62
+
63
+ # The syntax used for the styling was invalid.
64
+ class ElementSyntaxError < ProcessorBuildError; end
65
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ # Executes a string of code in the instance of the class.
6
+ #
7
+ # @api private
8
+ class Execute
9
+ # Initialize the execution context.
10
+ #
11
+ # @param context [{::Symbol, ::String => ::Object}] The context. The keys
12
+ # are set as instance variables on the class, with the values being the
13
+ # instance variable's respective value.
14
+ def initialize(context)
15
+ context.each { |k, v| instance_variable_set(:"@#{k}", v) }
16
+ end
17
+
18
+ # Executes the given code in the context of the class.
19
+ #
20
+ # @param code [::String] The code to execute.
21
+ # @return [::Object]
22
+ def exec(code)
23
+ instance_exec(code)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ # Markup modules for use with the {Processors::Common::Markup} processor.
6
+ # This is only to provide certain integrations with Brandish.
7
+ module Markup
8
+ autoload :Redcarpet, "brandish/markup/redcarpet"
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "brandish/markup/redcarpet/html"
5
+ require "brandish/markup/redcarpet/format"
6
+
7
+ module Brandish
8
+ module Markup
9
+ # The Redcarpet format. This is used with the
10
+ # {Processors::Common::Markup} processor.
11
+ module Redcarpet
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "pry"
5
+
6
+ module Brandish
7
+ module Markup
8
+ module Redcarpet
9
+ # Formats text with Redcarpet. This is extracted into a seperate object
10
+ # in order to provide highlighting properties. This is
11
+ # format-independant.
12
+ #
13
+ # This class can take the following options:
14
+ #
15
+ # - All Markdown extension options that are listed on
16
+ # <https://github.com/vmg/redcarpet>, e.g. `:no_intra_emphasis`,
17
+ # `:tables`, etc.
18
+ # - All HTML formatter options that are listed on
19
+ # <https://github.com/vmg/redcarpet>, e.g. `:filter_html`,
20
+ # `:no_images`, etc.
21
+ # - `:highlight` - Optional. The highlighting engine to use. Can be
22
+ # one of `:rouge`, `:coderay`, `:pygments`, and `:none`. Remember to
23
+ # include the requisite highlighting engine in your Gemfile. They
24
+ # will automatically be required as needed. Defaults to `:none`.
25
+ class Format
26
+ # The options that are passed over to the markdown engine. These
27
+ # are extracted from the options that are passed to the markup engine.
28
+ #
29
+ # @return [<::Symbol>]
30
+ MARKDOWN_OPTIONS = %i(
31
+ no_intra_emphasis tables fenced_code_blocks autolink
32
+ disable_indented_code_blocks strikethrough lax_spacing
33
+ space_after_headers superscript underline quote footnotes
34
+ ).freeze
35
+
36
+ # The default options for the markdown engine as passed by this markup
37
+ # engine.
38
+ #
39
+ # @return [{::Symbol => ::Object}]
40
+ MARKDOWN_DEFAULTS = {
41
+ fenced_code_blocks: true, tables: true, autolink: true,
42
+ strikethrough: true, superscript: true, underline: true,
43
+ footnotes: true, space_after_headers: true
44
+ }.freeze
45
+
46
+ # The options that are passed over to the formatting engine. These are
47
+ # Extracted from the options that are passed to the markup engine.
48
+ #
49
+ # @return [<::Symbol>]
50
+ FORMAT_OPTIONS = %i(
51
+ filter_html no_images no_links no_styles escape_html safe_links_only
52
+ with_toc_data hard_wrap xhtml prettify link_attributes highlight
53
+ ).freeze
54
+
55
+ # The default options for the formatting engine as passed by this
56
+ # markup engine.
57
+ #
58
+ # @return [{::Symbol => ::Object}]
59
+ FORMAT_DEFAULTS = { highlight: :none }.freeze
60
+
61
+ # The highlighting engines that are supported by this markup engine.
62
+ # The key is the value passed by the `:highlight` option, and the value
63
+ # is the require file name. If the value is `nil`, no requirement
64
+ # is performed.
65
+ #
66
+ # @return [{::Symbol => ::Object, nil}]
67
+ HIGHLIGHTERS = {
68
+ rouge: "rouge", coderay: "coderay", pygments: "pygments",
69
+ none: nil
70
+ }.freeze
71
+
72
+ # The formating engines that can be used by this markup engine.
73
+ #
74
+ # @return [{::Symbol => ::Class}]
75
+ FORMATTERS = { html: HTML }.freeze
76
+
77
+ # Initialize the markup engine for Redcarpet. For the available
78
+ # options, see {Format}.
79
+ #
80
+ # @param options [::Hash]
81
+ def initialize(options)
82
+ @context = options.fetch(:context)
83
+ @format = options.fetch(:format)
84
+ @markdown_options = MARKDOWN_DEFAULTS
85
+ .merge(extract_options(MARKDOWN_OPTIONS, options))
86
+ @formatter_options = FORMAT_DEFAULTS
87
+ .merge(extract_options(FORMAT_OPTIONS, options))
88
+ @highlight = @formatter_options[:highlight]
89
+ load_highlighter
90
+ load_engine
91
+ end
92
+
93
+ # Renders the given text using the engine.
94
+ #
95
+ # @param string [::String] The value to render.
96
+ # @return [::String] The rendered value.
97
+ def render(string)
98
+ @engine.render(string)
99
+ end
100
+
101
+ private
102
+
103
+ def load_highlighter
104
+ file = HIGHLIGHTERS.fetch(@highlight)
105
+ require file if file
106
+ rescue ::KeyError
107
+ fail ProcessorError, "Unknown highlighter `#{@highlight}`"
108
+ end
109
+
110
+ def load_engine
111
+ begin
112
+ formatter = FORMATTERS.fetch(@format)
113
+ rescue ::KeyError
114
+ fail ProcessorError, "Unsupported format `#{@format}`"
115
+ end
116
+
117
+ renderer = formatter.new(@context, @formatter_options)
118
+ @engine = ::Redcarpet::Markdown.new(renderer, @markdown_options)
119
+ end
120
+
121
+ def extract_options(keys, options)
122
+ keys.zip(options.values_at(*keys)).select!(&:last).to_h
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end