marko 0.1.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +22 -0
- data/README.md +159 -0
- data/Rakefile +12 -0
- data/exe/marko +20 -0
- data/lib/assets/demo/README.md +13 -0
- data/lib/assets/demo/src/fr/assemble.md +27 -0
- data/lib/assets/demo/src/fr/compile.md +25 -0
- data/lib/assets/demo/src/fr/markup.md +111 -0
- data/lib/assets/demo/src/fr/storage.md +16 -0
- data/lib/assets/demo/src/fr/treenode.md +34 -0
- data/lib/assets/demo/src/index.md +34 -0
- data/lib/assets/demo/src/intro.md +98 -0
- data/lib/assets/demo/src/ui/cli.md +26 -0
- data/lib/assets/demo/src/ui/gem.md +14 -0
- data/lib/assets/demo/src/ur/uc.create.project.md +8 -0
- data/lib/assets/demo/src/ur/uc.general.flow.md +14 -0
- data/lib/assets/init/README.md +61 -0
- data/lib/assets/init/Rakefile +100 -0
- data/lib/assets/init/tt/artifact.md.tt +3 -0
- data/lib/marko/artifact.rb +3 -0
- data/lib/marko/assembler.rb +82 -0
- data/lib/marko/cli.rb +121 -0
- data/lib/marko/compiler.rb +16 -0
- data/lib/marko/config.rb +20 -0
- data/lib/marko/gadgets/pluggable.rb +55 -0
- data/lib/marko/gadgets/sentry.rb +66 -0
- data/lib/marko/gadgets/service.rb +52 -0
- data/lib/marko/gadgets.rb +3 -0
- data/lib/marko/loader.rb +38 -0
- data/lib/marko/markup/compiler.rb +36 -0
- data/lib/marko/markup/decorator.rb +65 -0
- data/lib/marko/markup/macro.rb +176 -0
- data/lib/marko/markup/parser.rb +122 -0
- data/lib/marko/markup/storage.rb +100 -0
- data/lib/marko/markup/validator.rb +101 -0
- data/lib/marko/markup.rb +24 -0
- data/lib/marko/parser.rb +19 -0
- data/lib/marko/services/assemble.rb +16 -0
- data/lib/marko/services/compile.rb +30 -0
- data/lib/marko/services.rb +2 -0
- data/lib/marko/storage.rb +36 -0
- data/lib/marko/tree_node.rb +128 -0
- data/lib/marko/validator.rb +19 -0
- data/lib/marko/version.rb +5 -0
- data/lib/marko.rb +37 -0
- data/marko.gemspec +44 -0
- metadata +99 -0
@@ -0,0 +1,61 @@
|
|
1
|
+
% Marko Demo Project
|
2
|
+
|
3
|
+
Welcome to your new [Marko](https://github.com/nvoynov/clerq) project!
|
4
|
+
|
5
|
+
# Structure
|
6
|
+
|
7
|
+
This project has the following structure:
|
8
|
+
|
9
|
+
- [bin/](bin/) - output folder
|
10
|
+
- [bin/assets/](bin/assets/) - assets folder
|
11
|
+
- [src/](src/) - markup sources
|
12
|
+
- [tt/](tt/) - templates
|
13
|
+
- [marko.yml](marko.yml) - project configuration
|
14
|
+
- [Rakefile](Rakefile) - Rakefile
|
15
|
+
- [README.md](README.md)
|
16
|
+
|
17
|
+
# Interface
|
18
|
+
|
19
|
+
Run `marko` command in your console to see basic command-line interface.
|
20
|
+
|
21
|
+
Extend it yourself through Rakefile
|
22
|
+
|
23
|
+
# Git Repository
|
24
|
+
|
25
|
+
[Git How To](https://githowto.com/)
|
26
|
+
|
27
|
+
Incorporates changes from a remote repository:
|
28
|
+
|
29
|
+
git pull
|
30
|
+
|
31
|
+
Create a new branch to start any activity with the repository:
|
32
|
+
|
33
|
+
git branch <branch_name>
|
34
|
+
|
35
|
+
Make changes and commit your work:
|
36
|
+
|
37
|
+
git add .
|
38
|
+
git commit -m "branch_name - commit description"
|
39
|
+
|
40
|
+
When your changes finished, incorporate changes from the remote repository and merge the `master` branch to your `branch_name`:
|
41
|
+
|
42
|
+
git checkout master
|
43
|
+
git pull
|
44
|
+
git checkout <branch_name>
|
45
|
+
git merge master
|
46
|
+
|
47
|
+
Resolve all conflicts and commit changes:
|
48
|
+
|
49
|
+
git add .
|
50
|
+
git commit -m "branch_name conflicts resolved"
|
51
|
+
|
52
|
+
Merge your changes to the `master` branch:
|
53
|
+
|
54
|
+
git checkout master
|
55
|
+
git merge <branch_name>
|
56
|
+
|
57
|
+
Push your changes to the remote repository:
|
58
|
+
|
59
|
+
git push
|
60
|
+
|
61
|
+
Create a merge request if you are not allowed to push to the `master` branch.
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'marko'
|
4
|
+
require 'marko/markup'
|
5
|
+
|
6
|
+
namespace :marko do
|
7
|
+
|
8
|
+
# @todo pandoc: bin/arfifact.docx: openBinaryFile: permission denied (Permission denied)
|
9
|
+
# Success! Find the artifact in bin/arfifact.docx
|
10
|
+
#
|
11
|
+
desc 'Publish the artifact'
|
12
|
+
task :publish do
|
13
|
+
output = 'bin/arfifact'
|
14
|
+
`marko c -o #{output}.md`
|
15
|
+
pandoc = `pandoc #{output}.md -o #{output}.docx`
|
16
|
+
File.delete("#{output}.md")
|
17
|
+
puts "Success! Find the artifact in #{output}.docx"
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'Print table of contents'
|
21
|
+
task :toc, :query do |t, args|
|
22
|
+
args.with_defaults(query: '')
|
23
|
+
tree = Marko.assemble
|
24
|
+
query = args.query
|
25
|
+
unless query.empty?
|
26
|
+
tree = tree.find_node(query)
|
27
|
+
unless tree
|
28
|
+
puts "Nothing to be printed!"
|
29
|
+
return
|
30
|
+
end
|
31
|
+
tree.orphan!
|
32
|
+
end
|
33
|
+
tree.each do |n|
|
34
|
+
if n.root?
|
35
|
+
puts "% #{n.title}" if n.root?
|
36
|
+
next
|
37
|
+
end
|
38
|
+
title = n.title.empty? ? n.id : n.title
|
39
|
+
puts '#' * n.nesting_level + " #{title} ##{n.id}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Print @@todo'
|
44
|
+
task :todo do
|
45
|
+
tree = Marko.assemble
|
46
|
+
tree.to_a.drop(1)
|
47
|
+
.select{|n| n.body =~ /@@todo/}
|
48
|
+
.each{|n|
|
49
|
+
list = n.body
|
50
|
+
.scan(/@@todo.*$/)
|
51
|
+
.map{_1.sub(/@@todo/, '')}
|
52
|
+
.map{" #{_1.strip}"}
|
53
|
+
.join(?\n)
|
54
|
+
puts n[:origin]
|
55
|
+
puts list
|
56
|
+
puts
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def datestamp
|
61
|
+
Time.new.strftime('%Y%m%d')
|
62
|
+
end
|
63
|
+
|
64
|
+
def timestamp
|
65
|
+
Time.new.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'Punch meeting munutes'
|
69
|
+
task :mm, :extra do |t, args|
|
70
|
+
args.with_defaults(extra: '')
|
71
|
+
extra = args.extra
|
72
|
+
Dir.mkdir('mm') unless Dir.exist?('mm')
|
73
|
+
body = MINUTES % timestamp
|
74
|
+
name = "minutes-#{datestamp}#{extra.empty? ? '' : ?_ + extra}"
|
75
|
+
name = File.join('mm', name)
|
76
|
+
errm = "Minutes exists already. Maybe you can provide [EXTRA]?"
|
77
|
+
fail errm if File.exist?(name)
|
78
|
+
File.write(name, body)
|
79
|
+
end
|
80
|
+
|
81
|
+
MINUTES = <<~EOF.freeze
|
82
|
+
%% Meeting Minutes %s
|
83
|
+
|
84
|
+
# Attendants
|
85
|
+
|
86
|
+
1.
|
87
|
+
2.
|
88
|
+
|
89
|
+
# Questions
|
90
|
+
|
91
|
+
1.
|
92
|
+
2.
|
93
|
+
|
94
|
+
# Decisions
|
95
|
+
|
96
|
+
1.
|
97
|
+
2.
|
98
|
+
EOF
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "gadgets"
|
4
|
+
require_relative "config"
|
5
|
+
require_relative "loader"
|
6
|
+
require_relative "tree_node"
|
7
|
+
|
8
|
+
module Marko
|
9
|
+
|
10
|
+
# The strategy for assembling sources into artifact tree
|
11
|
+
class Assembler < Service
|
12
|
+
|
13
|
+
class Failure # < StandardError
|
14
|
+
attr_reader :errors
|
15
|
+
def initialize(message, *errors)
|
16
|
+
@errors = errors
|
17
|
+
super(
|
18
|
+
errors
|
19
|
+
.map{|e| e.lines.map{|l| " #{l}"}.join }
|
20
|
+
.unshift(message + ?\n)
|
21
|
+
.join
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [TreeNode]
|
27
|
+
def call
|
28
|
+
@block.(:stage, 'loading sources') if @block
|
29
|
+
buffer, errors = Loader.(&@block)
|
30
|
+
fail Failure.new('markup parsing errors', *errors) if errors.any?
|
31
|
+
@block.(:stage, 'tree assemblage') if @block
|
32
|
+
tree = assemble(buffer)
|
33
|
+
@block.(:stage, 'tree enrichment') if @block
|
34
|
+
injectid(tree)
|
35
|
+
@block.(:stage, 'tree validation') if @block
|
36
|
+
errors = validate(tree)
|
37
|
+
fail Failure.new('tree validation errors', *errors) if errors.any?
|
38
|
+
tree
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# @param buff [Array<TreeNode>]
|
44
|
+
# @return [TreeNode]
|
45
|
+
def assemble(buff)
|
46
|
+
art = Marko.artifact
|
47
|
+
TreeNode.new(art.title, id: '0').tap {|root|
|
48
|
+
# @todo buff.inject(root, :<<) wrong but why?
|
49
|
+
buff.each{|n| root << n}
|
50
|
+
root.items
|
51
|
+
.select{|node| node[:parent] && node[:parent] != node.parent_id}
|
52
|
+
.each{|node|
|
53
|
+
parent = root.find{|n| n.id == node[:parent]}
|
54
|
+
next unless parent # is must be left under root
|
55
|
+
parent << node
|
56
|
+
node.delete_meta(:parent)
|
57
|
+
root.delete_item(node)
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def injectid(tree)
|
63
|
+
counter = {}
|
64
|
+
tree
|
65
|
+
.select {|node| node.id.empty?}
|
66
|
+
.each do |node|
|
67
|
+
index = counter[node.parent] || 1
|
68
|
+
counter[node.parent] = index + 1
|
69
|
+
id = index.to_s.rjust(2, '0')
|
70
|
+
id = '.' + id unless node.parent == tree
|
71
|
+
node[:id] = id
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate(tree)
|
76
|
+
validator = ValidatorPlug.plugged
|
77
|
+
validator.(tree)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
data/lib/marko/cli.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require_relative "services"
|
5
|
+
include Marko::Services
|
6
|
+
|
7
|
+
module Marko
|
8
|
+
module CLI
|
9
|
+
extend self
|
10
|
+
|
11
|
+
def punch(dir, args = ARGV)
|
12
|
+
if Dir.exist?(dir)
|
13
|
+
puts "Directory '#{dir}' already exist!"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
storage.punch(dir)
|
17
|
+
puts "Marko directory created!"
|
18
|
+
log = Dir.chdir(dir) { Dir.glob('**/*') }
|
19
|
+
puts log.map{ " created #{dir}/#{_1}" }.join(?\n)
|
20
|
+
kwargs = options(:new, args)
|
21
|
+
editor = kwargs.fetch(:editor, '')
|
22
|
+
Dir.chdir(dir) { `#{editor} .` } unless editor.empty?
|
23
|
+
end
|
24
|
+
|
25
|
+
def punch_demo(args = ARGV)
|
26
|
+
# pp options(:demo, args)
|
27
|
+
log = storage.punch_demo
|
28
|
+
puts "Marko Demo cloned!\n#{log}"
|
29
|
+
kwargs = options(:demo, args)
|
30
|
+
editor = kwargs.fetch(:editor, '')
|
31
|
+
Dir.chdir(log) { `#{editor} .` } unless editor.empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def compile(args = ARGV)
|
35
|
+
unless storage.marko_home?
|
36
|
+
puts "Marko directory required!"
|
37
|
+
return
|
38
|
+
end
|
39
|
+
kwargs = options(:compile, args)
|
40
|
+
verbose = kwargs.delete(:verbose) { false }
|
41
|
+
pulsefn = method(verbose ? :pulse_verbose : :pulse_concise)
|
42
|
+
result = Compile.(**kwargs, &pulsefn)
|
43
|
+
puts "compiled #{result}\nSuccess!"
|
44
|
+
rescue Marko::Assembler::Failure => e
|
45
|
+
puts "Assembler failed with #{e.errors.size} #{e.message}"
|
46
|
+
puts "Failure"
|
47
|
+
end
|
48
|
+
|
49
|
+
def banner
|
50
|
+
puts "#{BANNER}\nUsage:"
|
51
|
+
PARSER.each{|_, cmd| puts "\n #{cmd}"}
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def storage
|
57
|
+
StoragePlug.plugged
|
58
|
+
end
|
59
|
+
|
60
|
+
def pulse_verbose(events, payload)
|
61
|
+
case events
|
62
|
+
when :stage
|
63
|
+
puts "#{payload}.."
|
64
|
+
when :source
|
65
|
+
print " #{payload}.."
|
66
|
+
when :errors
|
67
|
+
unless payload.empty?
|
68
|
+
puts "#{payload.size} errors #{payload.join}"
|
69
|
+
else
|
70
|
+
puts "OK"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def pulse_concise(events, payload)
|
76
|
+
case events
|
77
|
+
when :stage
|
78
|
+
puts "#{payload}.."
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def options(cmd, args = ARGV)
|
83
|
+
{}.tap{|opt| PARSER[cmd].parse(args, into: opt)}
|
84
|
+
end
|
85
|
+
|
86
|
+
# \ ^__^
|
87
|
+
# \ (oo)\_______
|
88
|
+
# (__)\ )\/\
|
89
|
+
# ||----w |
|
90
|
+
# || ||
|
91
|
+
BANNER = <<~EOF.freeze
|
92
|
+
~ Marko v0.1.3 ~ Markup Compiler
|
93
|
+
~ https://github.io/nvoynov/marko
|
94
|
+
EOF
|
95
|
+
|
96
|
+
PARSER = {
|
97
|
+
new: OptionParser.new {|cmd|
|
98
|
+
ban = "marko new|n DIRECTORY [OPTIONS]".ljust(35, ?\s)
|
99
|
+
cmd.banner = ban + "Create a new Marko project"
|
100
|
+
cmd.on('-e EDITOR', '--editor EDITOR',
|
101
|
+
'Open the project in EDITOR', String)
|
102
|
+
},
|
103
|
+
demo: OptionParser.new {|cmd|
|
104
|
+
ban = "marko demo|d [OPTIONS]".ljust(35, ?\s)
|
105
|
+
cmd.banner = ban + "Clone Marko Demo project"
|
106
|
+
cmd.on('-e EDITOR', '--editor EDITOR',
|
107
|
+
'Open the project in EDITOR', String)
|
108
|
+
},
|
109
|
+
compile: OptionParser.new {|cmd|
|
110
|
+
ban = "marko compile|c [OPTIONS]".ljust(35, ?\s)
|
111
|
+
cmd.banner = ban + "Compile Marko artifact"
|
112
|
+
cmd.on('-t TEMPLATE', '--template TEMPLATE',
|
113
|
+
'Template to render', String)
|
114
|
+
cmd.on('-o FILENAME', '--filename FILENAME',
|
115
|
+
'Render to filename', String)
|
116
|
+
cmd.on('-v', '--verbose', 'Run verbosely')
|
117
|
+
}
|
118
|
+
}.freeze
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "tree_node"
|
4
|
+
require_relative "gadgets"
|
5
|
+
|
6
|
+
module Marko
|
7
|
+
class Compiler
|
8
|
+
extend Pluggable
|
9
|
+
def call(tree, template, filename, &block)
|
10
|
+
@tree = MustbeTreeNode.(tree)
|
11
|
+
@template = MustbeString.(template)
|
12
|
+
@filename = MustbeString.(filename)
|
13
|
+
@block = block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/marko/config.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
require_relative "parser"
|
3
|
+
require_relative "storage"
|
4
|
+
require_relative "compiler"
|
5
|
+
require_relative "validator"
|
6
|
+
|
7
|
+
module Marko
|
8
|
+
|
9
|
+
# default abstract starters that must be replugged later
|
10
|
+
ParserPlug = Parser.plug
|
11
|
+
StoragePlug = Storage.plug
|
12
|
+
CompilerPlug = Compiler.plug
|
13
|
+
ValidatorPlug = Validator.plug
|
14
|
+
|
15
|
+
def self.artifact
|
16
|
+
storage = StoragePlug.plugged
|
17
|
+
storage.artifact
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Marko
|
4
|
+
# Pluggable mixin serves for dependency injection
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Storage
|
8
|
+
# extend Pluggable
|
9
|
+
# def call
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# StoragePort = Storage.port
|
14
|
+
#
|
15
|
+
# class SequelStorage < Storage
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# StoragePort.plugged = SequelStorage.new
|
19
|
+
#
|
20
|
+
# require 'forwardable'
|
21
|
+
# class Service
|
22
|
+
# extend Forwardable
|
23
|
+
# def_delegator :StoragePort, :plugged, :storage
|
24
|
+
# def call
|
25
|
+
# storage.call
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
module Pluggable
|
29
|
+
|
30
|
+
def plug
|
31
|
+
klass = self
|
32
|
+
Module.new {
|
33
|
+
extend Plug;
|
34
|
+
plug klass
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
module Plug
|
39
|
+
def plug(klass)
|
40
|
+
@klass = klass
|
41
|
+
end
|
42
|
+
|
43
|
+
def plugged
|
44
|
+
fail "unknown @klass" unless @klass
|
45
|
+
@plugged ||= @klass.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def plugged=(obj)
|
49
|
+
fail ArgumentError.new("required an instance of #{@klass}"
|
50
|
+
) unless obj.is_a?(@klass)
|
51
|
+
@plugged = obj
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Marko
|
4
|
+
# Factory module for guarding method arguments
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# ShortString = Sentry.new(:str, "must be String[3..100]"
|
9
|
+
# ) {|v| v.is_a?(String) && v.size.between?(3,100)}
|
10
|
+
#
|
11
|
+
# ShortString.(str) => "str"
|
12
|
+
# ShortString.(nil) => ArgumentError ":str must be String[3..100]"
|
13
|
+
# ShortString.error(nil) => ":str must be String[3..100]"
|
14
|
+
# ShortString.error!(nil) => ArgumentError":str must be String[3..100]"
|
15
|
+
# ShortString.(nil, :name) => ArgumentError ":name must be String[3..100]"
|
16
|
+
# ShortString.(nil, 'John Doe', 'Ups!') => ArgumentError ":John Doe Ups!"
|
17
|
+
#
|
18
|
+
module Sentry
|
19
|
+
|
20
|
+
# creates a new Sentry
|
21
|
+
# @param key [Symbol|String] key for error message
|
22
|
+
# @param msg [String] error message
|
23
|
+
# @param blk [&block] validation block that should return boolen
|
24
|
+
# @return [Sentry] based on key, msg, and validation block
|
25
|
+
def self.new(key, msg, &blk)
|
26
|
+
# origin ;)
|
27
|
+
argerror = ->(val, msg, cnd) {
|
28
|
+
fail ArgumentError, msg unless cnd
|
29
|
+
val
|
30
|
+
}
|
31
|
+
Module.new do
|
32
|
+
include Sentry
|
33
|
+
extend self
|
34
|
+
|
35
|
+
@key = argerror.(key, ":key must be Symbol|String",
|
36
|
+
key.is_a?(String) || key.is_a?(Symbol))
|
37
|
+
@msg = argerror.(msg, ":msg must be String", msg.is_a?(String))
|
38
|
+
@blk = argerror.(blk, "&blk must be provided", block_given?)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# returns error message for invalid :val
|
43
|
+
# @param val [Object] value to be validated
|
44
|
+
# @param key [Symbol|String] key for error message
|
45
|
+
# @param msg [String] optional error message
|
46
|
+
# @return [String] error message for invalid :val or nil when :val is valid
|
47
|
+
def error(val, key = @key, msg = @msg)
|
48
|
+
":#{key} #{msg}" unless @blk.(val)
|
49
|
+
end
|
50
|
+
|
51
|
+
# guards :val
|
52
|
+
# @todo @see Yard!
|
53
|
+
# @param val [Object] value to be returned if it valid
|
54
|
+
# @param key [Symbol|String] key for error message
|
55
|
+
# @param msg [String] optional error message
|
56
|
+
# @return [Object] valid :val or raieses ArgumentError when invalid
|
57
|
+
def error!(val, key = @key, msg = @msg)
|
58
|
+
return val if @blk.(val)
|
59
|
+
fail ArgumentError, ":#{key} #{msg}", caller[0..-1]
|
60
|
+
end
|
61
|
+
|
62
|
+
# @todo how do describe in Yard?
|
63
|
+
alias :call :error!
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Marko
|
4
|
+
# Service like ServiceObject, Command, etc.
|
5
|
+
#
|
6
|
+
# @example just call without parameters
|
7
|
+
# class BoldService < Service
|
8
|
+
# def call
|
9
|
+
# 42
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# @example with parameters
|
14
|
+
# class PlusService < Service
|
15
|
+
# def initialize(a, b)
|
16
|
+
# @a, @b = a, b
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def call
|
20
|
+
# 42
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
class Service
|
25
|
+
Failure = Class.new(StandardError)
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def call(*args, **kwargs, &block)
|
29
|
+
new(*args, **kwargs, &block).call
|
30
|
+
end
|
31
|
+
|
32
|
+
def inherited(klass)
|
33
|
+
klass.const_set(:Failure, Class.new(klass::Failure))
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private_class_method :new
|
39
|
+
|
40
|
+
def initialize(*args, **kwargs, &block)
|
41
|
+
@args = args
|
42
|
+
@kwargs = kwargs
|
43
|
+
@block = block
|
44
|
+
end
|
45
|
+
|
46
|
+
def call
|
47
|
+
fail Failure, "#{self.class.name}#call must be overrided"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/marko/loader.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "gadgets"
|
4
|
+
require_relative "config"
|
5
|
+
|
6
|
+
module Marko
|
7
|
+
|
8
|
+
# The strategy class for loading sources from repository
|
9
|
+
class Loader < Service
|
10
|
+
# load markup sources, parse and return TreeNode buffer
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# fn = proc{|event, paylod| ... }
|
14
|
+
# buffer, errors = loader.(&fn)
|
15
|
+
# fail "Failed" if errors.any?
|
16
|
+
# # procced ...
|
17
|
+
#
|
18
|
+
# @param block [&block] aka proc {|event, payload| ..}
|
19
|
+
# @return [Array<TreeNode>, Array<String>] where
|
20
|
+
# the first item is buffer and the second is array<error>
|
21
|
+
def call
|
22
|
+
buffer = []
|
23
|
+
errors = []
|
24
|
+
parser = ParserPlug.plugged
|
25
|
+
storage = StoragePlug.plugged
|
26
|
+
storage.sources.each do |source|
|
27
|
+
@block.(:source, source) if @block
|
28
|
+
content = storage.content(source)
|
29
|
+
buff, errs = parser.(content, source, &@block)
|
30
|
+
buffer.concat(buff)
|
31
|
+
errors.concat(errs)
|
32
|
+
@block.(:errors, errs) if @block
|
33
|
+
end
|
34
|
+
[buffer.flatten, errors]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require_relative "../compiler"
|
5
|
+
require_relative "../config"
|
6
|
+
|
7
|
+
module Marko
|
8
|
+
module Markup
|
9
|
+
|
10
|
+
class Compiler < Marko::Compiler
|
11
|
+
|
12
|
+
# @see Marko::Compliler#call
|
13
|
+
def call(tree, template, filename, &block)
|
14
|
+
super(tree, template, filename, &block)
|
15
|
+
compile
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def compile
|
21
|
+
storage = StoragePlug.plugged
|
22
|
+
erbgen = ERB.new(@template, trim_mode: '-')
|
23
|
+
payload = @tree.map{|n| Decorator.new(n)}
|
24
|
+
storage.write(@filename){|f|
|
25
|
+
payload.each{|node|
|
26
|
+
@node = node
|
27
|
+
text = erbgen.result(binding)
|
28
|
+
f.puts text
|
29
|
+
}
|
30
|
+
}
|
31
|
+
@filename
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|