umwelt 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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +46 -0
  3. data/.gitignore +8 -0
  4. data/.rubocop.yml +28 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +7 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +8 -0
  10. data/Gemfile.lock +79 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +75 -0
  13. data/Rakefile +12 -0
  14. data/bin/console +14 -0
  15. data/bin/setup +8 -0
  16. data/bin/umwelt +7 -0
  17. data/lib/umwelt.rb +23 -0
  18. data/lib/umwelt/abstract.rb +7 -0
  19. data/lib/umwelt/abstract/file.rb +7 -0
  20. data/lib/umwelt/abstract/file/restore.rb +48 -0
  21. data/lib/umwelt/abstract/file/store.rb +44 -0
  22. data/lib/umwelt/abstract/mapper.rb +17 -0
  23. data/lib/umwelt/cli.rb +6 -0
  24. data/lib/umwelt/cli/commands.rb +15 -0
  25. data/lib/umwelt/cli/commands/clone.rb +16 -0
  26. data/lib/umwelt/cli/commands/convey.rb +57 -0
  27. data/lib/umwelt/cli/commands/pull.rb +11 -0
  28. data/lib/umwelt/cli/commands/version.rb +9 -0
  29. data/lib/umwelt/command.rb +6 -0
  30. data/lib/umwelt/command/convey.rb +84 -0
  31. data/lib/umwelt/episode.rb +7 -0
  32. data/lib/umwelt/episode/file.rb +7 -0
  33. data/lib/umwelt/episode/file/restore.rb +22 -0
  34. data/lib/umwelt/episode/file/store.rb +15 -0
  35. data/lib/umwelt/episode/mapper.rb +33 -0
  36. data/lib/umwelt/fragment.rb +6 -0
  37. data/lib/umwelt/fragment/mapper.rb +11 -0
  38. data/lib/umwelt/history.rb +10 -0
  39. data/lib/umwelt/history/aggregate.rb +49 -0
  40. data/lib/umwelt/history/file.rb +7 -0
  41. data/lib/umwelt/history/file/restore.rb +22 -0
  42. data/lib/umwelt/history/file/store.rb +15 -0
  43. data/lib/umwelt/history/follow.rb +32 -0
  44. data/lib/umwelt/history/mapper.rb +33 -0
  45. data/lib/umwelt/history/trace.rb +51 -0
  46. data/lib/umwelt/node.rb +46 -0
  47. data/lib/umwelt/node/build.rb +31 -0
  48. data/lib/umwelt/node/root.rb +6 -0
  49. data/lib/umwelt/node/space.rb +6 -0
  50. data/lib/umwelt/phase.rb +6 -0
  51. data/lib/umwelt/phase/mapper.rb +18 -0
  52. data/lib/umwelt/project.rb +7 -0
  53. data/lib/umwelt/project/file.rb +7 -0
  54. data/lib/umwelt/project/file/restore.rb +22 -0
  55. data/lib/umwelt/project/file/store.rb +15 -0
  56. data/lib/umwelt/project/mapper.rb +11 -0
  57. data/lib/umwelt/semantic.rb +57 -0
  58. data/lib/umwelt/semantic/plain.rb +12 -0
  59. data/lib/umwelt/semantic/plain/root.rb +13 -0
  60. data/lib/umwelt/semantic/plain/space.rb +13 -0
  61. data/lib/umwelt/structs/episode.rb +9 -0
  62. data/lib/umwelt/structs/fragment.rb +13 -0
  63. data/lib/umwelt/structs/history.rb +3 -0
  64. data/lib/umwelt/structs/node.rb +13 -0
  65. data/lib/umwelt/structs/phase.rb +13 -0
  66. data/lib/umwelt/structs/project.rb +10 -0
  67. data/lib/umwelt/structs/trunk.rb +8 -0
  68. data/lib/umwelt/tree.rb +8 -0
  69. data/lib/umwelt/tree/fill.rb +35 -0
  70. data/lib/umwelt/tree/imprint.rb +53 -0
  71. data/lib/umwelt/tree/trunk.rb +27 -0
  72. data/lib/umwelt/version.rb +5 -0
  73. data/umwelt.gemspec +56 -0
  74. metadata +248 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../structs/fragment'
4
+
5
+ module Umwelt::Fragment
6
+ class Mapper < Umwelt::Abstract::Mapper
7
+ def call(data)
8
+ @struct = fill(Struct::Fragment, data)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::History
4
+ end
5
+
6
+ require_relative './history/file'
7
+ require_relative './history/mapper'
8
+ require_relative './history/aggregate'
9
+ require_relative './history/follow'
10
+ require_relative './history/trace'
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::History
4
+ class Aggregate
5
+ include Hanami::Interactor
6
+
7
+ expose :fragments
8
+
9
+ def initialize
10
+ @engaged = {}
11
+ @forgotten = []
12
+ end
13
+
14
+ def call(episodes)
15
+ episodes.each do |episode|
16
+ index_engaged(episode.engaged)
17
+ collect_forgotten(episode.forgotten)
18
+ end
19
+
20
+ verify_consistency
21
+
22
+ @fragments = @engaged.slice(*alive_ids).values
23
+ end
24
+
25
+ private
26
+
27
+ def alive_ids
28
+ @engaged.keys - @forgotten
29
+ end
30
+
31
+ def fantom_ids
32
+ @forgotten - @engaged.keys
33
+ end
34
+
35
+ def verify_consistency
36
+ fantom_ids.empty? || error!("Aggregate failed: fantom ids: #{fantom_ids}")
37
+ end
38
+
39
+ def collect_forgotten(ids)
40
+ @forgotten.push(*ids)
41
+ end
42
+
43
+ def index_engaged(fragments)
44
+ fragments.each do |fragment|
45
+ @engaged[fragment[:id]] = fragment
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::History::File
4
+ end
5
+
6
+ require_relative './file/store'
7
+ require_relative './file/restore'
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../structs/history'
4
+
5
+ module Umwelt::History::File
6
+ class Restore < Umwelt::Abstract::File::Restore
7
+ def initialize(
8
+ path: '.umwelt',
9
+ mapper: Umwelt::History::Mapper
10
+ )
11
+ super
12
+ end
13
+
14
+ def call
15
+ @struct = struct parse read full_path
16
+ end
17
+
18
+ def full_path
19
+ umwelt_root_path / 'history.json'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::History::File
4
+ class Store < Umwelt::Abstract::File::Store
5
+ def call(struct)
6
+ mkpath full_path.dirname
7
+
8
+ full_path.write serialize destruct struct
9
+ end
10
+
11
+ def full_path
12
+ umwelt_root_path / 'history.json'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::History
4
+ class Follow
5
+ include Hanami::Interactor
6
+
7
+ expose :episodes
8
+
9
+ def initialize(
10
+ reader: Umwelt::Episode::File::Restore,
11
+ path: '.umwelt'
12
+ )
13
+ @reader = reader.new(path: path)
14
+ end
15
+
16
+ def call(phases)
17
+ @episodes = phases.map { |phase| episode(phase) }
18
+ end
19
+
20
+ private
21
+
22
+ def episode(phase)
23
+ restored = restore_episode(phase.id)
24
+ error! restored.errors if restored.failure?
25
+ restored.struct
26
+ end
27
+
28
+ def restore_episode(phase_id)
29
+ @reader.call(phase_id)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../structs/phase'
4
+ require_relative '../structs/project'
5
+
6
+ module Umwelt::History
7
+ class Mapper < Umwelt::Abstract::Mapper
8
+ def call(
9
+ data = {
10
+ project: {},
11
+ phases: []
12
+ }
13
+ )
14
+
15
+ @struct = Struct::History.new(
16
+ project: project(data[:project]),
17
+ phases: phases(data[:phases])
18
+ )
19
+ end
20
+
21
+ def phases(phases_data)
22
+ phases_data.map { |phase_data| phase(phase_data) }
23
+ end
24
+
25
+ def project(data)
26
+ fill(Struct::Project, data)
27
+ end
28
+
29
+ def phase(data)
30
+ fill(Struct::Phase, data)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::History
4
+ class Trace
5
+ include Hanami::Interactor
6
+
7
+ expose :continuity
8
+
9
+ def initialize
10
+ @queue = Queue.new # for phases
11
+ @phases_index = {}
12
+ @continuity = []
13
+ @timeline = {}
14
+ end
15
+
16
+ def call(history, phase_id)
17
+ to_index(history.phases)
18
+
19
+ @queue.push from_index(phase_id)
20
+
21
+ loop do
22
+ break if @queue.empty?
23
+
24
+ process(@queue.pop)
25
+ end
26
+
27
+ @continuity = @timeline.values
28
+ end
29
+
30
+ def process(phase)
31
+ @timeline[phase[:id]] = phase
32
+ enqueue(phase)
33
+ end
34
+
35
+ def enqueue(phase)
36
+ @queue.push from_index(phase.merge_id) unless phase.merge_id.nil?
37
+ @queue.push from_index(phase.parent_id) unless phase.parent_id.nil?
38
+ end
39
+
40
+ def from_index(phase_id)
41
+ phase = @phases_index[phase_id]
42
+ phase || error!("Phase with ID #{phase_id} not exist, but referenced")
43
+ end
44
+
45
+ def to_index(phases)
46
+ phases.each do |phase|
47
+ @phases_index[phase[:id]] = phase
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './structs/node'
4
+
5
+ module Umwelt::Node
6
+ class Base < Struct::Node
7
+ def semantics
8
+ [:Plain]
9
+ end
10
+
11
+ def semantic(semantic_name)
12
+ semantic_klass(semantic_name).new(node: self)
13
+ end
14
+
15
+ def ancestry
16
+ if context_id
17
+ context.ancestry << self
18
+ else
19
+ [self]
20
+ end
21
+ end
22
+
23
+ def context
24
+ tree.node(context_id)
25
+ end
26
+
27
+ def label
28
+ self.class.name.split('::').last.to_sym
29
+ end
30
+
31
+ private
32
+
33
+ # Umwelt::Node::Space.new.semantic_klass(semantic)
34
+ # => Umwelt::Semantic::Plain::Space
35
+ def semantic_klass(semantic_name)
36
+ [semantic_name, label]
37
+ .reduce(Umwelt::Semantic) do |klass, name|
38
+ klass.const_get name
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ require_relative './node/build'
45
+ require_relative './node/root'
46
+ require_relative './node/space'
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Node
4
+ class Build
5
+ extend Forwardable
6
+ def_delegators Hanami::Utils::String, :classify
7
+
8
+ def initialize(tree)
9
+ @tree = tree
10
+ end
11
+
12
+ def call(frag)
13
+ node_klass(frag.kind).new(attributes(frag))
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :tree
19
+
20
+ def attributes(frag)
21
+ frag
22
+ .to_h
23
+ .slice(:id, :abstract_id, :context_id, :body, :note)
24
+ .merge(tree: tree)
25
+ end
26
+
27
+ def node_klass(kind)
28
+ Umwelt::Node.const_get classify(kind)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Node
4
+ class Root < Base
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Node
4
+ class Space < Base
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Phase
4
+ end
5
+
6
+ require_relative './phase/mapper'
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../structs/phase'
4
+
5
+ module Umwelt::Phase
6
+ class Mapper < Umwelt::Abstract::Mapper
7
+ def call(data)
8
+ @struct = fill(
9
+ Struct::Phase,
10
+ data.merge(finished_at: time(data[:finished_at]))
11
+ )
12
+ end
13
+
14
+ def time(str)
15
+ Time.parse str if str
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Project
4
+ end
5
+
6
+ require_relative './project/file'
7
+ require_relative './project/mapper'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Project::File
4
+ end
5
+
6
+ require_relative './file/store'
7
+ require_relative './file/restore'
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../structs/history'
4
+
5
+ module Umwelt::Project::File
6
+ class Restore < Umwelt::Abstract::File::Restore
7
+ def initialize(
8
+ path: '.umwelt',
9
+ mapper: Umwelt::Project::Mapper
10
+ )
11
+ super
12
+ end
13
+
14
+ def call
15
+ @struct = struct parse read full_path
16
+ end
17
+
18
+ def full_path
19
+ umwelt_root_path / 'project.json'
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Project::File
4
+ class Store < Umwelt::Abstract::File::Store
5
+ def call(struct)
6
+ mkpath full_path.dirname
7
+
8
+ full_path.write serialize destruct struct
9
+ end
10
+
11
+ def full_path
12
+ umwelt_root_path / 'project.json'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../structs/project'
4
+
5
+ module Umwelt::Project
6
+ class Mapper < Umwelt::Abstract::Mapper
7
+ def call(data)
8
+ @struct = fill(Struct::Project, data)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Umwelt::Semantic
4
+ class Base
5
+ extend Forwardable
6
+ def_delegators Hanami::Utils::String, :classify, :underscore
7
+ def_delegators :node, :ancestry, :body, :note
8
+
9
+ attr_reader :node
10
+
11
+ def initialize(node:)
12
+ @node = node
13
+ end
14
+
15
+ def s(type, *children)
16
+ Parser::AST::Node.new(type, children)
17
+ end
18
+
19
+ def ancestry_path
20
+ ancestry
21
+ .collect(&:body)
22
+ .map { |body| underscore(body) }
23
+ .join '/'
24
+ end
25
+
26
+ def code
27
+ Unparser.unparse(ast)
28
+ end
29
+
30
+ # default_location defined, for example,
31
+ # in Semantic::Plain::Base
32
+ # or base file from another semantic
33
+ def path(location: nil)
34
+ Pathname.pwd / (location || default_location) / tail_path
35
+ end
36
+
37
+ # nil check for case of root node, which has not context
38
+ def context
39
+ node.context&.semantic(:Plain)
40
+ end
41
+
42
+ # classified symbol
43
+ def csymbol
44
+ classify(body).to_sym
45
+ end
46
+
47
+ def label
48
+ self.class.name.split('::').last.to_sym
49
+ end
50
+
51
+ def tail_path
52
+ Pathname.new(ancestry_path).sub_ext('.rb')
53
+ end
54
+ end
55
+ end
56
+
57
+ require_relative './semantic/plain'