brandish 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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