taski 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.
- checksums.yaml +7 -0
- data/README.md +65 -0
- data/Rakefile +13 -0
- data/lib/taski/version.rb +5 -0
- data/lib/taski.rb +155 -0
- data/sig/taski.rbs +4 -0
- metadata +53 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 260795a6ff4d622bd95b3bfb2668ba6123f17339650ea9371ea54d2210691d98
|
4
|
+
data.tar.gz: 60d7f97a6c736ed91e50bd2e8e9f48c163f76024e7407c0da96c9d77f4394514
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 71ccbe4131ac8409636071355af2de6cfda0862e21fb012e70b2f2fe60cb3bbc7023d640f9d3dc889f6759e8885a5dba5a1c0d543a6aa05e9299aa9ff401df54
|
7
|
+
data.tar.gz: a0a291d6fdd8d9ec1a122dbca9d115aa5c36f8008e7812120b0356e218ea895f46b292eab4404404c611c1f1b69e6dc9e4247f18d2cfc7a079b7d1f0ec2716d6
|
data/README.md
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# Taski
|
2
|
+
|
3
|
+
**Taski** is a Ruby-based task runner designed for small, composable processing steps.
|
4
|
+
With Taski, you define tasks and the outputs they depend on. Taski then statically resolves task dependencies and determines the correct execution order.
|
5
|
+
|
6
|
+
Tasks are executed in a topologically sorted order, ensuring that all dependencies are resolved before a task is run. Reverse execution is also supported, making it easy to clean up intermediate files after a build process.
|
7
|
+
|
8
|
+
> **🚧 Development Status:** Taski is currently under active development and the API may change.
|
9
|
+
|
10
|
+
> **⚠️ Limitation:** Circular dependencies are **not** supported at this time.
|
11
|
+
|
12
|
+
### Features
|
13
|
+
|
14
|
+
- Simple and declarative task definitions
|
15
|
+
- Static dependency resolution
|
16
|
+
- Topological execution order
|
17
|
+
- Reverse execution for teardown or cleanup
|
18
|
+
- Built entirely in Ruby
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Install the gem and add to the application's Gemfile by executing:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
bundle add taski
|
26
|
+
```
|
27
|
+
|
28
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
gem install taski
|
32
|
+
```
|
33
|
+
|
34
|
+
## Usage
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
class TaskA < Taski::Task
|
38
|
+
definition :task_a, -> { "Task A" }
|
39
|
+
|
40
|
+
def build
|
41
|
+
task_a
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class TaskB < Taski::Task
|
46
|
+
definition :simple_task, -> { "Task result is #{TaskA.task_a}" }
|
47
|
+
|
48
|
+
def build
|
49
|
+
puts simple_task
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
TaskB.build
|
54
|
+
# => Task result is Task A
|
55
|
+
```
|
56
|
+
|
57
|
+
## Development
|
58
|
+
|
59
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
60
|
+
|
61
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
62
|
+
|
63
|
+
## Contributing
|
64
|
+
|
65
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/taski.
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
task default: %i[test]
|
data/lib/taski.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "taski/version"
|
4
|
+
|
5
|
+
module Taski
|
6
|
+
class Reference
|
7
|
+
def initialize(klass)
|
8
|
+
@klass = klass
|
9
|
+
end
|
10
|
+
|
11
|
+
def deref
|
12
|
+
Object.const_get(@klass)
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"&#{@klass}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Task
|
21
|
+
def self.__resolve__
|
22
|
+
@__resolve__ ||= {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.ref(klass)
|
26
|
+
ref = Reference.new(klass)
|
27
|
+
throw :unresolve, ref
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.definition(name, block, **options)
|
31
|
+
@dependencies ||= []
|
32
|
+
@definitions ||= {}
|
33
|
+
|
34
|
+
self.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
35
|
+
def self.#{name}
|
36
|
+
__resolve__[__callee__] ||= false
|
37
|
+
if __resolve__[__callee__]
|
38
|
+
# already resolved
|
39
|
+
else
|
40
|
+
__resolve__[__callee__] = true
|
41
|
+
throw :unresolve, [self, __callee__]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
RUBY
|
45
|
+
|
46
|
+
classes = []
|
47
|
+
loop do
|
48
|
+
klass, task = catch(:unresolve) do
|
49
|
+
block.call
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
if klass.nil?
|
54
|
+
classes.each do |task_class|
|
55
|
+
task_class[:klass].class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
56
|
+
__resolve__ = {}
|
57
|
+
RUBY
|
58
|
+
end
|
59
|
+
|
60
|
+
break
|
61
|
+
else
|
62
|
+
classes << { klass:, task: }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
@dependencies += classes
|
67
|
+
@definitions[name] = {block:, options:, classes:}
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.build
|
71
|
+
resolve_dependences.reverse.each do |task_class|
|
72
|
+
task_class.new.build
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.clean
|
77
|
+
resolve_dependences.each do |task_class|
|
78
|
+
task_class.new.clean
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def exec_command(command, info = nil, ret = false)
|
83
|
+
puts "exec: #{info}" if info
|
84
|
+
puts command
|
85
|
+
|
86
|
+
if ret
|
87
|
+
ret = `#{command}`.chomp
|
88
|
+
if $?.exited?
|
89
|
+
ret
|
90
|
+
else
|
91
|
+
raise "Failed to execute command: #{command}"
|
92
|
+
end
|
93
|
+
else
|
94
|
+
system command, exception: true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.refresh
|
99
|
+
# TODO
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def self.resolve(queue, resolved)
|
105
|
+
@dependencies.each do |task|
|
106
|
+
if task[:klass].is_a?(Reference)
|
107
|
+
task_class = task[:klass].deref
|
108
|
+
end
|
109
|
+
|
110
|
+
# increase priority
|
111
|
+
if resolved.include?(task[:klass])
|
112
|
+
resolved.delete(task[:klass])
|
113
|
+
end
|
114
|
+
queue << task[:klass]
|
115
|
+
end
|
116
|
+
|
117
|
+
# override
|
118
|
+
self.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
119
|
+
def self.ref(klass)
|
120
|
+
Object.const_get(klass)
|
121
|
+
end
|
122
|
+
RUBY
|
123
|
+
|
124
|
+
@definitions.each do |name, (block, options)|
|
125
|
+
# override
|
126
|
+
self.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
127
|
+
def self.#{name}
|
128
|
+
@__#{name} ||= @definitions[:#{name}][:block].call
|
129
|
+
end
|
130
|
+
RUBY
|
131
|
+
|
132
|
+
self.define_method(name) do
|
133
|
+
unless instance_variable_defined?("@__#{name}")
|
134
|
+
instance_variable_set("@__#{name}", self.class.send(name))
|
135
|
+
end
|
136
|
+
instance_variable_get("@__#{name}")
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.resolve_dependences
|
144
|
+
queue = [self]
|
145
|
+
resolved = []
|
146
|
+
|
147
|
+
while queue.any?
|
148
|
+
resolved << task_class = queue.shift
|
149
|
+
task_class.resolve(queue, resolved)
|
150
|
+
end
|
151
|
+
|
152
|
+
resolved
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/sig/taski.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: taski
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ahogappa
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies: []
|
12
|
+
description: Taski is a Ruby-based task runner currently under development. It allows
|
13
|
+
you to define small, composable tasks along with the outputs they depend on. Taski
|
14
|
+
statically resolves dependencies and executes tasks in the correct topological order,
|
15
|
+
from the most dependent tasks first. It also supports reverse execution, useful
|
16
|
+
for cleaning up temporary files after a build. **Note:** Taski does not yet support
|
17
|
+
circular dependencies and may change as development progresses.
|
18
|
+
email:
|
19
|
+
- ahogappa@gmail.com
|
20
|
+
executables: []
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- lib/taski.rb
|
27
|
+
- lib/taski/version.rb
|
28
|
+
- sig/taski.rbs
|
29
|
+
homepage: https://github.com/ahogappa/taski
|
30
|
+
licenses: []
|
31
|
+
metadata:
|
32
|
+
homepage_uri: https://github.com/ahogappa/taski
|
33
|
+
source_code_uri: https://github.com/ahogappa/taski
|
34
|
+
changelog_uri: https://github.com/ahogappa/taski
|
35
|
+
rdoc_options: []
|
36
|
+
require_paths:
|
37
|
+
- lib
|
38
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 3.1.0
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
requirements: []
|
49
|
+
rubygems_version: 3.6.7
|
50
|
+
specification_version: 4
|
51
|
+
summary: A simple yet powerful Ruby task runner with static dependency resolution
|
52
|
+
(in development).
|
53
|
+
test_files: []
|