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,169 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "forwardable"
5
+
6
+ module Brandish
7
+ module Processor
8
+ # The state associated with all of the processors. Since all processors
9
+ # are independant class instances, instance variables that are used in
10
+ # one processor does not affect another; however, shared state can be
11
+ # used through the context.
12
+ #
13
+ # The context also keeps track of the processors being used for the
14
+ # processor, and distributes the processing management throughout all of
15
+ # the processors.
16
+ class Context
17
+ # A "skip" construct. This makes the node be skipped through processing
18
+ # for the rest of the processors. This is automatically applied to all
19
+ # nodes that are {Parser::Node#update_prevented?}.
20
+ #
21
+ # Once the skip construct has been passed through the processing, it is
22
+ # automatically unwrapped, returning the original node.
23
+ Skip = Struct.new(:node)
24
+
25
+ extend Forwardable
26
+ # @!method <<(*processors)
27
+ # Adds processors to the processor list. This delegates to
28
+ # {#processors}.
29
+ #
30
+ # @return [<#call>]
31
+ # @!method push(*processors)
32
+ # Adds processors to the processor list. This delegates to
33
+ # {#processors}.
34
+ #
35
+ # @return [<#call>]
36
+ # @!method unshift(*processors)
37
+ # Adds a processor to the start of the processor list. This delegates
38
+ # to {#processors}.
39
+ #
40
+ # @return [<#call>]
41
+ delegate [:<<, :push, :unshift] => :@processors
42
+
43
+ # @!method [](key)
44
+ # Sets a key on the context. This gets an option that is used for all
45
+ # processors on this context.
46
+ #
47
+ # @param key [::Symbol, ::String] The key.
48
+ # @return [::Object]
49
+ # @!method []=(key, value)
50
+ # Sets a key on the context. This sets an option that is used for all
51
+ # processors on this context.
52
+ #
53
+ # @param key [::Symbol, ::String] The key.
54
+ # @param value [::Object] The value.
55
+ # @return [::Object]
56
+ # @!method fetch(key, default = CANARY, &block)
57
+ # Fetches a value at the given key, or provides a default if the key
58
+ # doesn't exist. If both a block and a default argument are given,
59
+ # the block form takes precedence.
60
+ #
61
+ # @overload fetch(key)
62
+ # Attempts to retrieve a value at the given key. If there is no
63
+ # key-value pair at the given key, it raises an error.
64
+ #
65
+ # @raise [KeyError] if the key isn't on the context.
66
+ # @param key [::Symbol, ::String] The key.
67
+ # @return [::Object] The value.
68
+ #
69
+ # @overload fetch(key, default)
70
+ # Attempts to retrieve a value at the given key. If there is no
71
+ # key-value pair at the given key, it returns the value given by
72
+ # `default`.
73
+ #
74
+ # @param key [::Symbol, ::String] The key.
75
+ # @param default [::Object] The default value.
76
+ # @return [::Object] The value, or the default value if there isn't
77
+ # one.
78
+ #
79
+ # @overload fetch(key, &block)
80
+ # attempts to retrieve a value at the given key. If there is no
81
+ # key-value pair at the given key, it yields.
82
+ #
83
+ # @yield if there is no corresponding key-value pair.
84
+ # @param key [::Symbol, ::String] The key.
85
+ # @return [::Object] The value, or the result of the block if there
86
+ # isn't one.
87
+ delegate [:[], :[]=, :fetch] => :@options
88
+
89
+ # @!method merge(options)
90
+ # Merges the given options into this context.
91
+ #
92
+ # @param options [{::Symbol, ::String => ::Object}]
93
+ # @return [void]
94
+ def_delegator :@options, :merge!, :merge
95
+
96
+ # The processors that are going to be run on an accept. This can be
97
+ # a {Processor::Base} subclass, or any object that responds to `#call`.
98
+ #
99
+ # @return [<#call>]
100
+ attr_reader :processors
101
+
102
+ # The configuration for the build. This is used for output directories
103
+ # and the like.
104
+ #
105
+ # @return [Configure]
106
+ attr_reader :configure
107
+
108
+ # The form that is being processed.
109
+ #
110
+ # @return [Configure::Form]
111
+ attr_reader :form
112
+
113
+ # Initialize the context, to set up the internal state.
114
+ def initialize(configure, form)
115
+ @processors = []
116
+ @configure = configure
117
+ @form = form
118
+ @descent = Processor::Descend.new(self)
119
+ @buffer = []
120
+ @options = {}
121
+ end
122
+
123
+ # Performs the processing of the given root node. This should be a
124
+ # {Parser::Node::Root}.
125
+ #
126
+ # @param root [Parser::Node::Root]
127
+ # @return [::Object]
128
+ def process(root)
129
+ root = accept(root)
130
+ effective_processors.each { |p| p.postprocess(root) }
131
+ end
132
+
133
+ # Accepts a node. This passes the node through all of the processors,
134
+ # as well as an instance of the {Processor::Descend} processor.
135
+ #
136
+ # @param node [Parser::Node] The node to process.
137
+ # @return [::Object]
138
+ def accept(node)
139
+ # Injects the node over all effective processors. Every iteration will
140
+ # use the value returned by the last `process` method call, unless it
141
+ # is `nil`.
142
+ result = effective_processors.inject(node) { |n, p| accept_node_with(n, p) }
143
+ result.is_a?(Skip) ? result.node : result
144
+ end
145
+
146
+ private
147
+
148
+ def accept_node_with(node, processor)
149
+ case node
150
+ when Skip, nil
151
+ node
152
+ when Parser::Node
153
+ result = processor.call(node)
154
+ if result.is_a?(Parser::Node) && result.update_prevented?
155
+ Skip.new(result)
156
+ else
157
+ result
158
+ end
159
+ else
160
+ fail ProcessorError, "Unknown node type `#{node.class}'"
161
+ end
162
+ end
163
+
164
+ def effective_processors
165
+ [@descent] + @processors
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processor
6
+ # A descent processor. This allows the context to descend into the
7
+ # children of the node as needed. This does *not* descend into block
8
+ # nodes by default, due to a design decision - that has to be handled by
9
+ # another processor.
10
+ #
11
+ # @api private
12
+ class Descend < Base
13
+ # Initializes the processor with the given context. This *does not* adds
14
+ # the processor to the context, and sets the context for use on the
15
+ # processor.
16
+ #
17
+ # @param context [Context]
18
+ def initialize(context)
19
+ @context = context
20
+ end
21
+
22
+ # Processes the root node. This updates the root node with an updated
23
+ # list of children that have been accepted.
24
+ #
25
+ # @param node [Parser::Node::Root]
26
+ # @return [Parser::Node::Root]
27
+ def process_root(node)
28
+ node.update(children: node.children.map { |c| accept(c) }.compact)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processor
6
+ # An inline-defined processor. This allows processors to be defined easily
7
+ # within a block. This is mostly used for configuration.
8
+ #
9
+ # @example
10
+ # class RFC < Brandish::Processor::Inline
11
+ # command! :rfc
12
+ # pairs :number
13
+ #
14
+ # perform do
15
+ # number = @pairs.fetch("number")
16
+ # "<a href='https://www.ietf.org/rfc/rfc#{number}.txt'>RFC #{number}</a>"
17
+ # end
18
+ # end
19
+ class Inline < Base
20
+ # Sets this processor as a command processor. This includes
21
+ # {Command}, and passes the given names to
22
+ # {NameFilter::ClassMethods#name}.
23
+ #
24
+ # @return [void]
25
+ def self.command!(*names)
26
+ include Command
27
+ name(*names)
28
+ end
29
+
30
+ # Sets this processor as a block processor. This include {Block}, and
31
+ # passes the given names to {NameFilter::ClassMethods#name}.
32
+ #
33
+ # @return [void]
34
+ def self.block!(*names)
35
+ include Block
36
+ name(*names)
37
+ end
38
+
39
+ # Defines the `#perform` method with the given block. This is only
40
+ # effective if one of {Command} or {Block} is included. This takes the
41
+ # block passed to it and uses that to define the `#perform` method.
42
+ #
43
+ # @return [void]
44
+ def self.perform(&block)
45
+ define_method(:perform, &block)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "set"
5
+
6
+ module Brandish
7
+ module Processor
8
+ # A "name filter" for command and block nodes. This provides helpers to
9
+ # filter out nodes that don't have the given name. The class will keep
10
+ # an internal list of names that are allowed to be used for the node,
11
+ # and if the node matches, then it processes it.
12
+ #
13
+ # @api private
14
+ module NameFilter
15
+ # The class methods that are implemented on the including module. This
16
+ # is extended onto the class.
17
+ module ClassMethods
18
+ # The name that is assumed from the class name.
19
+ #
20
+ # @return [::String]
21
+ def assumed_class_name
22
+ name
23
+ .gsub(/\A(?:.+::)?(.*?)\z/, "\\1")
24
+ .gsub(/(?<!\A)[A-Z]/) { |m| "-#{m}" }
25
+ .downcase
26
+ end
27
+
28
+ # A list of allowed names for the class.
29
+ #
30
+ # @return [::Set<::String>]
31
+ def allowed_names
32
+ @names ||= name ? Set.new : Set[assumed_class_name]
33
+ end
34
+
35
+ # If no names are given, it retrieves them using {#allowed_names};
36
+ # otherwise, it merges the names into the allowed names set.
37
+ #
38
+ # @param names [#to_s, <#to_s>] The names.
39
+ # @return [::Set<::String>] The allowed names.
40
+ def names(*names)
41
+ return allowed_names if names.none?
42
+ allowed_names.merge(Array(names).flatten.map(&:to_s))
43
+ end
44
+
45
+ alias_method :name, :names
46
+ alias_method :names=, :names
47
+ alias_method :name=, :names
48
+ end
49
+
50
+ # The instance methods on the including class.
51
+ module InstanceMethods
52
+ # (see ClassMethods#allowed_names)
53
+ def allowed_names
54
+ self.class.allowed_names
55
+ end
56
+ end
57
+
58
+ # Used as a hook for Ruby.
59
+ #
60
+ # @api private
61
+ def self.included(receiver)
62
+ receiver.extend ClassMethods
63
+ receiver.send :include, InstanceMethods
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processor
6
+ # A filter for defining which pairs are accepted by the given command or
7
+ # block processor. By default, all pairs are restricted; however, certain
8
+ # pairs can be whitelisted, or all pairs can be allowed. The former helps
9
+ # with debugging.
10
+ module PairFilter
11
+ # A placehodler object to denote that all pairs are allowed in the pair
12
+ # set. This is only inserted into the allowed pair set when
13
+ # {ClassMethods#unrestricted_pairs!} is called.
14
+ ALL = Object.new.freeze
15
+
16
+ # The class methods that are implemented on the including module. This
17
+ # is extended onto the class.
18
+ module ClassMethods
19
+ # A set of allowed pairs that can be used with the command or block
20
+ # element.
21
+ #
22
+ # @return [::Set<::String>]
23
+ def allowed_pairs
24
+ @allowed_pairs ||= Set.new
25
+ end
26
+
27
+ # A list of all of the ancestors' pairs. This includes the current
28
+ # classes' pairs. This allows allowed pair inheritance.
29
+ #
30
+ # @return [::Set<::String>]
31
+ def ancestor_allowed_pairs
32
+ ancestors
33
+ .select { |a| a.respond_to?(:allowed_pairs) }
34
+ .map(&:allowed_pairs)
35
+ .inject(Set.new, :merge)
36
+ end
37
+
38
+ # Retrives or sets the pairs for the class.
39
+ #
40
+ # @overload pairs
41
+ # Retrieves the current pairs. This is the same as calling
42
+ # {#allowed_pairs}.
43
+ #
44
+ # @return [::Set<::String>]
45
+ # @overload pairs(*pairs)
46
+ # Adds pairs to the current {#allowed_pairs}.
47
+ #
48
+ # @param pairs [::String, ::Symbol, #to_s]
49
+ # @return [void]
50
+ def pairs(*pairs)
51
+ return allowed_pairs if pairs.none?
52
+ allowed_pairs.merge(Array(pairs).flatten.map(&:to_s))
53
+ end
54
+ alias_method :pair, :pairs
55
+
56
+ # Adds {PairFilter::ALL} to the pair list, allowing all pairs to be
57
+ # used with the command or block.
58
+ #
59
+ # @return [void]
60
+ def unrestricted_pairs!
61
+ pairs PairFilter::ALL
62
+ end
63
+ alias_method :unrestricted_pairs, :unrestricted_pairs!
64
+ end
65
+
66
+ # The instance methods on the including class.
67
+ module InstanceMethods
68
+ # (see ClassMethods#ancestor_allowed_pairs)
69
+ def allowed_pairs
70
+ self.class.ancestor_allowed_pairs
71
+ end
72
+
73
+ # Asserts that the pairs given are all allowed. This uses the
74
+ # `@pairs` and `@node` instance variables.
75
+ #
76
+ # @raise [PairError] If an invalid pair was given.
77
+ # @return [void]
78
+ def assert_valid_pairs
79
+ return if allowed_pairs.include?(PairFilter::ALL)
80
+ excessive = @pairs.keys - allowed_pairs
81
+ return unless excessive.any?
82
+ fail PairError.new("Unexpected pairs found " \
83
+ "(#{excessive.map(&:inspect).join(', ')})", @node.location)
84
+ end
85
+ end
86
+
87
+ # Used as a hook for Ruby.
88
+ #
89
+ # @api private
90
+ def self.included(receiver)
91
+ receiver.extend ClassMethods
92
+ receiver.send :include, InstanceMethods
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "brandish/processors/common"
5
+ require "brandish/processors/all"
6
+ require "brandish/processors/html"
7
+ require "brandish/processors/latex"
8
+
9
+ module Brandish
10
+ # All of the available processors provided by Brandish by default are defined
11
+ # under this module. Under this module, there are a set of modules, all
12
+ # pertaining to either a format, `All`, or `Common`. `All` processors are
13
+ # provided for all documents, regardless of format; `Common` processors are
14
+ # always abstract, and define a set of processors that are required to be
15
+ # implemented for all formats. If a format does not provide a `Common`
16
+ # processor, it is considered a bug.
17
+ module Processors
18
+ # All of the format modules under the {Processors} module. This is
19
+ # essentially all modules that aren't named `All` or `Common`.
20
+ #
21
+ # @return [<Module>]
22
+ def self.format_modules
23
+ (constants - [:All, :Common]).map { |c| Processors.const_get(c) }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "brandish/processors/all/embed"
5
+ require "brandish/processors/all/if"
6
+ require "brandish/processors/all/import"
7
+ require "brandish/processors/all/literal"
8
+ require "brandish/processors/all/verify"
9
+
10
+ module Brandish
11
+ module Processors
12
+ # Processors designed for use with all formats. This module does not
13
+ # implement the {Common} processors, since the common processors can be
14
+ # format-dependant. All processors in this module are defined on the
15
+ # `:all` special format.
16
+ module All
17
+ end
18
+ end
19
+ end