clerq 0.2.0 → 0.3.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 +4 -4
- data/.gitignore +4 -1
- data/CHANGELOG.md +14 -2
- data/Gemfile.lock +24 -0
- data/README.md +107 -30
- data/clerq.thor +28 -0
- data/docs/README.md +408 -0
- data/lib/assets/knb/business-case.md +135 -0
- data/lib/assets/knb/requirement-life-cycle.md +47 -0
- data/lib/assets/knb/vision-document.md +191 -0
- data/lib/assets/lib/clerq_doc.thor +119 -0
- data/lib/assets/lib/colonize_repo.rb +82 -0
- data/lib/assets/lib/spec/colonize_repo_spec.rb +85 -0
- data/lib/assets/new/content.md.tt +1 -0
- data/lib/assets/tt/default.md.erb +1 -1
- data/lib/assets/tt/pandoc.md.erb +6 -6
- data/lib/clerq.rb +7 -2
- data/lib/clerq/cli.rb +33 -47
- data/lib/clerq/entities/node.rb +11 -5
- data/lib/clerq/repositories.rb +0 -1
- data/lib/clerq/repositories/file_repository.rb +1 -0
- data/lib/clerq/repositories/node_repository.rb +7 -6
- data/lib/clerq/services.rb +8 -0
- data/lib/clerq/services/check_assembly.rb +108 -0
- data/lib/clerq/{interactors → services}/create_node.rb +4 -5
- data/lib/clerq/services/load_assembly.rb +54 -0
- data/lib/clerq/{interactors/query_assembly.rb → services/query_node.rb} +15 -12
- data/lib/clerq/{interactors → services}/query_template.rb +3 -8
- data/lib/clerq/services/read_node.rb +98 -0
- data/lib/clerq/services/render_erb.rb +29 -0
- data/lib/clerq/{interactors/render_assembly.rb → services/render_node.rb} +8 -12
- data/lib/clerq/services/service.rb +19 -0
- data/lib/clerq/version.rb +1 -1
- metadata +21 -12
- data/TODO.md +0 -7
- data/lib/clerq/interactors.rb +0 -5
- data/lib/clerq/interactors/check_assembly.rb +0 -77
- data/lib/clerq/interactors/interactor.rb +0 -26
- data/lib/clerq/render_erb.rb +0 -33
- data/lib/clerq/repositories/node_reader.rb +0 -107
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'clerq'
|
2
|
+
require 'erb'
|
3
|
+
include Clerq::Entities
|
4
|
+
include Clerq::Services
|
5
|
+
|
6
|
+
# Creates clerq repository sources in current work directory
|
7
|
+
# Warning! Change work dierectory before calling for the serice
|
8
|
+
# Usage
|
9
|
+
# node = Clerq.node_repository.assembly
|
10
|
+
# Dir.chdir(Clerq.src) { ColonizeRepo.(node) }
|
11
|
+
class ColonizeRepo < Service
|
12
|
+
private_class_method :new
|
13
|
+
|
14
|
+
def call
|
15
|
+
write(@node)
|
16
|
+
end
|
17
|
+
|
18
|
+
# TODO callback? and calback for ReadNode.()?
|
19
|
+
def write(node)
|
20
|
+
dir = folder(node)
|
21
|
+
src = source(node)
|
22
|
+
txt = text(node)
|
23
|
+
unless dir.empty? || Dir.exist?(dir)
|
24
|
+
Dir.mkdir(dir)
|
25
|
+
@on_create_dir.call(dir) if @on_create_dir
|
26
|
+
end
|
27
|
+
File.write(src, txt)
|
28
|
+
@on_create_file.call(src) if @on_create_file
|
29
|
+
node.items.reject{|n| n.items.empty?}.each{|n| write(n)}
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param node [Node] the node for colonization
|
33
|
+
# @param on_create_file [Block(arg)] on create new file callback
|
34
|
+
# @param on_create_dir [Block(arg)] on create new directory callback
|
35
|
+
def initialize(node, on_create_dir = nil, on_create_file = nil)
|
36
|
+
@node = node
|
37
|
+
@on_create_dir = on_create_dir
|
38
|
+
@on_create_file = on_create_file
|
39
|
+
end
|
40
|
+
|
41
|
+
def source(node)
|
42
|
+
fld = folder(node)
|
43
|
+
src = filename(node)
|
44
|
+
fld.empty? ? src : File.join(fld, src)
|
45
|
+
end
|
46
|
+
|
47
|
+
def folder(node)
|
48
|
+
dir = ''
|
49
|
+
n = node
|
50
|
+
while n != @node.root && n.parent != @node.root
|
51
|
+
dir = File.join("#{n.parent.id} #{n.parent.title}", dir)
|
52
|
+
n = n.parent
|
53
|
+
end
|
54
|
+
dir
|
55
|
+
end
|
56
|
+
|
57
|
+
def filename(node)
|
58
|
+
"#{node.id} #{node.title}.md"
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO replace to services
|
62
|
+
def text(node)
|
63
|
+
RenderErb.(erb: TEMPLATE, object: node)
|
64
|
+
end
|
65
|
+
|
66
|
+
TEMPLATE = <<~EOF
|
67
|
+
# <%= @object.title %>
|
68
|
+
{{id: <%= @object.id %><%= ", parent: " + @object.parent.id if @object.parent %>, order_index: <%= @object.items.map(&:id).join(' ') %>}}
|
69
|
+
|
70
|
+
<%= @object.body %>
|
71
|
+
|
72
|
+
<% for n in @object.items -%>
|
73
|
+
<% next unless n.items.empty? -%>
|
74
|
+
## <%= n.title %>
|
75
|
+
{{id: <%= n.id %>}}
|
76
|
+
|
77
|
+
<%= n.body %>
|
78
|
+
|
79
|
+
<% end %>
|
80
|
+
EOF
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'clerq'
|
2
|
+
require 'erb'
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require_relative 'colonize_repo'
|
5
|
+
include Clerq::Entities
|
6
|
+
include Clerq::Services
|
7
|
+
|
8
|
+
describe ColonizeRepo do
|
9
|
+
class FakeWriter < ColonizeRepo
|
10
|
+
public_class_method :new
|
11
|
+
attr_reader :node
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:node) {
|
15
|
+
Node.new(id: '0', title: 'Import').tap{|n|
|
16
|
+
n << Node.new(id: '01', title: 'User')
|
17
|
+
n << Node.new(id: '02', title: 'Func')
|
18
|
+
n.item('01') << Node.new(id: '01.01', title: 'Story 1')
|
19
|
+
n.item('01') << Node.new(id: '01.02', title: 'Story 2')
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
let(:writer) { FakeWriter.new(node) }
|
24
|
+
|
25
|
+
describe '#folder' do
|
26
|
+
it 'must return parent.id + parent.name' do
|
27
|
+
spec = writer.node.map{|n| writer.folder(n)}
|
28
|
+
_(spec).must_equal([
|
29
|
+
'', '', '01 User/', '01 User/', ''
|
30
|
+
])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#filename' do
|
35
|
+
it 'must return node.id + node.title' do
|
36
|
+
spec = writer.node.map{|n| writer.filename(n)}
|
37
|
+
_(spec).must_equal([
|
38
|
+
'0 Import.md', '01 User.md', '01.01 Story 1.md',
|
39
|
+
'01.02 Story 2.md', '02 Func.md'
|
40
|
+
])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#source' do
|
45
|
+
# title and first level shall be paced in root folder
|
46
|
+
it 'must return node.id + node.title' do
|
47
|
+
spec = writer.node.map{|n| writer.source(n)}
|
48
|
+
_(spec).must_equal([
|
49
|
+
'0 Import.md',
|
50
|
+
'01 User.md',
|
51
|
+
'01 User/01.01 Story 1.md',
|
52
|
+
'01 User/01.02 Story 2.md',
|
53
|
+
'02 Func.md'
|
54
|
+
])
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#write(node)' do
|
59
|
+
let(:node) {
|
60
|
+
Node.new(id: '0', title: 'Import').tap{|n|
|
61
|
+
n << Node.new(id: '01', title: 'User')
|
62
|
+
n << Node.new(id: '02', title: 'Func')
|
63
|
+
n.item('01') << Node.new(id: '01.01', title: 'Story 1')
|
64
|
+
n.item('01') << Node.new(id: '01.02', title: 'Story 2')
|
65
|
+
n.node('01.01') << Node.new(id: '01.01.01', title: 'Story 1.1')
|
66
|
+
n.node('01.01') << Node.new(id: '01.01.02', title: 'Story 1.2')
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
it 'must build repo' do
|
71
|
+
files = ['0 Import.md', '01 User.md', '01 User/01.01 Story 1.md']
|
72
|
+
Dir.mktmpdir(['clerq']) do |dir|
|
73
|
+
Dir.chdir(dir) do
|
74
|
+
ColonizeRepo.(node)
|
75
|
+
files.each{|fn| _(File.exist?(fn)).must_equal true }
|
76
|
+
_(File.read('0 Import.md')).wont_match "parent:"
|
77
|
+
_(File.read('01 User.md')).must_match "parent: 0"
|
78
|
+
_(File.read('01 User/01.01 Story 1.md')).must_match "parent: 01"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
1
|
# <%=config[:project]%>
|
2
|
+
{{id: 0}}
|
2
3
|
|
3
4
|
This file was created automatically by the `clerq new` command as the root node of your project. To familiarize yourself with how to start writing with the Clerq - please read the [Writing](https://github.com/nvoynov/clerq/blob/master/README.md#writing) section and then just delete the content of the paragraph.
|
data/lib/assets/tt/pandoc.md.erb
CHANGED
@@ -12,7 +12,7 @@ class MarkupNode < SimpleDelegator
|
|
12
12
|
|
13
13
|
hsh = {id: id}.merge(super)
|
14
14
|
hsh.delete(:order_index)
|
15
|
-
hsh.delete(:
|
15
|
+
hsh.delete(:filename)
|
16
16
|
hsh.delete(:parent)
|
17
17
|
[].tap{|ary|
|
18
18
|
ary << "Attribute | Value"
|
@@ -37,11 +37,11 @@ class MarkupNode < SimpleDelegator
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def url(id)
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
r = id.start_with?(/[[:digit:]]/) ? "p#{id}" : id
|
41
|
+
r.downcase
|
42
|
+
.gsub(/[^A-Za-z0-9]{1,}/, '-')
|
43
|
+
.gsub(/^-/, '')
|
44
|
+
.gsub(/-$/, '')
|
45
45
|
end
|
46
46
|
|
47
47
|
class Macro
|
data/lib/clerq.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require_relative 'clerq/version'
|
2
2
|
require_relative 'clerq/entities'
|
3
|
-
require_relative 'clerq/
|
3
|
+
require_relative 'clerq/services'
|
4
4
|
require_relative 'clerq/repositories'
|
5
5
|
require_relative 'clerq/properties'
|
6
6
|
require_relative 'clerq/settings'
|
7
|
-
require_relative 'clerq/render_erb'
|
8
7
|
require_relative 'clerq/cli'
|
9
8
|
include Clerq::Repositories
|
10
9
|
|
@@ -76,6 +75,12 @@ module Clerq
|
|
76
75
|
@node_repository = repository
|
77
76
|
end
|
78
77
|
|
78
|
+
def assemble(on_parse: nil, on_error: nil)
|
79
|
+
node_repository.assemble(
|
80
|
+
on_parse: on_parse,
|
81
|
+
on_error: on_error)
|
82
|
+
end
|
83
|
+
|
79
84
|
end
|
80
85
|
|
81
86
|
end
|
data/lib/clerq/cli.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
require 'thor'
|
4
|
-
require_relative '
|
5
|
-
include Clerq::
|
4
|
+
require_relative 'services'
|
5
|
+
include Clerq::Services
|
6
6
|
|
7
7
|
module Clerq
|
8
8
|
|
@@ -23,7 +23,7 @@ module Clerq
|
|
23
23
|
no_commands {
|
24
24
|
# @param [String]
|
25
25
|
# @returns [String] usual name for ruby file
|
26
|
-
def
|
26
|
+
def thor_filename(str);
|
27
27
|
str.split(/[\W+_]/).map(&:downcase).join('_') + '.thor'
|
28
28
|
end
|
29
29
|
|
@@ -44,6 +44,13 @@ module Clerq
|
|
44
44
|
def stop!(msg)
|
45
45
|
raise Thor::Error, msg
|
46
46
|
end
|
47
|
+
|
48
|
+
def query_assembly(query)
|
49
|
+
# TODO pretty errors ...OK, ... 1 error found, ... 2 errors found
|
50
|
+
on_parse = lambda {|src| puts "Reading '#{src}'... "}
|
51
|
+
on_error = lambda {|err| puts "\terror: #{err} "}
|
52
|
+
QueryAssembly.(query: query, on_parse: on_parse, on_error: on_error)
|
53
|
+
end
|
47
54
|
}
|
48
55
|
|
49
56
|
desc "new PROJECT", "Create a new Clerq project"
|
@@ -55,7 +62,7 @@ module Clerq
|
|
55
62
|
tts = [
|
56
63
|
{tt: 'new/README.md.tt', target: 'README.md'},
|
57
64
|
{tt: 'new/clerq.yml.tt', target: Clerq::Settings::STORAGE},
|
58
|
-
{tt: 'new/clerq.thor.tt', target:
|
65
|
+
{tt: 'new/clerq.thor.tt', target: thor_filename(project)},
|
59
66
|
{tt: 'new/content.md.tt', target: File.join(settings.src, "#{project}.md")}
|
60
67
|
]
|
61
68
|
|
@@ -68,6 +75,7 @@ module Clerq
|
|
68
75
|
template(tt[:tt], File.join(Dir.pwd, tt[:target]), config)
|
69
76
|
end
|
70
77
|
directory('tt', File.join(Dir.pwd, 'tt'))
|
78
|
+
directory('lib', File.join(Dir.pwd, 'lib'))
|
71
79
|
say "Project created!"
|
72
80
|
end
|
73
81
|
end
|
@@ -87,68 +95,46 @@ module Clerq
|
|
87
95
|
stop_unless_clerq!
|
88
96
|
document = options[:output] || Clerq.document + '.md'
|
89
97
|
template = options[:tt] || Clerq.template
|
90
|
-
query = options[:query] || ''
|
91
98
|
build = File.join(Clerq.bin, document)
|
92
|
-
|
93
|
-
|
99
|
+
|
100
|
+
node = LoadAssembly.()
|
101
|
+
node = QueryNode.(assembly: node, query: options[:query]) if options[:query]
|
102
|
+
text = RenderNode.(node: node, template: template)
|
103
|
+
File.write(build, text)
|
94
104
|
say "'#{build}' created!"
|
95
|
-
rescue
|
105
|
+
rescue StandardError => e
|
96
106
|
stop!(e.message)
|
97
107
|
end
|
98
108
|
|
99
109
|
desc "check", "Check the project for errors"
|
100
110
|
def check
|
101
111
|
stop_unless_clerq!
|
102
|
-
|
103
|
-
|
104
|
-
say "No errors found"
|
105
|
-
return
|
106
|
-
end
|
107
|
-
|
108
|
-
CHECK_MESSAGES.each do |key, msg|
|
109
|
-
if errors.key?(key)
|
110
|
-
say "The following #{msg[0]}:"
|
111
|
-
errors[key].each{|k,v| say "\t#{k}\t#{msg[1]} #{v.join(', ')}"}
|
112
|
-
end
|
113
|
-
end
|
114
|
-
rescue CheckAssembly::Failure => e
|
115
|
-
stop!(e.message)
|
116
|
-
end
|
117
|
-
|
118
|
-
CHECK_MESSAGES = {
|
119
|
-
nonuniq_ids: ['node identifiers are non-uniqe', 'in'],
|
120
|
-
unknown_parents: ['meta[:parent] not found', 'in'],
|
121
|
-
unknown_references: ['links are unknown', 'in'],
|
122
|
-
unknown_order_index: ['node meta[:order_index] unknown', ':']
|
123
|
-
}.freeze
|
124
|
-
|
125
|
-
desc "node ID [TITLE]", "Create a new node"
|
126
|
-
method_option :template, aliases: "-t", type: :string, desc: "template"
|
127
|
-
def node(id, title = '')
|
128
|
-
stop_unless_clerq!
|
129
|
-
# smells! smells! smells!
|
130
|
-
# TODO interactor must return file name
|
131
|
-
# TODO must not check if file exists - it task of repository
|
132
|
-
file = File.join(Clerq.src, "#{id}.md")
|
133
|
-
stop!("File already exists #{fn}") if File.exist?(file)
|
134
|
-
template = options[:template] || ''
|
135
|
-
CreateNode.(id: id, title: title, template: template)
|
136
|
-
say "'#{file}' created"
|
137
|
-
rescue CreateNode::Failure => e
|
138
|
-
stop!(e.message)
|
112
|
+
puts "Checking assembly for writing errors..."
|
113
|
+
CheckAssembly.(LoadAssembly.())
|
139
114
|
end
|
140
115
|
|
141
116
|
desc "toc [OPTIONS]", "Print the project TOC"
|
142
117
|
method_option :query, aliases: "-q", type: :string, desc: "Query"
|
143
118
|
def toc
|
144
119
|
stop_unless_clerq!
|
145
|
-
node =
|
120
|
+
node = LoadAssembly.()
|
121
|
+
node = QueryNode.(assembly: node, query: options[:query]) if options[:query]
|
146
122
|
puts "% #{node.title}"
|
147
123
|
puts "% #{node[:query]}" if node[:query]
|
148
124
|
node.to_a.drop(1).each{|n|
|
149
125
|
puts "#{' ' * (n.nesting_level - 1)}[#{n.id}] #{n.title}"
|
150
126
|
}
|
151
|
-
rescue
|
127
|
+
rescue StandardError => e
|
128
|
+
stop!(e.message)
|
129
|
+
end
|
130
|
+
|
131
|
+
desc "node ID [TITLE]", "Create a new node"
|
132
|
+
method_option :template, aliases: "-t", type: :string, desc: "template"
|
133
|
+
def node(id, title = '')
|
134
|
+
stop_unless_clerq!
|
135
|
+
fn = CreateNode.(id: id, title: title, template: options[:template] || '')
|
136
|
+
say "'#{fn}' created"
|
137
|
+
rescue StandardError => e
|
152
138
|
stop!(e.message)
|
153
139
|
end
|
154
140
|
|
data/lib/clerq/entities/node.rb
CHANGED
@@ -44,6 +44,8 @@ module Clerq
|
|
44
44
|
raise ArgumentError, "Invalid argument :title" unless title.is_a? String
|
45
45
|
raise ArgumentError, "Invalid argument :body" unless body.is_a? String
|
46
46
|
raise ArgumentError, "Invalid argument :meta" unless meta.is_a? Hash
|
47
|
+
id = meta.delete(:id) if id.empty? && meta[:id]
|
48
|
+
meta.delete(:id) unless id.empty?
|
47
49
|
@parent = nil
|
48
50
|
@items = []
|
49
51
|
@id = id
|
@@ -68,16 +70,20 @@ module Clerq
|
|
68
70
|
@items.find{|r| r.id.eql? id}
|
69
71
|
end
|
70
72
|
|
73
|
+
# @return [Array<String>] of ids from meta[:order_index]
|
74
|
+
def order_index
|
75
|
+
return [] unless @meta[:order_index]
|
76
|
+
@meta[:order_index].strip.gsub(/[\s]{2,}/, ' ').split(/\s/)
|
77
|
+
end
|
78
|
+
|
71
79
|
# @return [Array<Node>] list of child nodes; when the node
|
72
80
|
# metadate has :order_index arrtibute, the list will be
|
73
81
|
# ordered according the attribute value
|
74
82
|
def items
|
75
|
-
return @items if @items.empty?
|
76
|
-
return @items if @meta[:order_index].nil?
|
77
|
-
source = Array.new(@items)
|
78
|
-
order = @meta[:order_index]
|
83
|
+
return @items if @items.empty? || order_index.empty?
|
79
84
|
[].tap do |ordered|
|
80
|
-
|
85
|
+
source = Array.new(@items)
|
86
|
+
order_index.each do |o|
|
81
87
|
e = source.delete(item(o))
|
82
88
|
ordered << e if e
|
83
89
|
end
|
data/lib/clerq/repositories.rb
CHANGED
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
require_relative '../entities'
|
4
4
|
require_relative 'file_repository'
|
5
|
-
require_relative '
|
5
|
+
require_relative '../services/read_node'
|
6
6
|
include Clerq::Entities
|
7
|
+
include Clerq::Services
|
7
8
|
|
8
9
|
module Clerq
|
9
10
|
module Repositories
|
@@ -21,9 +22,9 @@ module Clerq
|
|
21
22
|
|
22
23
|
# asseble repository nodes hierarchy
|
23
24
|
# @return [Node]
|
24
|
-
def assemble
|
25
|
+
def assemble(on_parse: nil, on_error: nil)
|
25
26
|
@node = Node.new(id: 'join', title: Clerq.title)
|
26
|
-
loadn = load
|
27
|
+
loadn = load(on_parse: on_parse, on_error: on_error)
|
27
28
|
loadn.each{|n| @node << n}
|
28
29
|
subo!
|
29
30
|
eqid!
|
@@ -36,12 +37,12 @@ module Clerq
|
|
36
37
|
|
37
38
|
protected
|
38
39
|
|
39
|
-
def load
|
40
|
+
def load(on_parse: nil, on_error: nil)
|
40
41
|
inside do
|
41
42
|
[].tap do |ary|
|
42
43
|
glob.each do |file|
|
43
|
-
|
44
|
-
tmp =
|
44
|
+
on_parse.call(file) if on_parse
|
45
|
+
tmp = ReadNode.(file, on_error)
|
45
46
|
tmp.each{|node| ary << node }
|
46
47
|
end
|
47
48
|
end
|