clerq 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65f2b9b019f3810c02e66f1ed3eba1d6b706ad8a63be53c2ff55a88665816941
|
4
|
+
data.tar.gz: 74a5363279f3487897c342a31f134b9f9051bdfac4f62fbe73b4239ec00f09d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1652c52a62a58dc18e58b7aae6f9f622d4e602811020bf91a04f3009cc2cfe055d372fe200ac746b2c2c4691c7a9c6e3b3f91c1a5f0586d08acaaa596e53c7e6
|
7
|
+
data.tar.gz: e55c6afa867a5d193b033fb53e009026f4200cb10d3d4bedf8d47a266a360bd6cb729298fd05520fc3224a92456b1c822aa6b8bca2c2012125f2c3d96ac66636
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,20 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 0.3.0 (2019-12-04)
|
4
|
+
|
5
|
+
* Meet services instead of interactors. All interactors removed and their responsibility moved to appropriate services.
|
6
|
+
* Refactored printing information about repository loading progress. Now `ReadNode.call(on_error: )` accepts `on_error` callback and you can provide any method proc or lambda there like `lambda {|err| puts err}`.
|
7
|
+
* Refactored previous behavior where interactors loaded repository by QueryAssembly interactor. Now it is responsibility of `LoadAssembly` service and other services that require repository just get it through parameter.
|
8
|
+
* `clerq new PROJECT` command brings the `lib\clerq_doc.thor` example of publishing and importing existing documents in the current clerq project repository. To see these just copy the file to root project folder near `<project>.thor` file.
|
9
|
+
|
10
|
+
## 0.2.1 (2019-11-29)
|
4
11
|
|
5
|
-
|
12
|
+
* Enhanced the `Node` class that brings the possibility to provide node id through `{{id: <id>}}` metadata attribute. But it will just skipped when id is already provided by `# [<id>]`.
|
13
|
+
* Enhanced `NodeReader` class; now it supports three metadata attributes delimiters - `\n`, `;`, and `,` that can be mixed.
|
14
|
+
* `CheckAssembly` interactor replaced by `CheckAssembly` service that provides improved error information with nodes ids and source files names.
|
15
|
+
* `file_name` attribute changed to `filename` in `NodeReader`.
|
16
|
+
|
17
|
+
## 0.2.0 (2019-11-23)
|
6
18
|
|
7
19
|
* Started new project [Clerq Video Guide](https://github.com/nvoynov/clerq-video-guide) that provides example of using Clerq.
|
8
20
|
* Done massive refactoring of source code; no more gateways.
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
clerq (0.3.0)
|
5
|
+
thor (~> 0.20.3)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
minitest (5.13.0)
|
11
|
+
rake (10.5.0)
|
12
|
+
thor (0.20.3)
|
13
|
+
|
14
|
+
PLATFORMS
|
15
|
+
x64-mingw32
|
16
|
+
|
17
|
+
DEPENDENCIES
|
18
|
+
bundler (~> 1.17)
|
19
|
+
clerq!
|
20
|
+
minitest (~> 5.0)
|
21
|
+
rake (~> 10.0)
|
22
|
+
|
23
|
+
BUNDLED WITH
|
24
|
+
1.17.3
|
data/README.md
CHANGED
@@ -47,7 +47,7 @@ The Clerq reads nodes from a set of separate files and assembles it to a single
|
|
47
47
|
|
48
48
|
The first convention is the scheme how a markdown content becomes the `Node` entity.
|
49
49
|
|
50
|
-
```
|
50
|
+
```markdown
|
51
51
|
# [p2] Part two
|
52
52
|
{{parent: top}}
|
53
53
|
|
@@ -62,6 +62,13 @@ Where
|
|
62
62
|
* `{{parent: top}}` in an optional metadata section that becomes `node.meta`;
|
63
63
|
* and finally `Body` is an optional `node.body`.
|
64
64
|
|
65
|
+
```markdown
|
66
|
+
# Part two
|
67
|
+
{{id: p1, parent: top}}
|
68
|
+
|
69
|
+
Body
|
70
|
+
```
|
71
|
+
|
65
72
|
Every new header (`#`) at any level indicates a new node. When a file contains headers of different levels, the nodes will be created in a natural hierarchy based on header levels. So as the result of reading the content below, the Clerq will create the natural hierarchy with root node `Top` that holds two child nodes `First` and `Second`.
|
66
73
|
|
67
74
|
```markdown
|
@@ -74,29 +81,45 @@ One more extra thing is links. You can place links to other nodes in the body se
|
|
74
81
|
|
75
82
|
#### IDs
|
76
83
|
|
77
|
-
|
84
|
+
To be able to build a hierarchy or to refer to other nodes, one needs each node to have its unique id. And you can pass it straight into markdown header `# [node id] node title` or provide it through `{{id: }}`.
|
78
85
|
|
79
|
-
ID can start with one dot, like `[.suffix]`, and clerq will
|
86
|
+
ID can start with one dot, like `[.suffix]`, and clerq will build the node id as `node.parent_id + node.id`.
|
80
87
|
|
81
|
-
|
82
|
-
|
88
|
+
When and ID is not provided, the Clerq will generate it automatically. Let's see the example of node:
|
89
|
+
|
90
|
+
```markdown
|
91
|
+
# User requirements
|
92
|
+
## Requirement 1
|
93
|
+
## Requirement 2
|
94
|
+
# Function requirements
|
83
95
|
## [cm] Components
|
84
96
|
### [.fm] File manager
|
85
97
|
### Logger
|
86
98
|
```
|
87
99
|
|
88
|
-
|
100
|
+
According to rules mentioned above the example will be translated as followed:
|
101
|
+
|
102
|
+
```markdown
|
103
|
+
# [01] User requirements
|
104
|
+
## [01.01] Requirement 1
|
105
|
+
## [01.02] Requirement 2
|
106
|
+
# [02] Function requirements
|
107
|
+
## [cm] Components
|
108
|
+
### [cm.fm] File manager
|
109
|
+
### [cm.01] Logger
|
110
|
+
```
|
89
111
|
|
90
112
|
#### Meta
|
91
113
|
|
92
|
-
The excerpt, the text in brackets `{{ }}` that follows by the header, contains node attributes. And the second convention mentioned in [Writing](#writing) section is
|
114
|
+
The excerpt, the text in brackets `{{ }}` that follows by the header, contains node attributes. And the second convention mentioned in [Writing](#writing) section is the followed magic metadata attributes that specify parameters of a hierarchy:
|
93
115
|
|
94
|
-
1. `
|
95
|
-
2. `
|
116
|
+
1. `id: <id>` specifies id through metadata; when in provided together with `# [<id>]`, the last has priority;
|
117
|
+
2. `parent: <id>` indicates that the node belongs to a node with specified `id`;
|
118
|
+
3. `order_index: <id1> <id2>` indicates that child nodes must be lined up in specified order.
|
96
119
|
|
97
120
|
You can place in metadata any simple string that suitable for providing additional information like status, originator, author, priority, etc. E.g.
|
98
121
|
|
99
|
-
```
|
122
|
+
```markdown
|
100
123
|
# [r.1]
|
101
124
|
{{parent: r, status: draft}}
|
102
125
|
|
@@ -197,40 +220,42 @@ A usual scenario will consist of two simple steps:
|
|
197
220
|
1. Get data hierarchy from the repository.
|
198
221
|
2. Do some processing of the hierarchy.
|
199
222
|
|
200
|
-
Instead of adding extra scripts files somewhere in the project, you can write tasks to `<project>.thor` file and access to them through `thor <project>:<your-task> [<params>]`.
|
201
|
-
|
202
223
|
#### Node class
|
203
224
|
|
204
225
|
The [Writing](#writing) section provides the basic knowledge to understand Clerq, and now it is the right time to see the [Node class](https://github.com/nvoynov/clerq/blob/master/lib/clerq/entities/node.rb). It implements the Composite pattern.
|
205
226
|
|
206
|
-
####
|
227
|
+
#### Services
|
207
228
|
|
208
|
-
Clerq provides
|
229
|
+
Clerq provides the following main service objects:
|
209
230
|
|
210
|
-
* `
|
231
|
+
* `LoadAssembly` loads whole repository to Node class;
|
211
232
|
* `CheckAssembly` checks the assembly for errors (ids and links);
|
212
|
-
* `
|
233
|
+
* `QueryNode` provides ability to query nodes from assembly;
|
234
|
+
* `QueryTemplate` return template by the template name;
|
213
235
|
* `CreateNode` crates new node in the repository;
|
214
|
-
* `
|
236
|
+
* `RenderNode` return text rendered by ERB.
|
215
237
|
|
216
|
-
The first part of each repository related task is to get repository assembly. It can be performed through `NodeRepository#assemble` or `
|
238
|
+
The first part of each repository related task is to get repository assembly. It can be performed through `NodeRepository#assemble` or `LoadAssembly.call()`. Each of these methods returns Node that provides [Enumerable](https://ruby-doc.org/core-2.6.5/Enumerable.html) interface.
|
217
239
|
|
218
|
-
Let's
|
240
|
+
Let's see an example. Assume that you are developing a "User requirements document" and the project policy requires that each user requirement must have the parameter called `originator`. You can write the policy as followed:
|
219
241
|
|
220
242
|
```ruby
|
221
243
|
require 'clerq'
|
222
|
-
include
|
244
|
+
include Clerq::Services
|
223
245
|
|
224
246
|
# supposed you have something like user requirements document
|
225
|
-
node =
|
247
|
+
node = LoadAssembly.()
|
248
|
+
node = QueryNode.(node: node, query: "node.title == 'User requirements'")
|
226
249
|
miss = node.drop(1).select{|n| n[:originator].empty? }
|
227
250
|
unless miss.empty?
|
228
|
-
errmsg = "`Originator` is missed for the
|
251
|
+
errmsg = "`Originator` is missed for the following nodes:\n"
|
229
252
|
errmsg << miss.map(&:id).join(', ')
|
230
253
|
raise Error, errmsg
|
231
254
|
end
|
232
255
|
```
|
233
256
|
|
257
|
+
Instead of adding extra scripts files somewhere in the project, you can write tasks in `<project>.thor` (see [Automating](#automating) section for details.)
|
258
|
+
|
234
259
|
#### Root Node
|
235
260
|
|
236
261
|
A hierarchy starts form root node and Clerq provides the root node with parameter `title` specified in `clerq.yml` file. The subject is a bit tricky actually and there are few extra considerations I try to explain below (and you can always see tests)
|
@@ -241,54 +266,106 @@ When you have a few root nodes in your repository, those become direct childs o
|
|
241
266
|
|
242
267
|
The following example does not provide root node and it causes adding root node from `clerq.yml`.
|
243
268
|
|
244
|
-
```
|
269
|
+
```markdown
|
245
270
|
# User requirements
|
246
271
|
# Functional requirements
|
247
272
|
```
|
248
273
|
|
249
|
-
But this one provides, and root node will be `Product SRS
|
274
|
+
But this one provides, and the root node will be `Product SRS`.
|
250
275
|
|
251
|
-
```
|
276
|
+
```markdown
|
252
277
|
# Product SRS
|
253
278
|
## User requirements
|
254
279
|
## Functional requirements
|
255
280
|
```
|
256
281
|
|
257
|
-
The QueryAssembly
|
282
|
+
The QueryAssembly follows the similar logic
|
258
283
|
|
259
284
|
* When query result is empty, the Clerq will provide result with QueryNullNode (node.title == `Query`, node[:query] == QUERY_STRING)
|
260
285
|
* When query result contains single node, it becomes a root query node.
|
261
286
|
* When query result contains more than one, those becomes a child of root query node.
|
262
287
|
|
288
|
+
### Automating
|
289
|
+
|
290
|
+
The Clerq creates `<project>.thor` where you can place your project-specific tasks. It is a standard [Thor](https://github.com/erikhuda/thor) that brings you all script automation power through CLI and to dive deeper just spend a few minutes reading [the poject wiki](https://github.com/erikhuda/thor/wiki).
|
291
|
+
|
292
|
+
Let's move the code from [Scripting](#scripting) section to the `<project>.thor` file:
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
require 'clerq'
|
296
|
+
include Clerq::Services
|
297
|
+
|
298
|
+
class MyDocument < Thor
|
299
|
+
namespace :mydoc
|
300
|
+
|
301
|
+
no_commands {
|
302
|
+
def stop_on_error!(errmsg)
|
303
|
+
raise Thor::Error, errmsg
|
304
|
+
end
|
305
|
+
}
|
306
|
+
|
307
|
+
desc 'check_originator', 'Check :originator'
|
308
|
+
def check_originator
|
309
|
+
node = LoadAssembly.()
|
310
|
+
node = QueryAssembly.(node: node, query: "node.title == 'User requirements'")
|
311
|
+
miss = node.drop(1).select{|n| n[:originator].empty? }
|
312
|
+
unless miss.empty?
|
313
|
+
errmsg = "`Originator` is missed for the following nodes:\n"
|
314
|
+
errmsg << miss.map(&:id).join(', ')
|
315
|
+
stop_on_error!(errmsg)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
```
|
320
|
+
|
321
|
+
And then you can run the task by
|
322
|
+
|
323
|
+
$ thor mydoc:check_originator
|
324
|
+
|
325
|
+
This example is just very basic and your automation scripts could be much more complex.
|
326
|
+
|
327
|
+
Another quick example is [clerq.thor] (https://github.com/nvoynov/clerq/blob/master/clerq.thor) file that was created just to overcome handling curly bracket `{{}}` in Jekyll and now I run `thor clerqsrc:docs` every time after changing this file.
|
328
|
+
|
263
329
|
### Templating
|
264
330
|
|
265
331
|
The Clerq provides the ability to precise adjusting the output for `clerq build` command by erb-templates and gives you two basic templates from the box.
|
266
332
|
|
267
333
|
* [default.md.erb](https://github.com/nvoynov/clerq/blob/master/lib/assets/tt/default.md.erb) that just combines all nodes to one markdown document;
|
268
334
|
* [pandoc.md.erb](https://github.com/nvoynov/clerq/blob/master/lib/assets/tt/pandoc.md.erb) is more advanced, it produces [Pandoc's Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown) and provides three followed macros for node body:
|
269
|
-
* `{{@@list}}` - replaces the macro with the list of child
|
270
|
-
* `{{@@tree}}` - replaces the macro with the tree of child
|
335
|
+
* `{{@@list}}` - replaces the macro with the list of child nodes;
|
336
|
+
* `{{@@tree}}` - replaces the macro with the tree of child nodes;
|
271
337
|
* `{{@@skip}}` - skip all content inside the brackets.
|
272
338
|
|
339
|
+
### Publishing
|
340
|
+
|
341
|
+
In addition to the `clerq build` command in [lib/clerq_doc.thor](https://github.com/nvoynov/clerq/blob/master/lib/assets/lib/clerq_doc.rb) I provided the example of basic documents management tasks (it will be placed in new project `lib` folder). You can find there two example of commands that you can start your own publishing automation.
|
342
|
+
|
343
|
+
* `thor clerq:doc:publish` will create `<project>.docx` and `<project>.html`;
|
344
|
+
* `thor clerq:doc:grab` will import provided document into the current project repository.
|
345
|
+
|
273
346
|
## Known issues
|
274
347
|
|
275
348
|
### Thor version
|
276
349
|
|
277
350
|
The one issue I certain in is when you are using different version of thor, your custom scripts won't work.
|
278
351
|
|
352
|
+
### Test suite
|
353
|
+
|
354
|
+
Because `default.md.erb` and `pandoc.md.erb` have inside the same class `MarkupNode`, sometimes one of `default_spec.rb` or `pandoc_spec.rb` fails.
|
355
|
+
|
279
356
|
## Some considerations
|
280
357
|
|
281
358
|
### Some obvious things
|
282
359
|
|
283
360
|
Use modern text editor that provides projects tree. like Atom, Sublime, etc.
|
284
361
|
|
285
|
-
Hold your projects in Git
|
362
|
+
Hold your projects in Git.
|
286
363
|
|
287
364
|
Use pandoc for generating output in different formats
|
288
365
|
|
289
366
|
### MarkupNode
|
290
367
|
|
291
|
-
Don't like the current dirty solution with templates and incorporated MarkupNode that does all that stuff with macro. It is the first attempt to provide template that can
|
368
|
+
Don't like the current dirty solution with templates and incorporated MarkupNode that does all that stuff with macro. It is the first attempt to provide template that can skip comments.
|
292
369
|
|
293
370
|
### Several artifacts
|
294
371
|
|
data/clerq.thor
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
class ClerqSrc < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
namespace 'clerq:src'.to_sym
|
8
|
+
|
9
|
+
desc 'docs', 'Prepare docs'
|
10
|
+
def docs
|
11
|
+
# to wrap all '{{}}' of README.md with `{% raw %} <> {% endraw %}
|
12
|
+
text = File.read(SOURCE)
|
13
|
+
SUBS.each{|patt, subs|
|
14
|
+
text.scan(patt).uniq.each{|e| text.gsub!(e, subs.call(e))}
|
15
|
+
}
|
16
|
+
File.write(TARGET, text)
|
17
|
+
say "'#{TARGET}' created!"
|
18
|
+
end
|
19
|
+
|
20
|
+
SOURCE = 'README.md'
|
21
|
+
TARGET = 'docs/README.md'
|
22
|
+
REX1, REX2 = /`{{[\s\S]*?}}`/, /```markdown[\s\S]*?```/
|
23
|
+
SUBS = {}.tap do |curly|
|
24
|
+
curly[REX1] = lambda {|e| "{% raw %}#{e}{% endraw %}" }
|
25
|
+
curly[REX2] = lambda {|e| "{% raw %}\n#{e}\n{% endraw %}" }
|
26
|
+
end.freeze
|
27
|
+
|
28
|
+
end
|
data/docs/README.md
ADDED
@@ -0,0 +1,408 @@
|
|
1
|
+
# Clerq
|
2
|
+
|
3
|
+
__What is Clerq?__
|
4
|
+
|
5
|
+
The Clerq is a toolbox for manipulating the hierarchy of text data placed in separate markdown files. It implements three basic ideas:
|
6
|
+
|
7
|
+
1. Text data repository in file system, based on markdown files with few extra conventions.
|
8
|
+
2. Ruby gem that provides access to the text hierarchy from the repository.
|
9
|
+
3. Basic CLI to manage the repository and compile the text data to documents based on erb-templates.
|
10
|
+
|
11
|
+
__What for?__
|
12
|
+
|
13
|
+
The Clerq is suitable for writing thick structured texts. The initial purpose for the system was the "requirements management in file system" and it supposed to help in writing stuff like Vision, RFP, URD, SRS, SAD, and deriving various requirements based artifacts. But now it seems much wider.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Install it yourself as:
|
18
|
+
|
19
|
+
$ gem install clerq
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The Clerq is entirely based on one single domain entity `Node` that represents a node of tree hierarchy and provides `id`, `title`, `body`, and `metadata` attributes. It supposes the following simple workflow:
|
24
|
+
|
25
|
+
* you create files with text content,
|
26
|
+
* and manipulate the data by scripts.
|
27
|
+
|
28
|
+
### Project
|
29
|
+
|
30
|
+
The Clerq project lives in the following folders structure that will be created by `clerq new <project>`:
|
31
|
+
|
32
|
+
* `bin/` - for output documents;
|
33
|
+
* `bin/assets` - for assets;
|
34
|
+
* `knb/` - knowledge base;
|
35
|
+
* `lib/` - place for extra Ruby code;
|
36
|
+
* `src/` - source data repository;
|
37
|
+
* `tt/` - templates;
|
38
|
+
* `<project>.thor` - see [Scripting](#scripting);
|
39
|
+
* `clerq.yml` - project settings;
|
40
|
+
* `README.md`.
|
41
|
+
|
42
|
+
### Writing
|
43
|
+
|
44
|
+
The Clerq reads nodes from a set of separate files and assembles it to a single hierarchy. There are a few conventions for a separate file that will become a part of hierarchy.
|
45
|
+
|
46
|
+
#### Files
|
47
|
+
|
48
|
+
The first convention is the scheme how a markdown content becomes the `Node` entity.
|
49
|
+
|
50
|
+
{% raw %}
|
51
|
+
```markdown
|
52
|
+
# [p2] Part two
|
53
|
+
{{parent: top}}
|
54
|
+
|
55
|
+
Body
|
56
|
+
```
|
57
|
+
{% endraw %}
|
58
|
+
|
59
|
+
Where
|
60
|
+
|
61
|
+
* `#` familiar markdown header that indicates a new `node`;
|
62
|
+
* `[p1]` is an optional identifier that becomes `node.id`;
|
63
|
+
* `Part two` is an optional `node.title`;
|
64
|
+
* {% raw %}`{{parent: top}}`{% endraw %} in an optional metadata section that becomes `node.meta`;
|
65
|
+
* and finally `Body` is an optional `node.body`.
|
66
|
+
|
67
|
+
{% raw %}
|
68
|
+
```markdown
|
69
|
+
# Part two
|
70
|
+
{{id: p1, parent: top}}
|
71
|
+
|
72
|
+
Body
|
73
|
+
```
|
74
|
+
{% endraw %}
|
75
|
+
|
76
|
+
Every new header (`#`) at any level indicates a new node. When a file contains headers of different levels, the nodes will be created in a natural hierarchy based on header levels. So as the result of reading the content below, the Clerq will create the natural hierarchy with root node `Top` that holds two child nodes `First` and `Second`.
|
77
|
+
|
78
|
+
{% raw %}
|
79
|
+
```markdown
|
80
|
+
# Top
|
81
|
+
## First
|
82
|
+
## Second
|
83
|
+
```
|
84
|
+
{% endraw %}
|
85
|
+
|
86
|
+
One more extra thing is links. You can place links to other nodes in the body section of the file content by using `[[<id>]]` macro. It can be handled in templates.
|
87
|
+
|
88
|
+
#### IDs
|
89
|
+
|
90
|
+
To be able to build a hierarchy or to refer to other nodes, one needs each node to have its unique id. And you can pass it straight into markdown header `# [node id] node title` or provide it through {% raw %}`{{id: }}`{% endraw %}.
|
91
|
+
|
92
|
+
ID can start with one dot, like `[.suffix]`, and clerq will build the node id as `node.parent_id + node.id`.
|
93
|
+
|
94
|
+
When and ID is not provided, the Clerq will generate it automatically. Let's see the example of node:
|
95
|
+
|
96
|
+
{% raw %}
|
97
|
+
```markdown
|
98
|
+
# User requirements
|
99
|
+
## Requirement 1
|
100
|
+
## Requirement 2
|
101
|
+
# Function requirements
|
102
|
+
## [cm] Components
|
103
|
+
### [.fm] File manager
|
104
|
+
### Logger
|
105
|
+
```
|
106
|
+
{% endraw %}
|
107
|
+
|
108
|
+
According to rules mentioned above the example will be translated as followed:
|
109
|
+
|
110
|
+
{% raw %}
|
111
|
+
```markdown
|
112
|
+
# [01] User requirements
|
113
|
+
## [01.01] Requirement 1
|
114
|
+
## [01.02] Requirement 2
|
115
|
+
# [02] Function requirements
|
116
|
+
## [cm] Components
|
117
|
+
### [cm.fm] File manager
|
118
|
+
### [cm.01] Logger
|
119
|
+
```
|
120
|
+
{% endraw %}
|
121
|
+
|
122
|
+
#### Meta
|
123
|
+
|
124
|
+
The excerpt, the text in brackets {% raw %}`{{ }}`{% endraw %} that follows by the header, contains node attributes. And the second convention mentioned in [Writing](#writing) section is the followed magic metadata attributes that specify parameters of a hierarchy:
|
125
|
+
|
126
|
+
1. `id: <id>` specifies id through metadata; when in provided together with `# [<id>]`, the last has priority;
|
127
|
+
2. `parent: <id>` indicates that the node belongs to a node with specified `id`;
|
128
|
+
3. `order_index: <id1> <id2>` indicates that child nodes must be lined up in specified order.
|
129
|
+
|
130
|
+
You can place in metadata any simple string that suitable for providing additional information like status, originator, author, priority, etc. E.g.
|
131
|
+
|
132
|
+
{% raw %}
|
133
|
+
```markdown
|
134
|
+
# [r.1]
|
135
|
+
{{parent: r, status: draft}}
|
136
|
+
|
137
|
+
# [r.2]
|
138
|
+
{{parent: r
|
139
|
+
}}
|
140
|
+
|
141
|
+
# [r.3]
|
142
|
+
{{
|
143
|
+
parent: r}}
|
144
|
+
```
|
145
|
+
{% endraw %}
|
146
|
+
|
147
|
+
#### Assets
|
148
|
+
|
149
|
+
When you want to provide some assets or links to something outside the repository you can provide the lint to the assets. Put the asset in the `bin/assets` folder and specify the link.
|
150
|
+
|
151
|
+
{% raw %}
|
152
|
+
```markdown
|
153
|
+
# [ent] Entities
|
154
|
+
|
155
|
+
The following picture shows something
|
156
|
+
|
157
|
+
![Image](assets/er.png)
|
158
|
+
```
|
159
|
+
{% endraw %}
|
160
|
+
|
161
|
+
### CLI
|
162
|
+
|
163
|
+
Clerq provides CLI that is based on Thor, so all standard thor features are supported. To print all Clerq commands type `$ clerq help` in your console. To see the list of all the project-specific commands type `thor help <project>`.
|
164
|
+
|
165
|
+
#### Create new project
|
166
|
+
|
167
|
+
To create a new project run `new` command:
|
168
|
+
|
169
|
+
$ clerq new <project_name>
|
170
|
+
|
171
|
+
#### Create new file
|
172
|
+
|
173
|
+
The simplest way of adding new items to the project is to add a new file to the `src` directory. Of course, Clerq also provides the command `node` that can create template-based files:
|
174
|
+
|
175
|
+
$ clerq node ID [TITLE] [-t TEMPLATE]
|
176
|
+
|
177
|
+
__Assets__
|
178
|
+
|
179
|
+
If you are using images or other assets, you should place it to `bin/assets` directory and write markdown link like `![img](assets/img.png)`
|
180
|
+
|
181
|
+
__Templates__
|
182
|
+
|
183
|
+
You also can prepare your own templates it `tt` folder and provide template through `-t/--template` option. The content of the template will be placed on the created file.
|
184
|
+
|
185
|
+
#### Check repository
|
186
|
+
|
187
|
+
Because of lots of handwriting there can be some specific errors in repository. The most obvious are:
|
188
|
+
|
189
|
+
* non-unique identifiers;
|
190
|
+
* links to and id that does not exist:
|
191
|
+
* for `parent` attribute;
|
192
|
+
* in `order_index`;
|
193
|
+
* in `body`.
|
194
|
+
|
195
|
+
The system provides command `clerq check` that will check the repository for these kinds of errors.
|
196
|
+
|
197
|
+
$ clerq check
|
198
|
+
|
199
|
+
#### Build project
|
200
|
+
|
201
|
+
Clerq provides the ability to combine all the text data from the project repository and create the final document. To create such document you can use `clerq build` command:
|
202
|
+
|
203
|
+
$ clerq build
|
204
|
+
|
205
|
+
It will create final document with default file name, title, and by default erb template. These default values are defined in `clerq.yml` and you should change it according to your aim.
|
206
|
+
|
207
|
+
Default values of final document parameters are:
|
208
|
+
|
209
|
+
* `document: <project_name>`;
|
210
|
+
* `template: default.md.erb`;
|
211
|
+
* `title: <project_name>`.
|
212
|
+
|
213
|
+
You also can specify these settings through `clerq build` options:
|
214
|
+
|
215
|
+
* `-t/--template TEMPLATE` provides the ability to specify template;
|
216
|
+
* `-o/--output FILE_NAME` provides the ability to specify output file name.
|
217
|
+
|
218
|
+
__Queries__
|
219
|
+
|
220
|
+
Clerq provides the ability to query data that match query criteria. To query data you should use `-q/--query QUERY_STRING` option where `QUERY_STRING` is ruby code that will test if each node matches the `QUERY_STRING`. For example, `node.tile == 'Functional requirements'` or `node.id == 'us'`.
|
221
|
+
|
222
|
+
#### Print TOC
|
223
|
+
|
224
|
+
Sometimes it helpful to check repository structure by `clerq toc` command. The command also supports `-q/--query QUERY_STRING` option.
|
225
|
+
|
226
|
+
### Scripting
|
227
|
+
|
228
|
+
The section assumes that you are familiar with Ruby or some other programming language.
|
229
|
+
|
230
|
+
Using the basic commands described in [CLI](#cli) section gives you just the ability to create final documents or other output. But this is just the tip of the iceberg, just the beginning, and you can do much more than that with Clerq.
|
231
|
+
|
232
|
+
A usual scenario will consist of two simple steps:
|
233
|
+
|
234
|
+
1. Get data hierarchy from the repository.
|
235
|
+
2. Do some processing of the hierarchy.
|
236
|
+
|
237
|
+
#### Node class
|
238
|
+
|
239
|
+
The [Writing](#writing) section provides the basic knowledge to understand Clerq, and now it is the right time to see the [Node class](https://github.com/nvoynov/clerq/blob/master/lib/clerq/entities/node.rb). It implements the Composite pattern.
|
240
|
+
|
241
|
+
#### Services
|
242
|
+
|
243
|
+
Clerq provides the following main service objects:
|
244
|
+
|
245
|
+
* `LoadAssembly` loads whole repository to Node class;
|
246
|
+
* `CheckAssembly` checks the assembly for errors (ids and links);
|
247
|
+
* `QueryNode` provides ability to query nodes from assembly;
|
248
|
+
* `QueryTemplate` return template by the template name;
|
249
|
+
* `CreateNode` crates new node in the repository;
|
250
|
+
* `RenderNode` return text rendered by ERB.
|
251
|
+
|
252
|
+
The first part of each repository related task is to get repository assembly. It can be performed through `NodeRepository#assemble` or `LoadAssembly.call()`. Each of these methods returns Node that provides [Enumerable](https://ruby-doc.org/core-2.6.5/Enumerable.html) interface.
|
253
|
+
|
254
|
+
Let's see an example. Assume that you are developing a "User requirements document" and the project policy requires that each user requirement must have the parameter called `originator`. You can write the policy as followed:
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
require 'clerq'
|
258
|
+
include Clerq::Services
|
259
|
+
|
260
|
+
# supposed you have something like user requirements document
|
261
|
+
node = LoadAssembly.()
|
262
|
+
node = QueryNode.(node: node, query: "node.title == 'User requirements'")
|
263
|
+
miss = node.drop(1).select{|n| n[:originator].empty? }
|
264
|
+
unless miss.empty?
|
265
|
+
errmsg = "`Originator` is missed for the following nodes:\n"
|
266
|
+
errmsg << miss.map(&:id).join(', ')
|
267
|
+
raise Error, errmsg
|
268
|
+
end
|
269
|
+
```
|
270
|
+
|
271
|
+
Instead of adding extra scripts files somewhere in the project, you can write tasks in `<project>.thor` (see [Automating](#automating) section for details.)
|
272
|
+
|
273
|
+
#### Root Node
|
274
|
+
|
275
|
+
A hierarchy starts form root node and Clerq provides the root node with parameter `title` specified in `clerq.yml` file. The subject is a bit tricky actually and there are few extra considerations I try to explain below (and you can always see tests)
|
276
|
+
|
277
|
+
When your repository stills empty, the Clerq will provide you with the root node. From one point it resembles the NullObject.
|
278
|
+
|
279
|
+
When you have a few root nodes in your repository, those become direct childs of the root node. But when your repository contains single root node, the Clerq will return the single node as root node.
|
280
|
+
|
281
|
+
The following example does not provide root node and it causes adding root node from `clerq.yml`.
|
282
|
+
|
283
|
+
{% raw %}
|
284
|
+
```markdown
|
285
|
+
# User requirements
|
286
|
+
# Functional requirements
|
287
|
+
```
|
288
|
+
{% endraw %}
|
289
|
+
|
290
|
+
But this one provides, and the root node will be `Product SRS`.
|
291
|
+
|
292
|
+
{% raw %}
|
293
|
+
```markdown
|
294
|
+
# Product SRS
|
295
|
+
## User requirements
|
296
|
+
## Functional requirements
|
297
|
+
```
|
298
|
+
{% endraw %}
|
299
|
+
|
300
|
+
The QueryAssembly follows the similar logic
|
301
|
+
|
302
|
+
* When query result is empty, the Clerq will provide result with QueryNullNode (node.title == `Query`, node[:query] == QUERY_STRING)
|
303
|
+
* When query result contains single node, it becomes a root query node.
|
304
|
+
* When query result contains more than one, those becomes a child of root query node.
|
305
|
+
|
306
|
+
### Automating
|
307
|
+
|
308
|
+
The Clerq creates `<project>.thor` where you can place your project-specific tasks. It is a standard [Thor](https://github.com/erikhuda/thor) that brings you all script automation power through CLI and to dive deeper just spend a few minutes reading [the poject wiki](https://github.com/erikhuda/thor/wiki).
|
309
|
+
|
310
|
+
Let's move the code from [Scripting](#scripting) section to the `<project>.thor` file:
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
require 'clerq'
|
314
|
+
include Clerq::Services
|
315
|
+
|
316
|
+
class MyDocument < Thor
|
317
|
+
namespace :mydoc
|
318
|
+
|
319
|
+
no_commands {
|
320
|
+
def stop_on_error!(errmsg)
|
321
|
+
raise Thor::Error, errmsg
|
322
|
+
end
|
323
|
+
}
|
324
|
+
|
325
|
+
desc 'check_originator', 'Check :originator'
|
326
|
+
def check_originator
|
327
|
+
node = LoadAssembly.()
|
328
|
+
node = QueryAssembly.(node: node, query: "node.title == 'User requirements'")
|
329
|
+
miss = node.drop(1).select{|n| n[:originator].empty? }
|
330
|
+
unless miss.empty?
|
331
|
+
errmsg = "`Originator` is missed for the following nodes:\n"
|
332
|
+
errmsg << miss.map(&:id).join(', ')
|
333
|
+
stop_on_error!(errmsg)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
```
|
338
|
+
|
339
|
+
And then you can run the task by
|
340
|
+
|
341
|
+
$ thor mydoc:check_originator
|
342
|
+
|
343
|
+
This example is just very basic and your automation scripts could be much more complex.
|
344
|
+
|
345
|
+
Another quick example is [clerq.thor] (https://github.com/nvoynov/clerq/blob/master/clerq.thor) file that was created just to overcome handling curly bracket {% raw %}`{{}}`{% endraw %} in Jekyll and now I run `thor clerqsrc:docs` every time after changing this file.
|
346
|
+
|
347
|
+
### Templating
|
348
|
+
|
349
|
+
The Clerq provides the ability to precise adjusting the output for `clerq build` command by erb-templates and gives you two basic templates from the box.
|
350
|
+
|
351
|
+
* [default.md.erb](https://github.com/nvoynov/clerq/blob/master/lib/assets/tt/default.md.erb) that just combines all nodes to one markdown document;
|
352
|
+
* [pandoc.md.erb](https://github.com/nvoynov/clerq/blob/master/lib/assets/tt/pandoc.md.erb) is more advanced, it produces [Pandoc's Markdown](https://pandoc.org/MANUAL.html#pandocs-markdown) and provides three followed macros for node body:
|
353
|
+
* {% raw %}`{{@@list}}`{% endraw %} - replaces the macro with the list of child nodes;
|
354
|
+
* {% raw %}`{{@@tree}}`{% endraw %} - replaces the macro with the tree of child nodes;
|
355
|
+
* {% raw %}`{{@@skip}}`{% endraw %} - skip all content inside the brackets.
|
356
|
+
|
357
|
+
### Publishing
|
358
|
+
|
359
|
+
In addition to the `clerq build` command in [lib/clerq_doc.thor](https://github.com/nvoynov/clerq/blob/master/lib/assets/lib/clerq_doc.rb) I provided the example of basic documents management tasks (it will be placed in new project `lib` folder). You can find there two example of commands that you can start your own publishing automation.
|
360
|
+
|
361
|
+
* `thor clerq:doc:publish` will create `<project>.docx` and `<project>.html`;
|
362
|
+
* `thor clerq:doc:grab` will import provided document into the current project repository.
|
363
|
+
|
364
|
+
## Known issues
|
365
|
+
|
366
|
+
### Thor version
|
367
|
+
|
368
|
+
The one issue I certain in is when you are using different version of thor, your custom scripts won't work.
|
369
|
+
|
370
|
+
### Test suite
|
371
|
+
|
372
|
+
Because `default.md.erb` and `pandoc.md.erb` have inside the same class `MarkupNode`, sometimes one of `default_spec.rb` or `pandoc_spec.rb` fails.
|
373
|
+
|
374
|
+
## Some considerations
|
375
|
+
|
376
|
+
### Some obvious things
|
377
|
+
|
378
|
+
Use modern text editor that provides projects tree. like Atom, Sublime, etc.
|
379
|
+
|
380
|
+
Hold your projects in Git.
|
381
|
+
|
382
|
+
Use pandoc for generating output in different formats
|
383
|
+
|
384
|
+
### MarkupNode
|
385
|
+
|
386
|
+
Don't like the current dirty solution with templates and incorporated MarkupNode that does all that stuff with macro. It is the first attempt to provide template that can skip comments.
|
387
|
+
|
388
|
+
### Several artifacts
|
389
|
+
|
390
|
+
Because Clerq has `-q/--query QUERY_STRING` option you can be interested in developing several different artifacts in one project.
|
391
|
+
|
392
|
+
I was considering such an example to develop all software project documents in one clerq project but decided that it is more properly to develop one single artifact per project because usually, each artifact has its own develop-review-release cycle.
|
393
|
+
|
394
|
+
Also, I was considering to add some kind of a "top" project that is just a wrapper for individual projects inside (each of them is the clerq project, and the top project just provides a specific set of commands.) I was speculating about some kind of shared content and tracing nodes between different artifacts. But for the moment I have no full-fledged vision.
|
395
|
+
|
396
|
+
## Development
|
397
|
+
|
398
|
+
The project is bundled, so after checking out the repo, run `bundle` to install dependencies. Then, run `bundle exec rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
399
|
+
|
400
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
401
|
+
|
402
|
+
## Contributing
|
403
|
+
|
404
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nvoynov/clerq.
|
405
|
+
|
406
|
+
## License
|
407
|
+
|
408
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|