clerq 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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +24 -0
  6. data/README.md +269 -0
  7. data/Rakefile +10 -0
  8. data/TODO.md +3 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/clerq.gemspec +44 -0
  12. data/exe/clerq +8 -0
  13. data/lib/assets/new/README.md.tt +75 -0
  14. data/lib/assets/new/clerq.thor.tt +15 -0
  15. data/lib/assets/new/clerq.yml.tt +4 -0
  16. data/lib/assets/new/content.md.tt +41 -0
  17. data/lib/assets/promo/README.md +40 -0
  18. data/lib/assets/promo/bin/Clerq SRS.docx +0 -0
  19. data/lib/assets/promo/bin/Clerq SRS.md +579 -0
  20. data/lib/assets/promo/bin/assets/promo_dark.png +0 -0
  21. data/lib/assets/promo/bin/assets/promo_light.png +0 -0
  22. data/lib/assets/promo/clerq.yml +3 -0
  23. data/lib/assets/promo/promo.thor +58 -0
  24. data/lib/assets/promo/src/clerq.md +82 -0
  25. data/lib/assets/promo/src/fr/cmp.node.md +14 -0
  26. data/lib/assets/promo/src/fr/cmp.repo.md +10 -0
  27. data/lib/assets/promo/src/fr/cmp.tt.md +20 -0
  28. data/lib/assets/promo/src/fr/cmp.writer.md +19 -0
  29. data/lib/assets/promo/src/fr/ent.md +32 -0
  30. data/lib/assets/promo/src/ui/cli/cli.bld.md +32 -0
  31. data/lib/assets/promo/src/ui/cli/cli.chk.md +17 -0
  32. data/lib/assets/promo/src/ui/cli/cli.hlp.md +14 -0
  33. data/lib/assets/promo/src/ui/cli/cli.new.md +20 -0
  34. data/lib/assets/promo/src/ui/cli/cli.opt.md +11 -0
  35. data/lib/assets/promo/src/ui/cli/cli.ver.md +4 -0
  36. data/lib/assets/promo/src/ui/ui.cli.md +8 -0
  37. data/lib/assets/promo/src/us/us.reader.md +8 -0
  38. data/lib/assets/promo/src/us/us.writer.md +79 -0
  39. data/lib/assets/tt/default.md.erb +64 -0
  40. data/lib/assets/tt/gitlab.md.erb +93 -0
  41. data/lib/assets/tt/pandoc.md.erb +88 -0
  42. data/lib/assets/tt/raw.md.erb +23 -0
  43. data/lib/clerq.rb +41 -0
  44. data/lib/clerq/cli.rb +129 -0
  45. data/lib/clerq/entities.rb +2 -0
  46. data/lib/clerq/entities/node.rb +135 -0
  47. data/lib/clerq/entities/template.rb +19 -0
  48. data/lib/clerq/gateways.rb +3 -0
  49. data/lib/clerq/gateways/gateway.rb +17 -0
  50. data/lib/clerq/gateways/in_files.rb +36 -0
  51. data/lib/clerq/gateways/in_memory.rb +35 -0
  52. data/lib/clerq/interactors.rb +5 -0
  53. data/lib/clerq/interactors/check_nodes.rb +81 -0
  54. data/lib/clerq/interactors/compile_nodes.rb +31 -0
  55. data/lib/clerq/interactors/create_node.rb +40 -0
  56. data/lib/clerq/interactors/interactor.rb +28 -0
  57. data/lib/clerq/interactors/join_nodes.rb +59 -0
  58. data/lib/clerq/interactors/query_nodes.rb +62 -0
  59. data/lib/clerq/properties.rb +21 -0
  60. data/lib/clerq/repositories.rb +5 -0
  61. data/lib/clerq/repositories/in_memory.rb +45 -0
  62. data/lib/clerq/repositories/node_reader.rb +107 -0
  63. data/lib/clerq/repositories/node_repository.rb +56 -0
  64. data/lib/clerq/repositories/repository.rb +11 -0
  65. data/lib/clerq/repositories/template_repository.rb +53 -0
  66. data/lib/clerq/settings.rb +56 -0
  67. data/lib/clerq/templater.rb +32 -0
  68. data/lib/clerq/version.rb +3 -0
  69. 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 %>
@@ -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
@@ -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
@@ -0,0 +1,2 @@
1
+ require_relative 'entities/node'
2
+ require_relative 'entities/template'