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,93 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processors
6
+ module Common
7
+ class Asset < Processor::Base
8
+ # Common path operations used for asset processors. This assumes
9
+ # a css-like dependency structure similar to HTML's. These are not
10
+ # required to be used.
11
+ module Paths
12
+ # The asset path for this kind of asset.
13
+ #
14
+ # @abstract
15
+ # @return [::String]
16
+ def asset_kind_path
17
+ nil
18
+ end
19
+
20
+ # The extension for this kind of asset. This defaults to no
21
+ # extension.
22
+ #
23
+ # @abstract
24
+ # @return [::String]
25
+ def asset_kind_extension
26
+ ""
27
+ end
28
+
29
+ # The load paths for this asset type. This defaults to a pathset
30
+ # containing all of the sources, all of the sources' appended with
31
+ # {#asset_kind_path}, and all of the paths given by the
32
+ # `:asset_load_paths` option value.
33
+ def asset_load_paths
34
+ return @asset_load_paths if @asset_load_paths
35
+
36
+ paths = PathSet.new
37
+ @context.configure.sources
38
+ .each { |p| paths << p }
39
+ .each { |p| paths << p / asset_kind_path }
40
+ @options.fetch(:asset_load_paths, []).each { |p| paths << p }
41
+ @asset_load_paths = paths
42
+ end
43
+
44
+ # The default output assets path.
45
+ #
46
+ # @return [::String]
47
+ def output_assets_path
48
+ @context.configure.output / asset_kind_path
49
+ end
50
+
51
+ # Converts the given uri into a pathname, using the host, the path,
52
+ # the query, and the fragment all as directories. This turns the
53
+ # uri `https://example.com/some-path?waffles#test` into the path
54
+ # `example.com/some-path/waffles/test.<asset_kind_extension>`.
55
+ #
56
+ # @param uri [::URI] The uri to convert into a path.
57
+ # @return [::Pathname]
58
+ def uri_path(uri)
59
+ base = [uri.host,
60
+ (uri.path unless uri.path.empty?),
61
+ (uri.query if uri.query),
62
+ (uri.fragment if uri.fragment)].compact
63
+ ::Pathname.new(::File.join(*base)).sub_ext(asset_kind_extension)
64
+ end
65
+
66
+ # The file paths. This returns a hash with three elements:
67
+ # `:file`, which is the given file name from the element;
68
+ # `:out`, which is the full output directory for outputting the
69
+ # asset file; and `:src`, which is similar to what the HTML src
70
+ # or href value would be for the asset.
71
+ #
72
+ # @return [{::Symbol => ::Pathname}]
73
+ def load_file_paths
74
+ file = load_asset_file
75
+ # The full path to the file itself.
76
+ path = asset_load_paths.find(file, @options)
77
+ output_path = @context.configure.output
78
+ # The relative path from the source asset directory.
79
+ asset_path = asset_load_paths.resolve(file)
80
+ # The "raw" output path.
81
+ raw_output_path =
82
+ @pairs.fetch("output") { asset_path.sub_ext(asset_kind_extension) }
83
+ # The actual output path of the file.
84
+ file_output_path = output_assets_path / raw_output_path
85
+ src_output_path = file_output_path.relative_path_from(output_path)
86
+
87
+ { file: path, out: file_output_path, src: src_output_path }
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "securerandom"
5
+
6
+ module Brandish
7
+ module Processors
8
+ module Common
9
+ # A "group." This is for grouping together elements for styling
10
+ # or logical purposes. By default, this creates a block element.
11
+ #
12
+ # This takes no options.
13
+ #
14
+ # Pairs:
15
+ #
16
+ # - `"class"` - Optional. See {#class_value}.
17
+ # - `"id"` - Optional. See {#id_value}.
18
+ # - `"name"` - Optional. See {#name_value}.
19
+ #
20
+ # @abstract
21
+ # Implement {#perform} and register the processor with {.register}.
22
+ class Group < Processor::Base
23
+ include Processor::Block
24
+ pairs :class, :id, :name
25
+
26
+ # The class value of the group. This can have a 1-to-1 correspondence
27
+ # to the destination source. This works similarly to HTML's class
28
+ # attribute. This uses the `"class"` pair.
29
+ #
30
+ # @return [::String]
31
+ def class_value
32
+ @pairs.fetch("class", "")
33
+ end
34
+
35
+ # The ID value of the group. This can have a 1-to-1 correspondence
36
+ # to the destination source. This works similarly to HTML's id
37
+ # attribute; especially the concept that it should be unique. This
38
+ # uses the `"id"` pair.
39
+ #
40
+ # @return [::String, nil]
41
+ def id_value
42
+ @pairs["id"]
43
+ end
44
+
45
+ # The name value of the group. This doesn't have a 1-to-1
46
+ # correspondence to the destination source. This is used to provide
47
+ # an internal styling or grouping process. This uses the `"name"`
48
+ # pair.
49
+ #
50
+ # @return [::String, nil]
51
+ def name_value
52
+ @pairs["name"]
53
+ end
54
+
55
+ # The body, accepted and flattened. This essentially converts the
56
+ # contents into a string that can be used as the value for the group.
57
+ #
58
+ # @see #accept
59
+ # @see Parser::Node::Root#flatten
60
+ # @return [::String]
61
+ def accepted_body
62
+ accept(@body).flatten
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processors
6
+ module Common
7
+ # A processor that defines the `header` command. This creates a list
8
+ # internally of all of the headers in the document, to later be used to
9
+ # possibly create a table of contents, if needed.
10
+ #
11
+ # This takes no options.
12
+ #
13
+ # Pairs:
14
+ #
15
+ # - `"level"` - Optional. Defaults to `1`. The "level" of the header.
16
+ # This should be a value between 1 and 6.
17
+ # - `"value"` - Required. The name of the header.
18
+ # - `"id"` - Optional. The ID of the header. This is a unique value
19
+ # to reference to this header. If no value is given, it defaults
20
+ # to a modified `"value"`.
21
+ #
22
+ # @abstract
23
+ # Implement {#header_render}, and register the processor with
24
+ # {.register}.
25
+ class Header < Processor::Base
26
+ include Processor::Command
27
+ pairs :level, :value, :id
28
+
29
+ # (see Processor::Base#setup)
30
+ def setup
31
+ super
32
+ @context[:headers] = []
33
+ end
34
+
35
+ # Handles the header. This stores the {#header_data} in the context
36
+ # `:headers` key, and then calls {#header_render}.
37
+ #
38
+ # @return [Parser::Node::Text] The resulting header text.
39
+ def perform
40
+ @context[:headers] << header_data
41
+ header_render
42
+ end
43
+
44
+ # Renders the header for the format. This should be implemented by the
45
+ # implementing format.
46
+ #
47
+ # @abstract
48
+ # @return [Parser::Node::Text] The resulting header text.
49
+ def header_render
50
+ fail ProcessorNotImplementedError,
51
+ "Please implement #{self.class}#header_render"
52
+ end
53
+
54
+ # The header data used for the internal `:headers` structure.
55
+ #
56
+ # @return [{::Symbol => ::Object}]
57
+ def header_data
58
+ { level: header_level, value: header_value, id: header_id }
59
+ end
60
+
61
+ # The header level. This is an integer between 1 and 6.
62
+ #
63
+ # @return [Numeric]
64
+ def header_level
65
+ @pairs.fetch("level", "1").to_i
66
+ end
67
+
68
+ # The "value" of the header. This is the contents or the name of the
69
+ # header. This is required.
70
+ #
71
+ # @return [::String]
72
+ def header_value
73
+ @pairs.fetch("value")
74
+ end
75
+
76
+ # The "id" of the header. This should be a unique value between all
77
+ # headers that specifies this exact header.
78
+ #
79
+ # @return [::String]
80
+ def header_id
81
+ @pairs.fetch("id") { header_value.downcase.gsub(/[^\w]|_/, "-") }
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Brandish
5
+ module Processors
6
+ module Common
7
+ # Processes the text in the document with the given markup. Markup
8
+ # support varies from format to format, from a lot, to none. All
9
+ # supported markups are not included as a dependency on the Brandish
10
+ # gem; they must be used as a dependency for the using library or
11
+ # application.
12
+ #
13
+ # *All* formats *must* provide an `:escape` engine, that escapes the
14
+ # source for the target format.
15
+ #
16
+ # Options:
17
+ #
18
+ # - `:engine` - Required. This is the markup engine to use. The allowed
19
+ # values for this option is dependant on the format.
20
+ # - `:options` - Optional. This is the
21
+ # options for the markup engine. The actual type is dependant on the
22
+ # markup engine. The default values are dependant on the markup engine.
23
+ #
24
+ # @abstract
25
+ # Implement engines using {.engine}, and register the processor using
26
+ # {.register}.
27
+ class Markup < Processor::Base
28
+ # The engines defined for the subclass. This should not be used on the
29
+ # parent class ({Common::Markup}). This returns a key-value pair for
30
+ # the engines. The key is the "name" of the format; this is used for
31
+ # the `:engine` option. The value is a tuple containing two values:
32
+ # the default options, and a proc that takes two arguments to markup
33
+ # the text.
34
+ #
35
+ # @api private
36
+ # @return [{::Symbol => (::Object, ::Proc<::String, ::Object, ::String>)}]
37
+ def self.engines
38
+ @_engines ||= {}
39
+ end
40
+
41
+ # Defines an engine for use on the subclass. This should not be used
42
+ # on the parent class ({Common::Markup}). This takes the name of the
43
+ # engine, the default options for the engine, and the processor to
44
+ # perform the markup processor.
45
+ #
46
+ # If both a third argument and a block are provided, then the block
47
+ # takes precedence.
48
+ #
49
+ # @api private
50
+ # @param name [::Symbol] The name of the engine. This isn't the actual
51
+ # name of the markup; this is the name of the engine that processes
52
+ # the markup.
53
+ # @param default [::Object] The default options for the engine.
54
+ # @param initializer [::Symbol, ::Proc, nil] The initializer for the
55
+ # engine. If this is `nil`, no initializer is called. If this is
56
+ # a Symbol, the method is called for initialization. If this is a
57
+ # Proc, it is called for initialization.
58
+ # @param processor [::Symbol, ::Proc] The processor for the
59
+ # engine. If this is a Symbol, the method is called for
60
+ # processing. If this is a Proc, it is called for processing.
61
+ # @return [void]
62
+ def self.engine(name, default, initializer, processor)
63
+ engines[name] = [default.freeze, initializer, processor]
64
+ end
65
+
66
+ # (see Processor::Base#setup)
67
+ def setup
68
+ super
69
+ initialize_engine
70
+ end
71
+
72
+ # (see Processor::Base#process_text)
73
+ def process_text(node)
74
+ node.update(value: markup(node.value))
75
+ end
76
+
77
+ private
78
+
79
+ def initialize_engine
80
+ @engine = find_engine
81
+
82
+ case @engine[1]
83
+ when ::Symbol then send(@engine[1])
84
+ when ::Proc then @engine[1].call
85
+ when nil then return
86
+ else
87
+ fail ProcessorError, "Unknown initializer `#{@engine[1].inspect}'"
88
+ end
89
+ end
90
+
91
+ def find_engine
92
+ engine = @options.fetch(:engine)
93
+
94
+ case engine
95
+ when ::Symbol
96
+ self.class.engines.fetch(engine)
97
+ when ::Proc
98
+ [{}, nil, engine]
99
+ else
100
+ fail ProcessorError, "Unknown engine `#{engine.inspect}'"
101
+ end
102
+ end
103
+
104
+ def engine_options
105
+ @_engine_options ||= _build_engine_options
106
+ end
107
+
108
+ def markup(value)
109
+ case @engine[2]
110
+ when ::Symbol then send(@engine[2], value, engine_options)
111
+ when ::Proc then @engine[2].call(value, engine_options)
112
+ else
113
+ fail ProcessorError, "Unknown processor `#{@engine[2].inspect}'"
114
+ end
115
+ end
116
+
117
+ def _build_engine_options
118
+ if @engine[0].is_a?(::Hash)
119
+ @engine[0].merge(@options.fetch(:options, {}))
120
+ else
121
+ @options.fetch(:options) { @engine[0].dup }
122
+ end.freeze
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require "liquid"
5
+
6
+ module Brandish
7
+ module Processors
8
+ module Common
9
+ # Outputs the result of processing the document. Without this processor,
10
+ # the document is not output, and most other processors have no effect.
11
+ #
12
+ # Options:
13
+ #
14
+ # - `:template` - Optional. The name of the template to use. This
15
+ # defaults to the format used.
16
+ #
17
+ # @abstract
18
+ # Please implement {#find_path} and {#template_data}, and register
19
+ # the processor with {.register}.
20
+ class Output < Processor::Base
21
+ # Sets up the path and template for the output processor. It first
22
+ # attempts to find the output path, creating the directory to that
23
+ # path if needed. Then, it builds up the template that will later then
24
+ # be used to render the data.
25
+ #
26
+ # @see Processor::Base#setup
27
+ # @return [void]
28
+ def setup
29
+ super
30
+
31
+ @path = find_path.tap { |p| p.dirname.mkpath }
32
+ template_option = @options.fetch(:template, @context.form.format.to_s)
33
+ template_path = ::Pathname.new(template_option).sub_ext(".liquid")
34
+ template_full = @context.configure.templates.find(template_path)
35
+ @template = ::Liquid::Template.parse(template_full.read)
36
+ end
37
+
38
+ # Postprocess the result of processing. The given root should only
39
+ # have text children, and should respond successfully to `#flatten`.
40
+ # This will render the template, and write it out to the value given
41
+ # in `@path`.
42
+ #
43
+ # @see Processor::Base#postprocess
44
+ # @param root [Parser::Node::Root]
45
+ # @return [void]
46
+ def postprocess(root)
47
+ @root = root
48
+ value = @template.render!(template_data, strict_variables: true)
49
+ @path.open("wb") { |f| f.write(value) }
50
+ end
51
+
52
+ # Finds the output path.
53
+ #
54
+ # @abstract
55
+ # @return [::Pathname]
56
+ def find_path
57
+ fail ProcessorNotImplementedError,
58
+ "Please implement #{self.class}#find_path"
59
+ end
60
+
61
+ # A hash of data to pass to the template for rendering. The keys
62
+ # should always be strings.
63
+ #
64
+ # @abstract
65
+ # @return [{::String => ::Object}]
66
+ def template_data
67
+ fail ProcessorNotImplementedError,
68
+ "Please implement #{self.class}#template_data"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end