clerq 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/CHANGELOG.md +14 -2
  4. data/Gemfile.lock +24 -0
  5. data/README.md +107 -30
  6. data/clerq.thor +28 -0
  7. data/docs/README.md +408 -0
  8. data/lib/assets/knb/business-case.md +135 -0
  9. data/lib/assets/knb/requirement-life-cycle.md +47 -0
  10. data/lib/assets/knb/vision-document.md +191 -0
  11. data/lib/assets/lib/clerq_doc.thor +119 -0
  12. data/lib/assets/lib/colonize_repo.rb +82 -0
  13. data/lib/assets/lib/spec/colonize_repo_spec.rb +85 -0
  14. data/lib/assets/new/content.md.tt +1 -0
  15. data/lib/assets/tt/default.md.erb +1 -1
  16. data/lib/assets/tt/pandoc.md.erb +6 -6
  17. data/lib/clerq.rb +7 -2
  18. data/lib/clerq/cli.rb +33 -47
  19. data/lib/clerq/entities/node.rb +11 -5
  20. data/lib/clerq/repositories.rb +0 -1
  21. data/lib/clerq/repositories/file_repository.rb +1 -0
  22. data/lib/clerq/repositories/node_repository.rb +7 -6
  23. data/lib/clerq/services.rb +8 -0
  24. data/lib/clerq/services/check_assembly.rb +108 -0
  25. data/lib/clerq/{interactors → services}/create_node.rb +4 -5
  26. data/lib/clerq/services/load_assembly.rb +54 -0
  27. data/lib/clerq/{interactors/query_assembly.rb → services/query_node.rb} +15 -12
  28. data/lib/clerq/{interactors → services}/query_template.rb +3 -8
  29. data/lib/clerq/services/read_node.rb +98 -0
  30. data/lib/clerq/services/render_erb.rb +29 -0
  31. data/lib/clerq/{interactors/render_assembly.rb → services/render_node.rb} +8 -12
  32. data/lib/clerq/services/service.rb +19 -0
  33. data/lib/clerq/version.rb +1 -1
  34. metadata +21 -12
  35. data/TODO.md +0 -7
  36. data/lib/clerq/interactors.rb +0 -5
  37. data/lib/clerq/interactors/check_assembly.rb +0 -77
  38. data/lib/clerq/interactors/interactor.rb +0 -26
  39. data/lib/clerq/render_erb.rb +0 -33
  40. data/lib/clerq/repositories/node_reader.rb +0 -107
@@ -0,0 +1,8 @@
1
+ require_relative 'services/service'
2
+ require_relative 'services/check_assembly'
3
+ require_relative 'services/read_node'
4
+ require_relative 'services/load_assembly'
5
+ require_relative 'services/query_node'
6
+ require_relative 'services/query_template'
7
+ require_relative 'services/create_node'
8
+ require_relative 'services/render_node'
@@ -0,0 +1,108 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'service'
4
+
5
+ module Clerq
6
+ module Services
7
+
8
+ # Find errors in hierarchy and prints in console
9
+ class CheckAssembly < Service
10
+
11
+ private_class_method :new
12
+
13
+ def call
14
+ print_nonuniq_id
15
+ print_lost_roots
16
+ print_lost_index
17
+ print_lost_links
18
+ end
19
+
20
+ protected
21
+
22
+ def nonuniq_id
23
+ @node.inject({}) do |memo, node|
24
+ memo[node.id] ||= []
25
+ memo[node.id] << node
26
+ memo
27
+ end.select{|_, v| v.size > 1}
28
+ end
29
+
30
+ def print_nonuniq_id
31
+ errors = nonuniq_id
32
+ print "Checking for duplicates in node ids... "
33
+ puts errors.empty? ? 'OK' : "#{errors.size} found"
34
+ errors.each do |id, nodes|
35
+ occs = nodes.map{|n| n[:filename]}
36
+ .group_by{|i| i}
37
+ .map{|k,v| [v.size, k]}
38
+ .sort{|a, b| b.first <=> a.first}
39
+ .map{|i| n, src = i; "#{how_many_times(n)} in '#{src}'"}
40
+ occurrences = occs.join(occs.size == 2 ? ' and ' : ', ')
41
+ puts "- [#{id}] occured #{occurrences}"
42
+ end
43
+ end
44
+
45
+ def print_lost_roots
46
+ lost = @node.select{|n| n[:parent] && n.parent.id != n[:parent]}
47
+ print "Checking for lost roots in node parents... "
48
+ puts lost.empty? ? 'OK' : "#{lost.size} found"
49
+ lost.each do |n|
50
+ puts "- {{parent: #{n[:parent]}}} of '#{n.id}' in '#{n[:filename]}'"
51
+ end
52
+ end
53
+
54
+ def print_lost_index
55
+ errors = @node
56
+ .reject{|n| n.order_index.empty?}
57
+ .inject({}) do |memo, n|
58
+ lost = n.order_index.reject{|o| n.item(o)}
59
+ memo[n] = lost unless lost.empty?
60
+ memo
61
+ end
62
+
63
+ print "Checking for lost items in order_index... "
64
+ puts errors.empty? ? 'OK' : "#{errors.size} found"
65
+ errors.each do |n, lost|
66
+ puts "- {{order_index: #{lost.join(' ')}}} not found of node '#{n.id}' in '#{n[:filename]}'"
67
+ end
68
+ end
69
+
70
+ def lost_links
71
+ index = @node.map(&:id).drop(1).uniq
72
+ @node.inject({}) do |memo, node|
73
+ node.links
74
+ .reject{ |lnk| index.include?(lnk) || @node.node(lnk)}
75
+ .each do |lnk|
76
+ memo[lnk] ||= []
77
+ memo[lnk] << node
78
+ end
79
+ memo
80
+ end
81
+ end
82
+
83
+ def print_lost_links
84
+ errors = lost_links
85
+ print "Checking for lost links in nodes body... "
86
+ puts errors.empty? ? 'OK' : "#{errors.size} found"
87
+ errors.each do |link, arry|
88
+ where = arry.map{|n| "[#{n.id}] of '#{n[:filename]}'"}.join(', ')
89
+ puts "- [[#{link}]] in #{where}"
90
+ end
91
+ end
92
+
93
+ # humanize number of how many times the error occurred in one file
94
+ def how_many_times(n)
95
+ case n
96
+ when 1 then 'once'
97
+ when 2 then 'twice'
98
+ else "#{n} times"
99
+ end
100
+ end
101
+
102
+ def initialize(node)
103
+ @node = node
104
+ end
105
+ end
106
+
107
+ end
108
+ end
@@ -1,24 +1,23 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require_relative "interactor"
3
+ require_relative "service"
4
4
  require_relative "query_template"
5
+ require_relative "../entities/node"
5
6
 
6
7
  module Clerq
7
- module Interactors
8
+ module Services
8
9
 
9
10
  # Creates new node in repository according to provided parameters
10
11
  # or raises CreateNode::Failure when
11
12
  # * template not found
12
13
  # * or repository contains a node with the same id
13
- class CreateNode < Interactor
14
+ class CreateNode < Service
14
15
 
15
16
  def call
16
17
  @body = QueryTemplate.(@template) if @body.empty? && !@template.empty?
17
18
  @node = Clerq::Entities::Node.new(
18
19
  id: @id, title: @title, body: @body, meta: @meta)
19
20
  Clerq.node_repository.save(@node)
20
- rescue StandardError => e
21
- raise Failure, e.message
22
21
  end
23
22
 
24
23
  protected
@@ -0,0 +1,54 @@
1
+ # encoding: UTF-8
2
+ require_relative 'service'
3
+
4
+ module Clerq
5
+ module Services
6
+
7
+ # The service returns assembly and prints progress in console
8
+ class LoadAssembly < Service
9
+ def call
10
+ memo = {}
11
+ on_parse_callback = lambda do |src|
12
+ puts "Reading '#{src}'..."
13
+ memo[src] = 0
14
+ end
15
+ on_error_callback = lambda do |err|
16
+ puts "\t#{err}"
17
+ memo[memo.keys.last] += 1
18
+ end
19
+
20
+ puts "Loading repository..."
21
+ assemble = Clerq.assemble(
22
+ on_parse: on_parse_callback,
23
+ on_error: on_error_callback)
24
+
25
+ if memo.empty?
26
+ puts "This repository is empty"
27
+ else
28
+ errors_count = memo.values.inject(0, &:+)
29
+ message = [].tap do |m|
30
+ m << "#{nos(memo.size, 'file')} loaded"
31
+ m << "#{nos(errors_count, 'error')} detected"
32
+ end.join(', ')
33
+ puts message
34
+ end
35
+
36
+ assemble
37
+ end
38
+
39
+ protected
40
+
41
+ # TODO 0 zero, no
42
+ def nos(number, subject)
43
+ case number
44
+ when 0 then "no #{subject}s"
45
+ when 1 then "one #{subject}"
46
+ when 2 then "two #{subject}s"
47
+ else "#{number} #{subject}s"
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -1,16 +1,16 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require_relative "interactor"
3
+ require_relative "service"
4
4
 
5
5
  module Clerq
6
- module Interactors
6
+ module Services
7
7
 
8
8
  # Provides repository nodes assembly
9
9
  # @param [Sring] optional query string
10
10
  # @return [Node] assembly of repository nodes
11
11
  #
12
12
  # Usage
13
- # QueryAssembly.()
13
+ # QueryAssembly.(asse:, query:)
14
14
  # QueryAssembly.(query_string)
15
15
  #
16
16
  # Rules for root node:
@@ -20,14 +20,12 @@ module Clerq
20
20
  # when query returns mare than one nodes, then
21
21
  # * it creates empty root node with meta[:query]
22
22
  # * and places found nodes to the root
23
- class QueryAssembly < Interactor
23
+ class QueryNode < Service
24
24
 
25
25
  def call
26
26
  proc = prepare_query unless @query.empty?
27
- join = Clerq.node_repository.assemble
28
- return join if @query.empty?
29
27
 
30
- arry = join.select{|node| proc.call(node) }
28
+ arry = @assembly.select{|node| proc.call(node) }
31
29
  return arry.first.orphan! if arry.size == 1
32
30
 
33
31
  node = Node.new(title: Clerq.title, meta: {query: @query})
@@ -37,12 +35,17 @@ module Clerq
37
35
 
38
36
  protected
39
37
 
40
- def initialize(query = '')
38
+ def initialize(assembly: , query: )
39
+ unless assembly.is_a? Node
40
+ msg = "Invailid argument 'assembly'"
41
+ raise ArgumentError, msg
42
+ end
41
43
  unless query.is_a? String
42
44
  msg = "Invailid argument 'query'"
43
45
  raise ArgumentError, msg
44
46
  end
45
47
  @query = query
48
+ @assembly = assembly
46
49
  end
47
50
 
48
51
  def prepare_query
@@ -50,13 +53,13 @@ module Clerq
50
53
  Node.new.select{|node| proc.call(node)}
51
54
  proc
52
55
  rescue Exception => e
53
- msg = "Invalid query #{@query} (#{e.message})\n#{USAGE}"
54
- raise self.class::Failure, msg
56
+ msg = "Invalid query format #{@query} (#{e.message})\n#{USAGE}"
57
+ raise ArgumentError, msg
55
58
  end
56
59
 
57
60
  USAGE = <<~EOF
58
- QueryNode.call(query) evaluates query paramater
59
- by eval() as 'Proc.new { |node| eval(@query) }'
61
+ QueryAssembly.call(query: ) evaluates query paramater
62
+ by eval() as 'Proc.new { |node| eval(query) }'
60
63
  e.g. the followed queries are valid:
61
64
  node.id == 'uc'
62
65
  node.title == 'Introduction'
@@ -1,19 +1,14 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require_relative "interactor"
3
+ require_relative "service"
4
4
 
5
5
  module Clerq
6
- module Interactors
6
+ module Services
7
7
 
8
- class QueryTemplate < Interactor
9
-
10
- private_class_method :new
8
+ class QueryTemplate < Service
11
9
 
12
10
  def call
13
11
  Clerq.text_repository.text(@template)
14
- rescue StandardError
15
- err = "'#{@template}' template not found!"
16
- raise Failure, err
17
12
  end
18
13
 
19
14
  # @param template [String] required name of template
@@ -0,0 +1,98 @@
1
+ # encoding: UTF-8
2
+ require_relative 'service'
3
+ require_relative '../entities/node'
4
+ include Clerq::Entities
5
+
6
+ module Clerq
7
+ module Services
8
+
9
+ # The service reads nodes from file and returns array of nodes
10
+ # It returns array because the file can have a few root nodes
11
+ class ReadNode < Service
12
+
13
+ def call
14
+ text = File.foreach(@filename)
15
+ read_nodes(text) do |node_text|
16
+ level, node = parse_node(node_text)
17
+ next unless node
18
+ node[:filename] = @filename
19
+ insert_node(node, level)
20
+ end
21
+
22
+ ary = Array.new(@node.items)
23
+ ary.each(&:orphan!)
24
+ ary
25
+ end
26
+
27
+ # @param filename [String] input file for reading
28
+ # @param on_error [Proc] proc object {|error| ...}
29
+ def initialize(filename, on_error = nil)
30
+ @filename = filename
31
+ @on_error = on_error
32
+ @node = Node.new(id: filename)
33
+ end
34
+
35
+ protected
36
+
37
+ # @param [Enumerator<String>] text
38
+ # @return [Array<String>] where each item represents one node
39
+ def read_nodes(text, &block)
40
+ quote, node = false, []
41
+ text.each do |line|
42
+ if line.start_with?('#') && !quote && !node.empty?
43
+ block.call(node.join("\n"))
44
+ node = []
45
+ end
46
+ node << line
47
+ quote = !quote if line.start_with?('```')
48
+ end
49
+ block.call(node.join("\n")) unless node.empty?
50
+ end
51
+
52
+ def insert_node(node, level)
53
+ parent = @node
54
+ while parent.last_item && (parent.nesting_level + 1) < level
55
+ parent = parent.last_item
56
+ end
57
+ unless (parent.nesting_level + 1) == level
58
+ msg = "invalid header level: #{'#' * level} #{node.title}"
59
+ @on_error.call(msg) if @on_error
60
+ parent = @node
61
+ end
62
+ parent << node
63
+ end
64
+
65
+ def parse_node(text)
66
+ text += "\n" unless text.end_with?("\n")
67
+ parts = NODE_REGX.match(text)
68
+ lv = parts[1] || ''
69
+ id = parts[3] || ''
70
+ title = parts[4] || ''
71
+ body = parts[7] || ''
72
+ meta = {}
73
+ meta.merge!(parse_meta(parts[6])) if parts[6]
74
+ [lv.size, Node.new(id: id, title: title, body: body.strip, meta: meta)]
75
+ rescue StandardError
76
+ msg = "invalid node format: #{text}"
77
+ @on_error.call(msg) if @on_error
78
+ [nil, nil]
79
+ end
80
+
81
+ def parse_meta(text)
82
+ text.strip.split(/[;,\n]/).inject({}) do |h, i|
83
+ pair = /\s?(\w*):\s*(.*)/.match(i)
84
+ h.merge(pair[1].to_sym => pair[2])
85
+ end || {}
86
+ rescue StandardError
87
+ msg = "invalid meta format:\n{{#{text}}}"
88
+ @on_error.call(msg) if @on_error
89
+ {}
90
+ end
91
+
92
+ NODE_REGX =
93
+ /^(\#+)[ ]*(\[([^\[\]\s]*)\][ ]*)?([\s\S]*?)\n({{([\s\S]*?)}})?(.*)$/m
94
+
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'erb'
4
+ require_relative 'service'
5
+
6
+ module Clerq
7
+ module Services
8
+
9
+ # Render @object trough ERB temlate
10
+ #
11
+ # Usage
12
+ # erb = "id: <%= @object.id %>\ntitle: <%= @object.title %>\n"
13
+ # obj = Node.new(id: 'uc', title: 'Use Cases', meta: {skip_meta: true})
14
+ # txt = RenderErb.call(erb, obj) # or RenderErb.(erb, obj)
15
+ class RenderErb < Service
16
+
17
+ def call
18
+ tt = ERB.new(@erb, nil, "-")
19
+ tt.result(binding)
20
+ end
21
+
22
+ def initialize(erb: , object: )
23
+ @erb = erb
24
+ @object = object
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -1,26 +1,22 @@
1
1
  # encoding: UTF-8
2
- require_relative 'interactor'
3
- require_relative '../render_erb'
2
+ require_relative 'service'
3
+ require_relative 'query_template'
4
+ require_relative 'render_erb'
4
5
 
5
6
  module Clerq
6
- module Interactors
7
+ module Services
7
8
 
8
- class RenderAssembly < Interactor
9
+ class RenderNode < Service
9
10
 
10
11
  def call
11
12
  @erb = QueryTemplate.(@ett)
12
- asmb = QueryAssembly.(@qry)
13
- RenderErb.(erb: @erb, object: asmb)
14
- # TODO Clerq.binaries.save(@out, text)
15
- rescue StandardError => e
16
- raise Failure, e.message
13
+ RenderErb.(erb: @erb, object: @node)
17
14
  end
18
15
 
19
- def initialize(template:, query: '')
16
+ def initialize(node: , template:)
20
17
  check_string_argument!(template, 'template')
21
18
  check_string_empty!(template, 'template')
22
- check_string_argument!(query, 'query')
23
- @qry = query
19
+ @node = node
24
20
  @ett = template
25
21
  end
26
22