clerq 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +24 -0
- data/README.md +269 -0
- data/Rakefile +10 -0
- data/TODO.md +3 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/clerq.gemspec +44 -0
- data/exe/clerq +8 -0
- data/lib/assets/new/README.md.tt +75 -0
- data/lib/assets/new/clerq.thor.tt +15 -0
- data/lib/assets/new/clerq.yml.tt +4 -0
- data/lib/assets/new/content.md.tt +41 -0
- data/lib/assets/promo/README.md +40 -0
- data/lib/assets/promo/bin/Clerq SRS.docx +0 -0
- data/lib/assets/promo/bin/Clerq SRS.md +579 -0
- data/lib/assets/promo/bin/assets/promo_dark.png +0 -0
- data/lib/assets/promo/bin/assets/promo_light.png +0 -0
- data/lib/assets/promo/clerq.yml +3 -0
- data/lib/assets/promo/promo.thor +58 -0
- data/lib/assets/promo/src/clerq.md +82 -0
- data/lib/assets/promo/src/fr/cmp.node.md +14 -0
- data/lib/assets/promo/src/fr/cmp.repo.md +10 -0
- data/lib/assets/promo/src/fr/cmp.tt.md +20 -0
- data/lib/assets/promo/src/fr/cmp.writer.md +19 -0
- data/lib/assets/promo/src/fr/ent.md +32 -0
- data/lib/assets/promo/src/ui/cli/cli.bld.md +32 -0
- data/lib/assets/promo/src/ui/cli/cli.chk.md +17 -0
- data/lib/assets/promo/src/ui/cli/cli.hlp.md +14 -0
- data/lib/assets/promo/src/ui/cli/cli.new.md +20 -0
- data/lib/assets/promo/src/ui/cli/cli.opt.md +11 -0
- data/lib/assets/promo/src/ui/cli/cli.ver.md +4 -0
- data/lib/assets/promo/src/ui/ui.cli.md +8 -0
- data/lib/assets/promo/src/us/us.reader.md +8 -0
- data/lib/assets/promo/src/us/us.writer.md +79 -0
- data/lib/assets/tt/default.md.erb +64 -0
- data/lib/assets/tt/gitlab.md.erb +93 -0
- data/lib/assets/tt/pandoc.md.erb +88 -0
- data/lib/assets/tt/raw.md.erb +23 -0
- data/lib/clerq.rb +41 -0
- data/lib/clerq/cli.rb +129 -0
- data/lib/clerq/entities.rb +2 -0
- data/lib/clerq/entities/node.rb +135 -0
- data/lib/clerq/entities/template.rb +19 -0
- data/lib/clerq/gateways.rb +3 -0
- data/lib/clerq/gateways/gateway.rb +17 -0
- data/lib/clerq/gateways/in_files.rb +36 -0
- data/lib/clerq/gateways/in_memory.rb +35 -0
- data/lib/clerq/interactors.rb +5 -0
- data/lib/clerq/interactors/check_nodes.rb +81 -0
- data/lib/clerq/interactors/compile_nodes.rb +31 -0
- data/lib/clerq/interactors/create_node.rb +40 -0
- data/lib/clerq/interactors/interactor.rb +28 -0
- data/lib/clerq/interactors/join_nodes.rb +59 -0
- data/lib/clerq/interactors/query_nodes.rb +62 -0
- data/lib/clerq/properties.rb +21 -0
- data/lib/clerq/repositories.rb +5 -0
- data/lib/clerq/repositories/in_memory.rb +45 -0
- data/lib/clerq/repositories/node_reader.rb +107 -0
- data/lib/clerq/repositories/node_repository.rb +56 -0
- data/lib/clerq/repositories/repository.rb +11 -0
- data/lib/clerq/repositories/template_repository.rb +53 -0
- data/lib/clerq/settings.rb +56 -0
- data/lib/clerq/templater.rb +32 -0
- data/lib/clerq/version.rb +3 -0
- metadata +172 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
<%
|
2
|
+
# Template for drafts
|
3
|
+
require 'delegate'
|
4
|
+
class MarkupNode < SimpleDelegator
|
5
|
+
def title
|
6
|
+
'#' * nesting_level + ' ' + super
|
7
|
+
end
|
8
|
+
|
9
|
+
def meta
|
10
|
+
return '' if nesting_level == 0
|
11
|
+
return '' if super[:skip_meta]
|
12
|
+
|
13
|
+
hsh = {id: id}.merge(super)
|
14
|
+
hsh.delete(:order_index)
|
15
|
+
hsh.delete(:parent)
|
16
|
+
hsh.delete(:file_name)
|
17
|
+
[].tap{|ary|
|
18
|
+
ary << "Attribute | Value"
|
19
|
+
ary << "--------- | -----"
|
20
|
+
hsh.each{|k,v| ary << "#{k} | #{v}"}
|
21
|
+
}.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def body
|
25
|
+
return '' if super.empty?
|
26
|
+
# replace links
|
27
|
+
txt = String.new(super)
|
28
|
+
links.each{|l| txt.gsub!("[[#{l}]]", link(l))}
|
29
|
+
txt
|
30
|
+
end
|
31
|
+
|
32
|
+
def link(id)
|
33
|
+
r = find_node(id)
|
34
|
+
return "[#{id}](#unknown)" unless r
|
35
|
+
"[#{r.title}](##{url(r.id)})"
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [String] id Node id, when it starts with
|
39
|
+
# '.' it finds for first descendant by #end_with?
|
40
|
+
# '*' it finds first note by #end_with? from root
|
41
|
+
# @return [Node] or nil when node not found
|
42
|
+
def find_node(id)
|
43
|
+
return find{|n| n.id.end_with? id[1..-1]} if id.start_with? '.'
|
44
|
+
return root.find{|n| n.id.end_with? id[1..-1]} if id.start_with? '*'
|
45
|
+
root.find{|n| n.id.eql? id}
|
46
|
+
end
|
47
|
+
|
48
|
+
def url(id)
|
49
|
+
id.downcase
|
50
|
+
.gsub(/[^A-Za-z0-9]{1,}/, '-')
|
51
|
+
.gsub(/^-/, '')
|
52
|
+
.gsub(/-$/, '')
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
-%>
|
57
|
+
% <%= @object.title %>
|
58
|
+
% generated by Clerq on <%= Time.now.strftime('%B %e, %Y at %H:%M') %>
|
59
|
+
|
60
|
+
<% for @node in @object.to_a.drop(1) -%>
|
61
|
+
<% n = MarkupNode.new(@node) -%>
|
62
|
+
<%= [n.title, n.meta, n.body].select{|t| !t.empty?}.join("\n\n") %>
|
63
|
+
|
64
|
+
<% end %>
|
@@ -0,0 +1,93 @@
|
|
1
|
+
<%
|
2
|
+
require 'delegate'
|
3
|
+
# Template for generate documents by Pandoc
|
4
|
+
class MarkupNode < SimpleDelegator
|
5
|
+
def title
|
6
|
+
[].tap do |ary|
|
7
|
+
ary << '#' * (nesting_level + 1)
|
8
|
+
ary << '[' + id + ']' unless __getobj__.meta[:skip_meta]
|
9
|
+
ary << super
|
10
|
+
end.join(' ')
|
11
|
+
end
|
12
|
+
|
13
|
+
def meta
|
14
|
+
return '' if nesting_level == 0
|
15
|
+
return '' if super[:skip_meta]
|
16
|
+
|
17
|
+
hsh = {}.merge(super)
|
18
|
+
hsh.delete(:order_index)
|
19
|
+
hsh.delete(:file_name)
|
20
|
+
hsh.delete(:parent)
|
21
|
+
return '' if hsh.empty?
|
22
|
+
|
23
|
+
[].tap{|ary|
|
24
|
+
ary << "Attribute | Value"
|
25
|
+
ary << "--------- | -----"
|
26
|
+
hsh.each{|k,v| ary << "#{k} | #{v}"}
|
27
|
+
}.join("\n")
|
28
|
+
end
|
29
|
+
|
30
|
+
def body
|
31
|
+
# links replacement
|
32
|
+
txt = String.new(super)
|
33
|
+
links.each{|l| txt.gsub! "[[#{l}]]", link(l)}
|
34
|
+
# macro substitution
|
35
|
+
macro.each{|m| txt.gsub! m.regex, m.substitution}
|
36
|
+
txt
|
37
|
+
end
|
38
|
+
|
39
|
+
def link(ref)
|
40
|
+
node = root.find{|n| n.id == ref}
|
41
|
+
return "[#{ref}](#unknown)" unless node
|
42
|
+
"[#{node.title}](##{url(node.title)})"
|
43
|
+
end
|
44
|
+
|
45
|
+
def url(id)
|
46
|
+
id.downcase
|
47
|
+
.gsub(/[^A-Za-z0-9]{1,}/, '-')
|
48
|
+
.gsub(/^-/, '')
|
49
|
+
.gsub(/-$/, '')
|
50
|
+
end
|
51
|
+
|
52
|
+
class Macro
|
53
|
+
attr_reader :regex
|
54
|
+
def initialize(regex, &substitution)
|
55
|
+
@regex, @substitution = regex, substitution
|
56
|
+
end
|
57
|
+
|
58
|
+
def substitution
|
59
|
+
@substitution.call()
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def macro
|
64
|
+
[].tap do |ary|
|
65
|
+
ary << Macro.new(/{{@@skip[\s\S]*?}}/, &Proc.new{ '' })
|
66
|
+
ary << Macro.new("{{@@list}}", &Proc.new{list})
|
67
|
+
ary << Macro.new("{{@@tree}}", &Proc.new{tree})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def tree
|
72
|
+
this_level = nesting_level + 1
|
73
|
+
to_a.drop(1).inject([]) do |ary, n|
|
74
|
+
lead_spaces = ' ' * (n.nesting_level - this_level)
|
75
|
+
ary << "#{lead_spaces}* [#{n.title}](##{url(n.id)})"
|
76
|
+
end.join("\n")
|
77
|
+
end
|
78
|
+
|
79
|
+
def list
|
80
|
+
items.inject([]) do |ary, n|
|
81
|
+
ary << "* [#{n.title}](##{url(n.id)})"
|
82
|
+
end.join("\n")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
-%>
|
86
|
+
% <%= @object.title %>
|
87
|
+
% generated by Clerq on <%= Time.now.strftime('%B %e, %Y at %H:%M') %>
|
88
|
+
|
89
|
+
<% for @node in @object.to_a.drop(1) -%>
|
90
|
+
<% n = MarkupNode.new(@node) -%>
|
91
|
+
<%= [n.title, n.meta, n.body].select{|t| !t.empty?}.join("\n\n") %>
|
92
|
+
|
93
|
+
<% end %>
|
@@ -0,0 +1,88 @@
|
|
1
|
+
<%
|
2
|
+
require 'delegate'
|
3
|
+
# Template for generate documents by Pandoc
|
4
|
+
class MarkupNode < SimpleDelegator
|
5
|
+
def title
|
6
|
+
"#{'#' * nesting_level} #{super} {##{url(id)}}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def meta
|
10
|
+
return '' if nesting_level == 0
|
11
|
+
return '' if super[:skip_meta]
|
12
|
+
|
13
|
+
hsh = {id: id}.merge(super)
|
14
|
+
hsh.delete(:order_index)
|
15
|
+
hsh.delete(:file_name)
|
16
|
+
hsh.delete(:parent)
|
17
|
+
[].tap{|ary|
|
18
|
+
ary << "Attribute | Value"
|
19
|
+
ary << "--------- | -----"
|
20
|
+
hsh.each{|k,v| ary << "#{k} | #{v}"}
|
21
|
+
}.join("\n")
|
22
|
+
end
|
23
|
+
|
24
|
+
def body
|
25
|
+
# links replacement
|
26
|
+
txt = String.new(super)
|
27
|
+
links.each{|l| txt.gsub! "[[#{l}]]", link(l)}
|
28
|
+
# macro substitution
|
29
|
+
macro.each{|m| txt.gsub! m.regex, m.substitution}
|
30
|
+
txt
|
31
|
+
end
|
32
|
+
|
33
|
+
def link(ref)
|
34
|
+
node = root.find{|n| n.id == ref}
|
35
|
+
return "[#{ref}](#unknown)" unless node
|
36
|
+
"[#{node.title}](##{url(ref)})"
|
37
|
+
end
|
38
|
+
|
39
|
+
def url(id)
|
40
|
+
id = 'p' + id if id[0] == '0'
|
41
|
+
id.downcase
|
42
|
+
.gsub(/[^A-Za-z0-9]{1,}/, '-')
|
43
|
+
.gsub(/^-/, '')
|
44
|
+
.gsub(/-$/, '')
|
45
|
+
end
|
46
|
+
|
47
|
+
class Macro
|
48
|
+
attr_reader :regex
|
49
|
+
def initialize(regex, &substitution)
|
50
|
+
@regex, @substitution = regex, substitution
|
51
|
+
end
|
52
|
+
|
53
|
+
def substitution
|
54
|
+
@substitution.call()
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def macro
|
59
|
+
[].tap do |ary|
|
60
|
+
ary << Macro.new(/{{@@skip[\s\S]*?}}/, &Proc.new{ '' })
|
61
|
+
ary << Macro.new("{{@@list}}", &Proc.new{list})
|
62
|
+
ary << Macro.new("{{@@tree}}", &Proc.new{tree})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def tree
|
67
|
+
this_level = nesting_level + 1
|
68
|
+
to_a.drop(1).inject([]) do |ary, n|
|
69
|
+
lead_spaces = ' ' * (n.nesting_level - this_level)
|
70
|
+
ary << "#{lead_spaces}* [#{n.title}](##{url(n.id)})"
|
71
|
+
end.join("\n")
|
72
|
+
end
|
73
|
+
|
74
|
+
def list
|
75
|
+
items.inject([]) do |ary, n|
|
76
|
+
ary << "* [#{n.title}](##{url(n.id)})"
|
77
|
+
end.join("\n")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
-%>
|
81
|
+
% <%= @object.title %>
|
82
|
+
% generated by Clerq on <%= Time.now.strftime('%B %e, %Y at %H:%M') %> using 'pandoc.md.erb' template
|
83
|
+
|
84
|
+
<% for @node in @object.to_a.drop(1) -%>
|
85
|
+
<% n = MarkupNode.new(@node) -%>
|
86
|
+
<%= [n.title, n.meta, n.body].select{|t| !t.empty?}.join("\n\n") %>
|
87
|
+
|
88
|
+
<% end %>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<%
|
2
|
+
# template for saving created nodes
|
3
|
+
require 'delegate'
|
4
|
+
class MarkupNode < SimpleDelegator
|
5
|
+
def title
|
6
|
+
'#' * (nesting_level + 1) + ' [' + id + '] ' + super
|
7
|
+
end
|
8
|
+
|
9
|
+
def meta
|
10
|
+
return '' if super.empty?
|
11
|
+
[].tap{|ary|
|
12
|
+
ary << '{{'
|
13
|
+
hsh.each{|k,v| ary << "#{k}: #{v}"}
|
14
|
+
ary << '}}'
|
15
|
+
}.join("\n")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
-%>
|
19
|
+
<% for @node in @object.to_a.drop(1) -%>
|
20
|
+
<% n = MarkupNode.new(@node) -%>
|
21
|
+
<%= [n.title, n.meta, n.body].select{|t| !t.empty?}.join("\n\n") %>
|
22
|
+
|
23
|
+
<% end %>
|
data/lib/clerq.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative 'clerq/version'
|
2
|
+
require_relative 'clerq/entities'
|
3
|
+
require_relative 'clerq/gateways'
|
4
|
+
require_relative 'clerq/interactors'
|
5
|
+
require_relative 'clerq/repositories'
|
6
|
+
require_relative 'clerq/properties'
|
7
|
+
require_relative 'clerq/settings'
|
8
|
+
require_relative 'clerq/templater'
|
9
|
+
require_relative 'clerq/cli'
|
10
|
+
|
11
|
+
module Clerq
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def root
|
17
|
+
File.dirname __dir__
|
18
|
+
end
|
19
|
+
|
20
|
+
def gateway
|
21
|
+
@gateway ||= Clerq::Gateways::InFiles.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def gateway=(gateway)
|
25
|
+
errmsg = "Invalid argument! Clark::Gateway required"
|
26
|
+
raise ArgumentError, errmsg unless gateway.is_a? Clerq::Gateways::Gateway
|
27
|
+
@gateway = gateway
|
28
|
+
end
|
29
|
+
|
30
|
+
def settings
|
31
|
+
@settings ||= Settings.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def reset
|
35
|
+
@gateway = nil
|
36
|
+
@settings = nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/clerq/cli.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require_relative 'interactors'
|
5
|
+
include Clerq::Interactors
|
6
|
+
|
7
|
+
module Clerq
|
8
|
+
|
9
|
+
class Cli < Thor
|
10
|
+
include Thor::Actions
|
11
|
+
namespace :clerq
|
12
|
+
|
13
|
+
def self.source_root
|
14
|
+
File.join Clerq.root, "lib/assets"
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "--version, -v", "Print the version"
|
18
|
+
def version
|
19
|
+
puts "Clerq v#{Clerq::VERSION}"
|
20
|
+
end
|
21
|
+
map %w[--version -v] => :version
|
22
|
+
|
23
|
+
desc "new PROJECT", "Create a new Clerq project"
|
24
|
+
def new(project)
|
25
|
+
say "Creating project '#{project}'..."
|
26
|
+
|
27
|
+
if Dir.exist?(project)
|
28
|
+
error "Directory '#{project}' already exists!"
|
29
|
+
return
|
30
|
+
end
|
31
|
+
|
32
|
+
settings = Clerq.settings
|
33
|
+
tts = [
|
34
|
+
{tt: 'new/README.md.tt', target: 'README.md'},
|
35
|
+
{tt: 'new/clerq.yml.tt', target: 'clerq.yml'},
|
36
|
+
{tt: 'new/clerq.thor.tt', target: "#{project}.thor"},
|
37
|
+
{tt: 'new/content.md.tt', target: File.join(settings.src, "#{project}.md")}
|
38
|
+
]
|
39
|
+
|
40
|
+
Dir.mkdir(project)
|
41
|
+
Dir.chdir(project) do
|
42
|
+
settings.folders.each{|f| Dir.mkdir(f)}
|
43
|
+
tts.each do |tt|
|
44
|
+
template(tt[:tt], File.join(Dir.pwd, tt[:target]), {project: project})
|
45
|
+
end
|
46
|
+
directory('tt', File.join(Dir.pwd, 'tt'))
|
47
|
+
say "Project created!"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc "promo", "Copy content of promo project"
|
52
|
+
def promo
|
53
|
+
say "Copying promo content ..."
|
54
|
+
directory('promo', Dir.pwd)
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "build [OPTIONS]", "Build the clerq project"
|
58
|
+
method_option :query, aliases: "-q", type: :string, desc: "query"
|
59
|
+
method_option :tt, aliases: "-t", type: :string, desc: "template"
|
60
|
+
method_option :output, aliases: "-o", type: :string, desc: "output file"
|
61
|
+
def build
|
62
|
+
settings = Clerq.settings
|
63
|
+
document = options[:output] || settings.document + '.md'
|
64
|
+
template = options[:tt] || settings.template
|
65
|
+
query = options[:query] || ''
|
66
|
+
build = File.join(settings.bin, document)
|
67
|
+
content = CompileNodes.(template: template, query: query)
|
68
|
+
File.write(build, content)
|
69
|
+
say "'#{build}' created!"
|
70
|
+
rescue CompileNodes::Failure => e
|
71
|
+
error e.message
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "check", "Check the project for errors"
|
75
|
+
def check
|
76
|
+
errors = CheckNodes.()
|
77
|
+
if errors.empty?
|
78
|
+
say "No errors found"
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
CHECK_MESSAGES.each do |key, msg|
|
83
|
+
if errors.key?(key)
|
84
|
+
say "The following #{msg[0]}:"
|
85
|
+
errors[key].each{|k,v| say "\t#{k}\t#{msg[1]} #{v.join(', ')}"}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
rescue CheckNodes::Failure => e
|
89
|
+
error e.message
|
90
|
+
end
|
91
|
+
|
92
|
+
CHECK_MESSAGES = {
|
93
|
+
nonuniq_ids: ['node identifiers are non-uniqe', 'in'],
|
94
|
+
unknown_parents: ['meta[:parent] not found', 'in'],
|
95
|
+
unknown_references: ['links are unknown', 'in'],
|
96
|
+
unknown_order_index: ['node meta[:order_index] unknown', ':']
|
97
|
+
}.freeze
|
98
|
+
|
99
|
+
desc "node ID [TITLE]", "Create a new node"
|
100
|
+
method_option :template, aliases: "-t", type: :string, desc: "template"
|
101
|
+
def node(id, title = '')
|
102
|
+
settings = Clerq.settings
|
103
|
+
file = File.join(settings.src, "#{id}.md")
|
104
|
+
if File.exist?(file)
|
105
|
+
error "File already exists #{fn}"
|
106
|
+
return
|
107
|
+
end
|
108
|
+
template = options[:template] || ''
|
109
|
+
CreateNode.(id: id, title: title, template: template)
|
110
|
+
say "'#{file}' created"
|
111
|
+
rescue CreateNode::Failure => e
|
112
|
+
error e.message
|
113
|
+
end
|
114
|
+
|
115
|
+
desc "toc [OPTIONS]", "Print the project TOC"
|
116
|
+
method_option :query, aliases: "-q", type: :string, desc: "Query"
|
117
|
+
def toc
|
118
|
+
query = options[:query]
|
119
|
+
node = query ? QueryNodes.(query: query) : JoinNodes.()
|
120
|
+
puts "% #{node.title}"
|
121
|
+
node.to_a.drop(1).each{|n|
|
122
|
+
puts "#{' ' * (n.nesting_level - 1)}[#{n.id}] #{n.title}"
|
123
|
+
}
|
124
|
+
rescue QueryNodes::Failure, JoinNodes::Failure => e
|
125
|
+
error e.message
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|