umwelt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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'