tataru 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/Rakefile +8 -0
- data/lib/tataru.rb +15 -0
- data/lib/tataru/default_resource_finder.rb +10 -0
- data/lib/tataru/execution_step.rb +79 -0
- data/lib/tataru/instruction.rb +15 -0
- data/lib/tataru/planner.rb +93 -0
- data/lib/tataru/requirements.rb +67 -0
- data/lib/tataru/requirements_dsl.rb +33 -0
- data/lib/tataru/resource.rb +52 -0
- data/lib/tataru/resource_dsl.rb +86 -0
- data/lib/tataru/state.rb +64 -0
- data/lib/tataru/version.rb +5 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e42d1ffd98cb2d158b53f58b572cba30e0e08cb9da10f65386260bf24156b069
|
4
|
+
data.tar.gz: 2078749626908e57c5bef6759dbba0ff2e244638f4811c36d1cfc97dc841a688
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c9eefcedd70c5328abbae21d9e286d751e819f28b7fb85c64468aa3f978ddabb29f3f40cecf4bf557e9eb0da4ee6e78a8cf402b4e49ed9e145586ac3df52cbee
|
7
|
+
data.tar.gz: c5f88a391a8f67f6ab30afd095fbf438a8ae76dcc08e7febbaba563d733f5209da60bbb6c23e3d9f343e0256c04c329f689f1b0dde156083c0d9c3e348f33553
|
data/Rakefile
ADDED
data/lib/tataru.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require 'bunny/tsort'
|
5
|
+
|
6
|
+
require 'tataru/version'
|
7
|
+
require 'tataru/resource'
|
8
|
+
require 'tataru/state'
|
9
|
+
require 'tataru/instruction'
|
10
|
+
require 'tataru/execution_step'
|
11
|
+
require 'tataru/planner'
|
12
|
+
require 'tataru/default_resource_finder'
|
13
|
+
require 'tataru/requirements_dsl'
|
14
|
+
require 'tataru/resource_dsl'
|
15
|
+
require 'tataru/requirements'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
# An execution step
|
5
|
+
class ExecutionStep
|
6
|
+
def initialize(state, instruction)
|
7
|
+
@state = state
|
8
|
+
@instruction = instruction
|
9
|
+
@instances = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def id
|
13
|
+
@instruction.id
|
14
|
+
end
|
15
|
+
|
16
|
+
def rf
|
17
|
+
@instruction.requirements.resource_finder
|
18
|
+
end
|
19
|
+
|
20
|
+
def type_of_id
|
21
|
+
@instruction.requirements.type(id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def class_of_id
|
25
|
+
rf.resource_named(type_of_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def instance_of_id
|
29
|
+
@instances[id] ||= class_of_id.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def execute
|
33
|
+
return execute_begin! if @instruction.action.to_s.start_with?('begin_')
|
34
|
+
|
35
|
+
execute_wait!
|
36
|
+
end
|
37
|
+
|
38
|
+
def overall_action
|
39
|
+
@instruction.action.to_s.split('_')[1].to_sym
|
40
|
+
end
|
41
|
+
|
42
|
+
def execute_begin!
|
43
|
+
instance_of_id.send(@instruction.action, @state)
|
44
|
+
[send(:"begin_#{overall_action}"), true]
|
45
|
+
end
|
46
|
+
|
47
|
+
def begin_create
|
48
|
+
new_state = @state.clone
|
49
|
+
replacer = true
|
50
|
+
replacer = false if new_state[id].nil?
|
51
|
+
|
52
|
+
@instruction.state.each do |name, value|
|
53
|
+
new_state.putstate(id, name, value, replacer: replacer)
|
54
|
+
end
|
55
|
+
new_state.waiting_on(id, overall_action)
|
56
|
+
new_state
|
57
|
+
end
|
58
|
+
|
59
|
+
def begin_delete
|
60
|
+
begin_update
|
61
|
+
end
|
62
|
+
|
63
|
+
def begin_update
|
64
|
+
new_state = @state.clone
|
65
|
+
new_state.waiting_on(id, overall_action)
|
66
|
+
new_state
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute_wait!
|
70
|
+
new_state = @state.clone
|
71
|
+
success = instance_of_id.send(:"#{overall_action}_complete?", @state)
|
72
|
+
if success
|
73
|
+
new_state.no_longer_waiting(id)
|
74
|
+
new_state.replace(id) if overall_action == :delete
|
75
|
+
end
|
76
|
+
[new_state, success]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
# An instruction
|
5
|
+
class Instruction
|
6
|
+
attr_reader :action, :id, :state, :requirements
|
7
|
+
|
8
|
+
def initialize(action, id, state, requirements)
|
9
|
+
@action = action
|
10
|
+
@id = id
|
11
|
+
@state = state
|
12
|
+
@requirements = requirements
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
module Rage
|
5
|
+
# not valid
|
6
|
+
class InvalidRequirement < StandardError; end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# A plan
|
11
|
+
class Planner
|
12
|
+
def initialize(current_state, requirement)
|
13
|
+
@current_state = current_state
|
14
|
+
@requirement = requirement
|
15
|
+
@actions = {}
|
16
|
+
|
17
|
+
raise Rage::InvalidRequirement unless requirement.valid?
|
18
|
+
end
|
19
|
+
|
20
|
+
def order
|
21
|
+
Bunny::Tsort.tsort(@requirement.dep_tree)
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete_instruction_for(id, pref)
|
25
|
+
Instruction.new(:"#{pref}_delete", id, @current_state[id], @requirement)
|
26
|
+
end
|
27
|
+
|
28
|
+
def generate_delete_instruction_for(id, pref)
|
29
|
+
return if @current_state[id].nil?
|
30
|
+
return unless action(id) == :replace
|
31
|
+
|
32
|
+
delete_instruction_for(id, pref)
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate_instruction_for(id, pref)
|
36
|
+
if @current_state[id].nil?
|
37
|
+
Instruction.new(:"#{pref}_create", id, end_state[id], @requirement)
|
38
|
+
elsif action(id) == :replace
|
39
|
+
Instruction.new(:"#{pref}_create", id, end_state[id], @requirement)
|
40
|
+
elsif action(id) == :update
|
41
|
+
Instruction.new(:"#{pref}_update", id, end_state[id], @requirement)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_removal_instructions
|
46
|
+
remove_actions = []
|
47
|
+
@current_state.id_list.keys.each do |id|
|
48
|
+
next if @requirement.exist? id
|
49
|
+
|
50
|
+
remove_actions << delete_instruction_for(id, :begin)
|
51
|
+
end
|
52
|
+
remove_actions
|
53
|
+
end
|
54
|
+
|
55
|
+
def generate_delete_instructions
|
56
|
+
delete_actions = []
|
57
|
+
order.reverse.each do |step|
|
58
|
+
%i[begin wait].each do |substep|
|
59
|
+
step.each do |id|
|
60
|
+
delete_action = generate_delete_instruction_for(id, substep)
|
61
|
+
delete_actions << delete_action unless delete_action.nil?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
delete_actions + generate_removal_instructions
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_instructions
|
69
|
+
instr = []
|
70
|
+
order.each do |step|
|
71
|
+
%i[begin wait].each do |substep|
|
72
|
+
step.each do |id|
|
73
|
+
instruction = generate_instruction_for(id, substep)
|
74
|
+
instr << instruction unless instruction.nil?
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
instr + generate_delete_instructions
|
79
|
+
end
|
80
|
+
|
81
|
+
def instructions
|
82
|
+
@instructions ||= generate_instructions
|
83
|
+
end
|
84
|
+
|
85
|
+
def action(id)
|
86
|
+
@actions[id] ||= @requirement.action(id, @current_state[id])
|
87
|
+
end
|
88
|
+
|
89
|
+
def end_state
|
90
|
+
@requirement.end_state
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
# Requirements list
|
5
|
+
class Requirements
|
6
|
+
attr_reader :resource_finder, :errors
|
7
|
+
|
8
|
+
def initialize(resource_finder = DefaultResourceFinder.new, &block)
|
9
|
+
dsl = RequirementsDSL.new(resource_finder)
|
10
|
+
dsl.instance_exec(&block)
|
11
|
+
@errors = dsl.errors
|
12
|
+
@reqs = dsl.resource_list
|
13
|
+
@resource_finder = resource_finder
|
14
|
+
end
|
15
|
+
|
16
|
+
def dep_tree
|
17
|
+
@reqs.map { |k, v| [k, v[:dependencies]] }.to_h
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_state
|
21
|
+
state = State.new
|
22
|
+
@reqs.each do |id, info|
|
23
|
+
info[:state].each do |state_name, state_value|
|
24
|
+
state.putstate(id, state_name, state_value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
state
|
28
|
+
end
|
29
|
+
|
30
|
+
def exist?(id)
|
31
|
+
@reqs.key? id
|
32
|
+
end
|
33
|
+
|
34
|
+
def type(id)
|
35
|
+
@reqs[id][:type]
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid?
|
39
|
+
errors.length.zero?
|
40
|
+
end
|
41
|
+
|
42
|
+
def compare(id, current_state)
|
43
|
+
rclass = @resource_finder.resource_named(type(id))
|
44
|
+
resdef = rclass.new
|
45
|
+
changed = false
|
46
|
+
replace = false
|
47
|
+
|
48
|
+
current_state.each do |state_name, current_value|
|
49
|
+
next if current_value == @reqs[id][:state][state_name]
|
50
|
+
|
51
|
+
changed = true
|
52
|
+
replace ||= resdef.send(:"#{state_name}_change_action") == :replace
|
53
|
+
end
|
54
|
+
|
55
|
+
[changed, replace]
|
56
|
+
end
|
57
|
+
|
58
|
+
def action(id, current_state)
|
59
|
+
changed, replace = compare(id, current_state)
|
60
|
+
|
61
|
+
return :nothing unless changed
|
62
|
+
return :update unless replace
|
63
|
+
|
64
|
+
:replace
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
# Requirements DSL
|
5
|
+
class RequirementsDSL
|
6
|
+
attr_reader :resource_list
|
7
|
+
|
8
|
+
def initialize(resource_finder)
|
9
|
+
@resource_finder = resource_finder
|
10
|
+
@resource_list = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to_missing?
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(type, name, &block)
|
18
|
+
rclass = @resource_finder.resource_named(type)
|
19
|
+
res = ResourceDSL.new(rclass.new)
|
20
|
+
res.instance_exec(&block) if block
|
21
|
+
@resource_list[name] = {
|
22
|
+
type: type, dependencies: res.dependencies,
|
23
|
+
state: res.fields, errors: res.errors
|
24
|
+
}
|
25
|
+
rescue NameError
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def errors
|
30
|
+
@resource_list.flat_map { |_, v| v[:errors] }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
# base resource class
|
5
|
+
class Resource
|
6
|
+
class << self
|
7
|
+
def state(state_name, change_behaviour)
|
8
|
+
define_method "#{state_name}_change_action" do
|
9
|
+
change_behaviour
|
10
|
+
end
|
11
|
+
|
12
|
+
define_method "_state_#{state_name}" do
|
13
|
+
state_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def output(output_name)
|
18
|
+
define_method "_output_#{output_name}" do
|
19
|
+
output_name
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method(output_name) {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def states
|
27
|
+
list_props(:state)
|
28
|
+
end
|
29
|
+
|
30
|
+
def outputs
|
31
|
+
list_props(:output)
|
32
|
+
end
|
33
|
+
|
34
|
+
%i[
|
35
|
+
begin_create
|
36
|
+
begin_update
|
37
|
+
begin_delete
|
38
|
+
create_complete?
|
39
|
+
update_complete?
|
40
|
+
delete_complete?
|
41
|
+
].each do |thing|
|
42
|
+
define_method(thing) { |_state| }
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def list_props(prop_type)
|
48
|
+
methods.select { |x| x.to_s.start_with? "_#{prop_type}_" }
|
49
|
+
.map { |x| x.to_s.sub("_#{prop_type}_", '').to_sym }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
module DoLater
|
5
|
+
# delayed expression
|
6
|
+
class Expression
|
7
|
+
def requested_resources
|
8
|
+
raise 'Abstract class'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# placeholder for extern resource
|
13
|
+
class ExternResourcePlaceholder < Expression
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to_missing?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(name, *_args)
|
23
|
+
super if name.nil?
|
24
|
+
|
25
|
+
MemberCallPlaceholder.new(self, name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def requested_resources
|
29
|
+
[@name]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# placeholder for member call
|
34
|
+
class MemberCallPlaceholder < ExternResourcePlaceholder
|
35
|
+
def initialize(expr, member)
|
36
|
+
@expr = expr
|
37
|
+
@member = member
|
38
|
+
end
|
39
|
+
|
40
|
+
def requested_resources
|
41
|
+
@expr.requested_resources
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Resource DSL
|
47
|
+
class ResourceDSL
|
48
|
+
attr_reader :fields, :extern_refs
|
49
|
+
|
50
|
+
def initialize(resource_inst)
|
51
|
+
@resource_inst = resource_inst
|
52
|
+
@fields = {}
|
53
|
+
@extern_refs = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def respond_to_missing?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def method_missing(name, *args, &block)
|
61
|
+
if @resource_inst.respond_to?("#{name}_change_action")
|
62
|
+
@fields[name] = args[0]
|
63
|
+
elsif name.to_s.start_with?(/[a-z]/)
|
64
|
+
@extern_refs[name] ||= DoLater::ExternResourcePlaceholder.new(name)
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def errors
|
71
|
+
(@resource_inst.states - @fields.keys).map do |x|
|
72
|
+
{ missing_state: x }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def dependencies
|
77
|
+
deps = []
|
78
|
+
@fields.each do |_name, info|
|
79
|
+
next unless info.is_a? DoLater::Expression
|
80
|
+
|
81
|
+
deps += info.requested_resources
|
82
|
+
end
|
83
|
+
deps.map(&:to_s).uniq
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/tataru/state.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tataru
|
4
|
+
# The state of the environment
|
5
|
+
class State
|
6
|
+
def initialize(objstate = {})
|
7
|
+
@current_ids = objstate[:current_ids] || {}
|
8
|
+
@replacer_ids = objstate[:replacer_ids] || {}
|
9
|
+
@waiting_ids = objstate[:waiting_ids] || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def putstate(id, state, value, replacer: false)
|
13
|
+
ids = id_list(replacer)
|
14
|
+
ids[id] = {} unless ids.key? id
|
15
|
+
ids[id][state] = value
|
16
|
+
end
|
17
|
+
|
18
|
+
def getstate(id, state, replacer: false)
|
19
|
+
ids = id_list(replacer)
|
20
|
+
return unless ids.key? id
|
21
|
+
|
22
|
+
ids[id][state]
|
23
|
+
end
|
24
|
+
|
25
|
+
def id_list(replacer = false)
|
26
|
+
return @replacer_ids if replacer
|
27
|
+
|
28
|
+
@current_ids
|
29
|
+
end
|
30
|
+
|
31
|
+
def replace(id)
|
32
|
+
@current_ids[id] = @replacer_ids[id]
|
33
|
+
@replacer_ids.delete(id)
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete_list
|
37
|
+
@replacer_ids.keys.select { |x| @current_ids.key? x }
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](id)
|
41
|
+
@current_ids[id].clone
|
42
|
+
end
|
43
|
+
|
44
|
+
def waiting_on(id, what)
|
45
|
+
@waiting_ids[id] = what
|
46
|
+
end
|
47
|
+
|
48
|
+
def no_longer_waiting(id)
|
49
|
+
@waiting_ids.delete(id)
|
50
|
+
end
|
51
|
+
|
52
|
+
def waiting_list
|
53
|
+
@waiting_ids.clone
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_h
|
57
|
+
{
|
58
|
+
current_ids: @current_ids,
|
59
|
+
replacer_ids: @replacer_ids,
|
60
|
+
waiting_ids: @waiting_ids
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tataru
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Siaw
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-12-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bunny-tsort
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.17'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.17'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: Sworn upon the name of the receptionist
|
84
|
+
email:
|
85
|
+
- dsiaw@degica.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- Rakefile
|
91
|
+
- lib/tataru.rb
|
92
|
+
- lib/tataru/default_resource_finder.rb
|
93
|
+
- lib/tataru/execution_step.rb
|
94
|
+
- lib/tataru/instruction.rb
|
95
|
+
- lib/tataru/planner.rb
|
96
|
+
- lib/tataru/requirements.rb
|
97
|
+
- lib/tataru/requirements_dsl.rb
|
98
|
+
- lib/tataru/resource.rb
|
99
|
+
- lib/tataru/resource_dsl.rb
|
100
|
+
- lib/tataru/state.rb
|
101
|
+
- lib/tataru/version.rb
|
102
|
+
homepage: https://github.com/davidsiaw/tataru
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata:
|
106
|
+
allowed_push_host: https://rubygems.org
|
107
|
+
homepage_uri: https://github.com/davidsiaw/tataru
|
108
|
+
source_code_uri: https://github.com/davidsiaw/tataru
|
109
|
+
changelog_uri: https://github.com/davidsiaw/tataru
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubygems_version: 3.0.3
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Task planner
|
129
|
+
test_files: []
|