umwelt 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +46 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +28 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +79 -0
- data/LICENSE.txt +21 -0
- data/README.md +75 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/umwelt +7 -0
- data/lib/umwelt.rb +23 -0
- data/lib/umwelt/abstract.rb +7 -0
- data/lib/umwelt/abstract/file.rb +7 -0
- data/lib/umwelt/abstract/file/restore.rb +48 -0
- data/lib/umwelt/abstract/file/store.rb +44 -0
- data/lib/umwelt/abstract/mapper.rb +17 -0
- data/lib/umwelt/cli.rb +6 -0
- data/lib/umwelt/cli/commands.rb +15 -0
- data/lib/umwelt/cli/commands/clone.rb +16 -0
- data/lib/umwelt/cli/commands/convey.rb +57 -0
- data/lib/umwelt/cli/commands/pull.rb +11 -0
- data/lib/umwelt/cli/commands/version.rb +9 -0
- data/lib/umwelt/command.rb +6 -0
- data/lib/umwelt/command/convey.rb +84 -0
- data/lib/umwelt/episode.rb +7 -0
- data/lib/umwelt/episode/file.rb +7 -0
- data/lib/umwelt/episode/file/restore.rb +22 -0
- data/lib/umwelt/episode/file/store.rb +15 -0
- data/lib/umwelt/episode/mapper.rb +33 -0
- data/lib/umwelt/fragment.rb +6 -0
- data/lib/umwelt/fragment/mapper.rb +11 -0
- data/lib/umwelt/history.rb +10 -0
- data/lib/umwelt/history/aggregate.rb +49 -0
- data/lib/umwelt/history/file.rb +7 -0
- data/lib/umwelt/history/file/restore.rb +22 -0
- data/lib/umwelt/history/file/store.rb +15 -0
- data/lib/umwelt/history/follow.rb +32 -0
- data/lib/umwelt/history/mapper.rb +33 -0
- data/lib/umwelt/history/trace.rb +51 -0
- data/lib/umwelt/node.rb +46 -0
- data/lib/umwelt/node/build.rb +31 -0
- data/lib/umwelt/node/root.rb +6 -0
- data/lib/umwelt/node/space.rb +6 -0
- data/lib/umwelt/phase.rb +6 -0
- data/lib/umwelt/phase/mapper.rb +18 -0
- data/lib/umwelt/project.rb +7 -0
- data/lib/umwelt/project/file.rb +7 -0
- data/lib/umwelt/project/file/restore.rb +22 -0
- data/lib/umwelt/project/file/store.rb +15 -0
- data/lib/umwelt/project/mapper.rb +11 -0
- data/lib/umwelt/semantic.rb +57 -0
- data/lib/umwelt/semantic/plain.rb +12 -0
- data/lib/umwelt/semantic/plain/root.rb +13 -0
- data/lib/umwelt/semantic/plain/space.rb +13 -0
- data/lib/umwelt/structs/episode.rb +9 -0
- data/lib/umwelt/structs/fragment.rb +13 -0
- data/lib/umwelt/structs/history.rb +3 -0
- data/lib/umwelt/structs/node.rb +13 -0
- data/lib/umwelt/structs/phase.rb +13 -0
- data/lib/umwelt/structs/project.rb +10 -0
- data/lib/umwelt/structs/trunk.rb +8 -0
- data/lib/umwelt/tree.rb +8 -0
- data/lib/umwelt/tree/fill.rb +35 -0
- data/lib/umwelt/tree/imprint.rb +53 -0
- data/lib/umwelt/tree/trunk.rb +27 -0
- data/lib/umwelt/version.rb +5 -0
- data/umwelt.gemspec +56 -0
- metadata +248 -0
@@ -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,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
|
data/lib/umwelt/node.rb
ADDED
@@ -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
|
data/lib/umwelt/phase.rb
ADDED
@@ -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,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,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'
|