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,163 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "forwardable"
5
+
6
+ module Brandish
7
+ # A set of paths that can be searched for a certain file. This is used for
8
+ # looking for certain files, like sources or templates. This can allow
9
+ # Brandish to provide "default" files.
10
+ #
11
+ # Despite its name, the order in which the paths are added to the pathset
12
+ # are important.
13
+ class PathSet
14
+ extend Forwardable
15
+ include Enumerable
16
+
17
+ # @!method clear
18
+ # Removes all of the paths from this pathset.
19
+ #
20
+ # @return [void]
21
+ def_delegator :@paths, :clear
22
+
23
+ # @!method each(&block)
24
+ # @overload each
25
+ # Returns an enumerable over all of the paths in the pathset.
26
+ #
27
+ # @return [::Enumerable<::Pathname>]
28
+ # @overload each(&block)
29
+ # Iterates over all of the paths in this pathset.
30
+ #
31
+ # @yield path For each path in the pathset.
32
+ # @yieldparam path [::Pathname] The path.
33
+ # @return [void]
34
+ def_delegator :@paths, :each
35
+
36
+ # Initialize the pathset.
37
+ def initialize
38
+ @paths = []
39
+ end
40
+
41
+ # Adds a path to this pathset.
42
+ #
43
+ # @param path [::String, ::Pathname]
44
+ # @return [self]
45
+ def <<(path)
46
+ @paths << ::Pathname.new(path)
47
+ end
48
+
49
+ # Calls {#clear}, and then uses {#<<} to append the given path to the
50
+ # pathset, effectively replacing all of the paths in the pathset with the
51
+ # one given.
52
+ #
53
+ # @param path [::String, ::Pathname]
54
+ # @return [self]
55
+ def replace(path)
56
+ clear
57
+ self << path
58
+ end
59
+
60
+ # The default options for {#find} and {#find_all}.
61
+ #
62
+ # @return [{::Symbol => ::Object}]
63
+ DEFAULT_FIND_OPTIONS = { file: true, allow_absolute: false }.freeze
64
+
65
+ # "Resolves" a path. This is resolved the exact same way that {#find}
66
+ # and {#find_all} are (with some slight variations), and so can be used
67
+ # as a sort of "name" for a certain path.
68
+ #
69
+ # @param path [::String, ::Pathname] The path to the file.
70
+ # @param options [{::Symbol => ::Object}] The options for resolution.
71
+ # @return [::Pathname] The resolved path.
72
+ def resolve(path, options = {})
73
+ options = DEFAULT_FIND_OPTIONS.merge(options)
74
+ path = ::Pathname.new(path)
75
+ if options[:allow_absolute]
76
+ path.cleanpath
77
+ else
78
+ ::Pathname.new(path.expand_path("/").to_s.gsub(%r{\A(/|\\)}, ""))
79
+ end
80
+ end
81
+
82
+ # Finds a file in the pathset. If the file is returned, it is guarenteed
83
+ # to exist. Relative paths, that do not expand out of the relative path,
84
+ # are handled like you would expect - the path is appended to one of the
85
+ # paths in the set, and checked for existance. However, for a file that
86
+ # expands out of the relative path (e.g. `../a` or `a/../../b`), the
87
+ # behavior for expansion depends on the `allow_absolute` option. If
88
+ # `allow_absolute` is false (default), the path is expanded against `/`
89
+ # before it is joind with the paths in the set (e.g. `../a`, against
90
+ # `/path/to`, with `allow_absolute=false`, expands to `/path/to/a`).
91
+ # If `allow_absolute` is true, it directly expanding against the path
92
+ # (e.g. `../a`, against `/path/to`, with `allow_absolute=true`, expands
93
+ # to `/path/a`). `allow_absolute` should only be used if the given path
94
+ # is trusted. Absolute paths are handled in a similar manner; if
95
+ # `allow_absolute=false`, for `/a` against `/path/to`, it expands to
96
+ # `/path/to/a`; with `allow_absolute=true`, for `/a` against `/path/to`,
97
+ # it expands to `/a`.
98
+ #
99
+ # @example
100
+ # pathset
101
+ # # => #<PathSet ...>
102
+ # pathset.find("some/file")
103
+ # # => #<Pathname /path/to/some/file>
104
+ # pathset.find("not/real")
105
+ # # !> NoFileError
106
+ # pathset.find("/path/to/some/file")
107
+ # # !> NoFileError
108
+ # pathset.find("/path/to/some/file", allow_absolute: true)
109
+ # # => #<Pathname /path/to/some/file>
110
+ # pathset.find("../to/some/file")
111
+ # # !> NoFileError
112
+ # pathset.find("../to/some/file", allow_absolute: true)
113
+ # # => #<Pathname /path/to/some/file>
114
+ # @raise [NoFileError] If no file could be found.
115
+ # @param short [::String, ::Pathname] The "short" path to resolve.
116
+ # @param options [{::Symbol => ::Object}] The options for finding.
117
+ # @option (see #find_all)
118
+ # @return [::Pathname] The full absolute path to the file.
119
+ def find(short, options = {})
120
+ find_all(short, options).next
121
+ rescue ::StopIteration
122
+ fail NoFileError, "Could not find `#{short}' in any of the given paths (paths: #{@paths})"
123
+ end
124
+
125
+ # Finds all versions of the short path name in the paths in the path
126
+ # sets. If no block is given, it returns an enumerable; otherwise, if
127
+ # a block is given, it yields the joined path if it exists.
128
+ #
129
+ # @raise NoFileError If no file could be found.
130
+ # @param short [::String, ::Pathname] The "short" path to resolve.
131
+ # @param options [{::Symbol => ::Object}] The options for finding.
132
+ # @option options [Boolean] :allow_absolute (false)
133
+ # @option options [Boolean] :file (true) Whether or not the full path
134
+ # must be a file for it to be considered existant. This should be set
135
+ # to true, because in most cases, it's the desired behavior.
136
+ # @yield [path] For every file that exists.
137
+ # @yieldparam path [::Pathname] The path to the file. This is guarenteed
138
+ # to exist.
139
+ # @return [void]
140
+ def find_all(short, options = {})
141
+ return to_enum(:find_all, short, options) unless block_given?
142
+ short = ::Pathname.new(short)
143
+ options = DEFAULT_FIND_OPTIONS.merge(options)
144
+
145
+ @paths.reverse.each do |path|
146
+ joined = path_join(path, short, options)
147
+ yield joined if (options[:file] && joined.file?) || joined.exist?
148
+ end
149
+
150
+ nil
151
+ end
152
+
153
+ private
154
+
155
+ def path_join(path, short, options)
156
+ if options[:allow_absolute]
157
+ short.expand_path(path)
158
+ else
159
+ ::Pathname.new(::File.join(path, short.expand_path("/")))
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "brandish/processor/base"
5
+ require "brandish/processor/context"
6
+ require "brandish/processor/name_filter"
7
+ require "brandish/processor/block"
8
+ require "brandish/processor/command"
9
+ require "brandish/processor/descend"
10
+
11
+ module Brandish
12
+ # Processors for Brandish. These just handle reshaping nodes so that they
13
+ # output nicely. This can be used for things like including, bold tags,
14
+ # etc.
15
+ module Processor
16
+ # A structure containing all of the processors available. This is a key
17
+ # value store, with the key being the format and the name, and the value
18
+ # being the actual processor.
19
+ #
20
+ # @return [{(::Symbol, ::Symbol) => Processor::Base}]
21
+ def self.all
22
+ @_processors ||= ::Hash.new
23
+ end
24
+
25
+ # Registers processors with the global registry. This interns the format
26
+ # and name of the processor. If the format and name pair already exists,
27
+ # it raises a {ProcessorError}.
28
+ #
29
+ # @example
30
+ # Processor.register [:html, :stripper] => self
31
+ # @example
32
+ # Processor.register [:all, :descend] => self
33
+ # @raise [ProcessorError] If one of the format and name pairs already
34
+ # exists.
35
+ # @param map [{(::Symbol, ::Symbol) => Processor::Base}] The processors to
36
+ # register.
37
+ # @return [void]
38
+ def self.register(map)
39
+ map.each do |(format, name), processor|
40
+ format, name = format.intern, name.intern
41
+ fail ProcessorError, "#{format}:#{name} already exists" \
42
+ if all.key?([format, name])
43
+ all[[format, name]] = processor
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,144 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processor
6
+ # A processor. This responds to a set of methods that return updated
7
+ # versions of the nodes that are passed to them. Processors are all
8
+ # initialized at the same time, with a context. The processor is expected
9
+ # to add an object that responds to #call with arity matching `1` to the
10
+ # context using {Context#<<}. The processor is allowed to add any number
11
+ # of objects to the context using this, if need be; by default, the
12
+ # processor just adds itself.
13
+ #
14
+ # @abstract
15
+ # This class is not designed to be used and instantiated directly; this
16
+ # just provides common behavior for a processor. This class should be
17
+ # subclassed and proper behavior defined on a subclass.
18
+ class Base
19
+ # (see Processor.register)
20
+ def self.register(map)
21
+ Processor.register(map)
22
+ end
23
+
24
+ # The context associated with this processor.
25
+ #
26
+ # @return [Context]
27
+ attr_reader :context
28
+
29
+ # Initializes the processor with the given context. This adds the
30
+ # processor to the context, and sets the context for use on the
31
+ # processor.
32
+ #
33
+ # @param context [Context]
34
+ # @param options [::Object] The options for this processor.
35
+ def initialize(context, options = {})
36
+ @context = context
37
+ @context << self
38
+ @options = options
39
+ setup
40
+ end
41
+
42
+ # This is called by {#initialize}. This allows subclasses to perform
43
+ # any nessicary setups without having to override {#initialize}. This
44
+ # does nothing by default.
45
+ #
46
+ # @return [void]
47
+ def setup; end
48
+
49
+ # Processes the given node. By default, it checks the classes of the
50
+ # inbound node, and maps them to `process_*` blocks. If it doesn't
51
+ # match, an `ArgumentError` is thrown.
52
+ #
53
+ # If this function returns a `nil` value, the node should be ignored.
54
+ # {Context#accept} acknowledges this, and skips over the remaining
55
+ # processors once a processor returns a `nil` value for a node.
56
+ #
57
+ # @raise [::ArgumentError] if the node given isn't one of
58
+ # {Parser::Node::Block}, {Parser::Node::Command}, {Parser::Node::Root},
59
+ # or {Parser::Node::Text}.
60
+ # @param node [Parser::Node] A parser node to handle.
61
+ # @return [Parser::Node, nil] The result of processing.
62
+ def call(node)
63
+ _fix_result(_switch_node(node), node)
64
+ rescue LocationError then fail
65
+ rescue => e
66
+ fail BuildError.new("#{e.class}: #{e.message}", node.location,
67
+ e.backtrace)
68
+ end
69
+
70
+ # (see Context#accept)
71
+ def accept(node)
72
+ context.accept(node)
73
+ end
74
+
75
+ # Processes a block. By default, this performs no modifications on the
76
+ # node, and returns the node itself.
77
+ #
78
+ # @param node [Parser::Node::Block]
79
+ # @return [::Object]
80
+ def process_block(node)
81
+ node
82
+ end
83
+
84
+ # Processes a command. By default, this performs no modifications on the
85
+ # node, and returns the node itself.
86
+ #
87
+ # @param node [Parser::Node::Command]
88
+ # @return [::Object]
89
+ def process_command(node)
90
+ node
91
+ end
92
+
93
+ # Processes a root node. By default, this performs no modifications on
94
+ # the node, and returns the node itself.
95
+ #
96
+ # @param node [Parser::Node::Root]
97
+ # @return [::Object]
98
+ def process_root(node)
99
+ node
100
+ end
101
+
102
+ # Processes a text node. By default, this performs no modifications on
103
+ # the node, and returns the node itself.
104
+ #
105
+ # @param node [Parser::Node::Text]
106
+ # @return [::Object]
107
+ def process_text(node)
108
+ node
109
+ end
110
+
111
+ # An optional post-process.
112
+ #
113
+ # @param root [Parser::Node::Root]
114
+ # @return [void]
115
+ def postprocess(root); end
116
+
117
+ private
118
+
119
+ def _switch_node(node)
120
+ case node
121
+ when Parser::Node::Block then process_block(node)
122
+ when Parser::Node::Command then process_command(node)
123
+ when Parser::Node::Root then process_root(node)
124
+ when Parser::Node::Text then process_text(node)
125
+ else
126
+ fail ArgumentError, "Expected node, got `#{node.class}'"
127
+ end
128
+ end
129
+
130
+ def _fix_result(result, node)
131
+ case result
132
+ when Parser::Node, nil
133
+ result
134
+ when ::String
135
+ Parser::Node::Text.new(value: result, location: node.location)
136
+ .prevent_update
137
+ else
138
+ fail ArgumentError, "Unknown result type `#{result.class}' " \
139
+ "(given from #{self.class})"
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processor
6
+ # A block processor. This is designed to act over a base to modify
7
+ # one specific block. The block name itself is specified on the
8
+ # class, and the class provides logic to only modify block nodes with
9
+ # the same name.
10
+ module Block
11
+ # Ruby hook.
12
+ #
13
+ # @api private
14
+ # @return [void]
15
+ def self.included(base)
16
+ base.include Processor::NameFilter
17
+ base.include Processor::Pairfilter
18
+ end
19
+
20
+ # Processes the block. If the node's name doesn't match the name for
21
+ # this class, it passes it on up to {Base#process_block}; otherwise,
22
+ # it passes it over to {#perform}.
23
+ #
24
+ # @param node [Parser::Node::Block]
25
+ # @return [::Object]
26
+ def process_block(node)
27
+ return super unless allowed_names.include?(node.name)
28
+ @node = node
29
+ @name = node.name
30
+ @pairs = node.pairs
31
+ @body = node.body
32
+
33
+ assert_valid_pairs
34
+ perform
35
+ end
36
+
37
+ # Performs the command adjustment. This must be subclassed and
38
+ # overwritten.
39
+ #
40
+ # @abstract
41
+ # @return [::Object]
42
+ def perform
43
+ fail NotImplementedError, "Please implement #{self.class}#perform"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processor
6
+ # A command processor. This is designed to act over a base to modify
7
+ # one specific command. The command name itself is specified on the
8
+ # class, and the class provides logic to only modify command nodes with
9
+ # the same name.
10
+ module Command
11
+ # Ruby hook.
12
+ #
13
+ # @api private
14
+ # @return [void]
15
+ def self.included(base)
16
+ base.include Processor::NameFilter
17
+ base.include Processor::PairFilter
18
+ end
19
+
20
+ # Processes the command. If the node's name doesn't match the name for
21
+ # this class, it passes it on up to {Base#process_command}; otherwise,
22
+ # it passes it over to {#perform}.
23
+ #
24
+ # @param node [Parser::Node::Command]
25
+ # @return [::Object]
26
+ def process_command(node)
27
+ return super unless allowed_names.include?(node.name)
28
+ @node = node
29
+ @name = node.name
30
+ @pairs = node.pairs
31
+ @body = nil
32
+
33
+ assert_valid_pairs
34
+ perform
35
+ end
36
+
37
+ # Performs the command adjustment. This must be subclassed and
38
+ # overwritten.
39
+ #
40
+ # @abstract
41
+ # @return [::Object]
42
+ def perform
43
+ fail NotImplementedError, "Please implement #{self.class}#perform"
44
+ end
45
+ end
46
+ end
47
+ end