clerq 0.1.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -3
  3. data/CHANGELOG.md +49 -0
  4. data/Gemfile.lock +9 -9
  5. data/README.md +260 -133
  6. data/Rakefile +1 -0
  7. data/clerq.gemspec +6 -6
  8. data/clerq.thor +28 -0
  9. data/docs/README.md +408 -0
  10. data/lib/assets/knb/SRS-IEEE-830-1998.md +293 -0
  11. data/lib/assets/knb/SRS-RUP.md +283 -0
  12. data/lib/assets/knb/business-case.md +135 -0
  13. data/lib/assets/knb/ears-with-examples.md +101 -0
  14. data/lib/assets/knb/problem-statement.md +8 -0
  15. data/lib/assets/knb/product-statement.md +8 -0
  16. data/lib/assets/knb/requirement-attributes.md +26 -0
  17. data/lib/assets/knb/requirement-classification.md +27 -0
  18. data/lib/assets/knb/requirement-life-cycle.md +47 -0
  19. data/lib/assets/knb/requirement-quality.md +13 -0
  20. data/lib/assets/knb/use-case.md +39 -0
  21. data/lib/assets/knb/vision-document.md +191 -0
  22. data/lib/assets/lib/clerq_doc.thor +119 -0
  23. data/lib/assets/lib/colonize_repo.rb +82 -0
  24. data/lib/assets/lib/spec/colonize_repo_spec.rb +85 -0
  25. data/lib/assets/new/clerq.thor.tt +32 -5
  26. data/lib/assets/new/content.md.tt +3 -40
  27. data/lib/assets/tt/default.md.erb +23 -42
  28. data/lib/assets/tt/pandoc.md.erb +11 -8
  29. data/lib/clerq.rb +57 -12
  30. data/lib/clerq/cli.rb +77 -60
  31. data/lib/clerq/entities.rb +0 -1
  32. data/lib/clerq/entities/node.rb +135 -115
  33. data/lib/clerq/properties.rb +1 -3
  34. data/lib/clerq/repositories.rb +2 -4
  35. data/lib/clerq/repositories/file_repository.rb +59 -0
  36. data/lib/clerq/repositories/node_repository.rb +72 -30
  37. data/lib/clerq/repositories/text_repository.rb +47 -0
  38. data/lib/clerq/services.rb +8 -0
  39. data/lib/clerq/services/check_assembly.rb +108 -0
  40. data/lib/clerq/{interactors → services}/create_node.rb +12 -11
  41. data/lib/clerq/services/load_assembly.rb +54 -0
  42. data/lib/clerq/services/query_node.rb +72 -0
  43. data/lib/clerq/services/query_template.rb +26 -0
  44. data/lib/clerq/services/read_node.rb +101 -0
  45. data/lib/clerq/services/render_erb.rb +29 -0
  46. data/lib/clerq/services/render_node.rb +37 -0
  47. data/lib/clerq/services/service.rb +19 -0
  48. data/lib/clerq/settings.rb +2 -2
  49. data/lib/clerq/version.rb +1 -1
  50. metadata +49 -37
  51. data/TODO.md +0 -3
  52. data/lib/assets/tt/gitlab.md.erb +0 -93
  53. data/lib/assets/tt/raw.md.erb +0 -23
  54. data/lib/clerq/entities/template.rb +0 -19
  55. data/lib/clerq/gateways.rb +0 -3
  56. data/lib/clerq/gateways/gateway.rb +0 -17
  57. data/lib/clerq/gateways/in_files.rb +0 -36
  58. data/lib/clerq/gateways/in_memory.rb +0 -35
  59. data/lib/clerq/interactors.rb +0 -5
  60. data/lib/clerq/interactors/check_nodes.rb +0 -81
  61. data/lib/clerq/interactors/compile_nodes.rb +0 -31
  62. data/lib/clerq/interactors/interactor.rb +0 -28
  63. data/lib/clerq/interactors/join_nodes.rb +0 -59
  64. data/lib/clerq/interactors/query_nodes.rb +0 -62
  65. data/lib/clerq/repositories/in_memory.rb +0 -45
  66. data/lib/clerq/repositories/node_reader.rb +0 -107
  67. data/lib/clerq/repositories/repository.rb +0 -11
  68. data/lib/clerq/repositories/template_repository.rb +0 -53
  69. data/lib/clerq/templater.rb +0 -32
@@ -4,9 +4,7 @@ module Clerq
4
4
  def property(name, options = {}, &validation)
5
5
  default_value = options[:default]
6
6
  define_method name do
7
- v = instance_variable_get("@#{name}")
8
- v = default_value unless v
9
- v
7
+ instance_variable_get("@#{name}") || default_value
10
8
  end
11
9
 
12
10
  define_method "#{name}=" do |val|
@@ -1,5 +1,3 @@
1
- require_relative 'repositories/repository'
2
- require_relative 'repositories/in_memory'
3
- require_relative 'repositories/node_reader'
1
+ require_relative 'repositories/file_repository'
4
2
  require_relative 'repositories/node_repository'
5
- require_relative 'repositories/template_repository'
3
+ require_relative 'repositories/text_repository'
@@ -0,0 +1,59 @@
1
+ # encoding: UTF-8
2
+
3
+ module Clerq
4
+ module Repositories
5
+
6
+ # The class provides File and Dir functions that executed relativly path
7
+ # provided in constructor.
8
+ #
9
+ # Usage:
10
+ # FileRepository.new(path: Dir.pwd, pattern: '*.*')
11
+ # FileRepository.new(path: Dir.pwd, pattern: ['*.rb', '*.md'])
12
+ class FileRepository
13
+ attr_reader :path
14
+ attr_reader :patt
15
+
16
+ # @param path [String]
17
+ # @param pattern [String, Array<String>]
18
+ def initialize(path: Dir.pwd, pattern: '*.*')
19
+ # TODO check that path exists and save it in full form
20
+ unless Dir.exist?(path)
21
+ msg = "'#{path}' directory does not exist!"
22
+ raise ArgumentError, msg
23
+ end
24
+ @path = path
25
+ @patt = pattern
26
+ end
27
+
28
+ def inside
29
+ Dir.chdir(@path) { yield }
30
+ end
31
+
32
+ protected
33
+
34
+ # @param pattern [String, Array<String>]
35
+ def glob(pattern = '')
36
+ pt = pattern.empty? ? @patt : pattern
37
+ pt = [pt] if pt.is_a?(String)
38
+ pt = pt.map{|p| p = File.join('**', p)}
39
+ Dir.chdir(@path) { Dir.glob pt }
40
+ end
41
+
42
+ def read(filename)
43
+ File.read(File.join @path, filename)
44
+ end
45
+
46
+ def write(filename, content)
47
+ join = File.join(@path, filename)
48
+ if File.exist?(join)
49
+ errmsg = "File '#{join}' alredy exists!"
50
+ raise StandardError, errmsg
51
+ end
52
+ File.write(join, content)
53
+ join
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+ end
@@ -1,55 +1,97 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require_relative '../entities'
4
- require_relative 'repository'
4
+ require_relative 'file_repository'
5
+ require_relative '../services/read_node'
5
6
  include Clerq::Entities
7
+ include Clerq::Services
6
8
 
7
9
  module Clerq
8
10
  module Repositories
9
11
 
10
- class NodeRepository < Repository
11
- def initialize(path = Dir.pwd)
12
- @path = path
13
- @items = []
14
- end
12
+ class NodeRepository < FileRepository
15
13
 
16
- def items(id = nil)
17
- load_nodes if @items.empty?
18
- return @items.find {|i| i.id == id} if id
19
- @items
14
+ def initialize(path: Dir.pwd, pattern: '*.md')
15
+ super(path: path, pattern: pattern)
20
16
  end
21
17
 
22
18
  def save(node)
23
- raise ArgumentError, "Invalid argument" unless node.is_a? Node
24
- Dir.chdir(@path){ File.write(node.id + '.md', markdown(node))}
19
+ check! node
20
+ write("#{node.id}.md", markup(node))
21
+ end
22
+
23
+ # asseble repository nodes hierarchy
24
+ # @return [Node]
25
+ def assemble(on_parse: nil, on_error: nil)
26
+ @node = Node.new(id: 'join', title: Clerq.title)
27
+ loadn = load(on_parse: on_parse, on_error: on_error)
28
+ loadn.each{|n| @node << n}
29
+ subo!
30
+ eqid!
31
+ if @node.items.size == 1
32
+ @node = @node.items.first
33
+ @node.orphan!
34
+ end
35
+ @node
25
36
  end
26
37
 
27
38
  protected
28
39
 
29
- def load_nodes
30
- @items = []
31
- Dir.chdir(@path) do
32
- Dir.glob('**/*.md').each do |f|
33
- # TODO: what to do with errors?
34
- nodes = NodeReader.(f)
35
- nodes.each{|n| @items << n}
40
+ def load(on_parse: nil, on_error: nil)
41
+ inside do
42
+ [].tap do |ary|
43
+ glob.each do |file|
44
+ on_parse.call(file) if on_parse
45
+ tmp = ReadNode.(file, on_error)
46
+ tmp.each{|node| ary << node }
36
47
  end
37
48
  end
38
49
  end
50
+ end
39
51
 
40
- def markdown(n)
41
- [].tap do |out|
42
- out << "#{'#' * (n.nesting_level + 1)} [#{n.id}] #{n.title}"
43
- unless n.meta.empty?
44
- out << '{{'
45
- n.meta.each{|k,v| out << "#{k}: #{v}"}
46
- out << '}}'
47
- out << "\n"
48
- end
49
- out << n.body
50
- end.join("\n")
52
+ def subo!
53
+ @node.items
54
+ .select{|n| n[:parent] && n[:parent] != n.parent.id}
55
+ .each{|n|
56
+ parent = @node.node(n[:parent])
57
+ next unless parent
58
+ parent << n
59
+ @node.items.delete(n)
60
+ n.meta.delete(:parent)
61
+ }
62
+ end
63
+
64
+ def eqid!
65
+ counter = {}
66
+ @node.select{|n| n.id.empty?}.each do |n|
67
+ # TODO maybe just .to_a.drop(1).select ?
68
+ next if n == @node
69
+ index = counter[n.parent] || 1
70
+ counter[n.parent] = index + 1
71
+ id = index.to_s.rjust(2, '0')
72
+ id = '.' + id unless n.parent == @node
73
+ n.id = id
51
74
  end
75
+ end
52
76
 
77
+ def check!(node)
78
+ return if node.is_a? Node
79
+ errmsg = "Invalid argument"
80
+ raise ArgumentError, errmsg, caller #caller[1..-1]
81
+ end
82
+
83
+ def markup(n)
84
+ head = "# [#{n.id}] #{n.title}"
85
+ meta = n.meta.empty? ? '' : n.meta
86
+ .map{|k,v| "#{k}: #{v}"}
87
+ .unshift("{{")
88
+ .push("}}")
89
+ .join("\n")
90
+ [head].tap do |txt|
91
+ txt << "\n#{meta}" unless meta.empty?
92
+ txt << "\n\n#{n.body}" unless n.body.empty?
93
+ end.join + "\n"
94
+ end
53
95
  end
54
96
 
55
97
  end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+ require_relative 'file_repository'
3
+
4
+ module Clerq
5
+ module Repositories
6
+
7
+ class TextRepository < FileRepository
8
+
9
+ def initialize(path: Dir.pwd, pattern: ['*.md.erb', '*.md.tt'])
10
+ super(path: path, pattern: pattern)
11
+ end
12
+
13
+ # Return template body @param name [String]
14
+ def text(name)
15
+ filename = find(name)
16
+ if filename.empty?
17
+ err = "File '#{name}' not found"
18
+ raise StandardError, err
19
+ end
20
+ read(filename)
21
+ end
22
+
23
+ # def find(filename)
24
+ # inside do
25
+ # return filename if File.exist?(filename)
26
+ # @patt.each do |p|
27
+ # fn = "#{filename}#{p[1..-1]}"
28
+ # return fn if File.exist?(fn)
29
+ # end
30
+ # end
31
+ # ''
32
+ # end
33
+
34
+ def find(name)
35
+ inside {
36
+ return name if File.exist?(name) and !File.directory?(name)}
37
+ all = glob
38
+ pos = @patt.map{|p| "#{name}#{p[1..-1]}"}.unshift(name)
39
+ all.find(lambda {''}){|n|
40
+ pos.include?(n) || n.start_with?(*pos) || n.end_with?(*pos)
41
+ }
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -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,22 +1,23 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require_relative "interactor"
3
+ require_relative "service"
4
+ require_relative "query_template"
5
+ require_relative "../entities/node"
4
6
 
5
7
  module Clerq
6
- module Interactors
8
+ module Services
7
9
 
8
- class CreateNode < Interactor
10
+ # Creates new node in repository according to provided parameters
11
+ # or raises CreateNode::Failure when
12
+ # * template not found
13
+ # * or repository contains a node with the same id
14
+ class CreateNode < Service
9
15
 
10
16
  def call
11
- unless @template.empty?
12
- tt = gateway.templates(@template)
13
- raise Failure, "Template not found" unless tt
14
- # TODO not just `@body = tt.body` but build `@body` in accordance with tt.body
15
- @body = tt.body
16
- end
17
- node = Clerq::Entities::Node.new(
17
+ @body = QueryTemplate.(@template) if @body.empty? && !@template.empty?
18
+ @node = Clerq::Entities::Node.new(
18
19
  id: @id, title: @title, body: @body, meta: @meta)
19
- gateway.save(node)
20
+ Clerq.node_repository.save(@node)
20
21
  end
21
22
 
22
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