taski 0.1.0 → 0.1.1
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 +4 -4
- data/LICENSE +21 -0
- data/README.md +26 -23
- data/lib/taski/utils.rb +53 -0
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +89 -101
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d04adfab160e07e101e7d6364796d5607a896010396950ee5d479824bb4c422e
|
4
|
+
data.tar.gz: aeb0ea3476853608c0ef77e80e907c79db7ea62c1aaead8a22d8232d7e4c13aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3201ee69bc45ae1e58569bf1a98647ff7e38adabf0d04334805fdbac816468d420b1d460113970e1fc9394e6f9e6ca2fe4c700e0c390b9032366a01690c7762
|
7
|
+
data.tar.gz: 81a9796f7234f8f66216b61b7ea676b40614bb8258b8a6d6a0244e5d016319ea1492f7817324e1669a6ede1eeaf3f0ad4d8b71fd210168022838f66047348bff
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 ahogappa
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -1,49 +1,37 @@
|
|
1
1
|
# Taski
|
2
2
|
|
3
3
|
**Taski** is a Ruby-based task runner designed for small, composable processing steps.
|
4
|
-
|
4
|
+
In Taski, you define tasks as Ruby classes that expose named values through `define`. Dependencies between tasks are established automatically when one task references the result of another—no need for explicit dependency declarations.
|
5
5
|
|
6
|
-
Tasks are executed in a topologically sorted order, ensuring that
|
6
|
+
Tasks are executed in a topologically sorted order, ensuring that tasks are built only after their inputs are available. Reverse execution is also supported, making it easy to clean up intermediate files or revert changes after a build.
|
7
7
|
|
8
8
|
> **🚧 Development Status:** Taski is currently under active development and the API may change.
|
9
9
|
|
10
10
|
> **⚠️ Limitation:** Circular dependencies are **not** supported at this time.
|
11
11
|
|
12
|
+
> **ℹ️ Note:** Taski does **not** infer dependencies from file contents or behavior. Instead, dependencies are implicitly established via references between task definitions.
|
13
|
+
|
12
14
|
### Features
|
13
15
|
|
14
|
-
-
|
15
|
-
-
|
16
|
+
- Define tasks using Ruby classes
|
17
|
+
- Implicit dependencies via reference to other task outputs
|
16
18
|
- Topological execution order
|
17
|
-
- Reverse execution for
|
19
|
+
- Reverse execution for cleanup
|
18
20
|
- Built entirely in Ruby
|
19
21
|
|
20
|
-
|
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
|
22
|
+
### Example
|
35
23
|
|
36
24
|
```ruby
|
37
25
|
class TaskA < Taski::Task
|
38
|
-
|
26
|
+
define :task_a_result, -> { "Task A" }
|
39
27
|
|
40
28
|
def build
|
41
|
-
|
29
|
+
puts 'Processing...'
|
42
30
|
end
|
43
31
|
end
|
44
32
|
|
45
33
|
class TaskB < Taski::Task
|
46
|
-
|
34
|
+
define :simple_task, -> { "Task result is #{TaskA.task_a_result}" }
|
47
35
|
|
48
36
|
def build
|
49
37
|
puts simple_task
|
@@ -51,9 +39,24 @@ class TaskB < Taski::Task
|
|
51
39
|
end
|
52
40
|
|
53
41
|
TaskB.build
|
42
|
+
# => Processing...
|
54
43
|
# => Task result is Task A
|
55
44
|
```
|
56
45
|
|
46
|
+
## Installation
|
47
|
+
|
48
|
+
Install the gem and add to the application's Gemfile by executing:
|
49
|
+
|
50
|
+
```bash
|
51
|
+
bundle add taski
|
52
|
+
```
|
53
|
+
|
54
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
55
|
+
|
56
|
+
```bash
|
57
|
+
gem install taski
|
58
|
+
```
|
59
|
+
|
57
60
|
## Development
|
58
61
|
|
59
62
|
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.
|
data/lib/taski/utils.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
module Taski
|
5
|
+
class Utils < FileUtils
|
6
|
+
def rm
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
def rm_f
|
11
|
+
end
|
12
|
+
|
13
|
+
def rm_rf
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def cp
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def cp_r
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def mkdir
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def mkdir_p
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def mktmpdir
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def cmd(command, info = nil, ret = false)
|
38
|
+
puts "exec: #{info}" if info
|
39
|
+
puts command
|
40
|
+
|
41
|
+
if ret
|
42
|
+
ret = `#{command}`.chomp
|
43
|
+
if $?.exited?
|
44
|
+
ret
|
45
|
+
else
|
46
|
+
raise "Failed to execute command: #{command}"
|
47
|
+
end
|
48
|
+
else
|
49
|
+
system command, exception: true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/taski/version.rb
CHANGED
data/lib/taski.rb
CHANGED
@@ -18,138 +18,126 @@ module Taski
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class Task
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
class << self
|
22
|
+
def ref(klass)
|
23
|
+
ref = Reference.new(klass)
|
24
|
+
throw :unresolved, ref
|
25
|
+
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
27
|
+
def define(name, block, **options)
|
28
|
+
@dependencies ||= []
|
29
|
+
@definitions ||= {}
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
32
|
+
def self.#{name}
|
33
|
+
__resolve__[__callee__] ||= false
|
34
|
+
if __resolve__[__callee__]
|
35
|
+
# already resolved
|
36
|
+
else
|
37
|
+
__resolve__[__callee__] = true
|
38
|
+
throw :unresolved, [self, __callee__]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
RUBY
|
33
42
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
else
|
40
|
-
__resolve__[__callee__] = true
|
41
|
-
throw :unresolve, [self, __callee__]
|
43
|
+
classes = []
|
44
|
+
loop do
|
45
|
+
klass, task = catch(:unresolved) do
|
46
|
+
block.call
|
47
|
+
nil
|
42
48
|
end
|
43
|
-
end
|
44
|
-
RUBY
|
45
49
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
if klass.nil?
|
51
|
+
classes.each do |task_class|
|
52
|
+
task_class[:klass].class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
53
|
+
__resolve__ = {}
|
54
|
+
RUBY
|
55
|
+
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
__resolve__ = {}
|
57
|
-
RUBY
|
57
|
+
break
|
58
|
+
else
|
59
|
+
classes << { klass:, task: }
|
58
60
|
end
|
59
|
-
|
60
|
-
break
|
61
|
-
else
|
62
|
-
classes << { klass:, task: }
|
63
61
|
end
|
64
|
-
end
|
65
|
-
|
66
|
-
@dependencies += classes
|
67
|
-
@definitions[name] = {block:, options:, classes:}
|
68
|
-
end
|
69
62
|
|
70
|
-
|
71
|
-
|
72
|
-
task_class.new.build
|
63
|
+
@dependencies += classes
|
64
|
+
@definitions[name] = { block:, options:, classes: }
|
73
65
|
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
66
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
if ret
|
87
|
-
ret = `#{command}`.chomp
|
88
|
-
if $?.exited?
|
89
|
-
ret
|
90
|
-
else
|
91
|
-
raise "Failed to execute command: #{command}"
|
67
|
+
def build
|
68
|
+
resolve_dependencies.reverse.each do |task_class|
|
69
|
+
task_class.new.build
|
92
70
|
end
|
93
|
-
else
|
94
|
-
system command, exception: true
|
95
71
|
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.refresh
|
99
|
-
# TODO
|
100
|
-
end
|
101
|
-
|
102
|
-
private
|
103
72
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
task_class = task[:klass].deref
|
73
|
+
def clean
|
74
|
+
resolve_dependencies.each do |task_class|
|
75
|
+
task_class.new.clean
|
108
76
|
end
|
77
|
+
end
|
109
78
|
|
110
|
-
|
111
|
-
|
112
|
-
resolved.delete(task[:klass])
|
113
|
-
end
|
114
|
-
queue << task[:klass]
|
79
|
+
def refresh
|
80
|
+
# TODO
|
115
81
|
end
|
116
82
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
83
|
+
def resolve(queue, resolved)
|
84
|
+
@dependencies.each do |task|
|
85
|
+
if task[:klass].is_a?(Reference)
|
86
|
+
task[:klass].deref
|
87
|
+
else
|
88
|
+
task[:klass]
|
89
|
+
end => task_class
|
90
|
+
|
91
|
+
# increase priority
|
92
|
+
if resolved.include?(task_class)
|
93
|
+
resolved.delete(task_class)
|
94
|
+
end
|
95
|
+
queue << task_class
|
121
96
|
end
|
122
|
-
RUBY
|
123
97
|
|
124
|
-
@definitions.each do |name, (block, options)|
|
125
98
|
# override
|
126
|
-
|
127
|
-
def self
|
128
|
-
|
99
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
100
|
+
def self.ref(klass)
|
101
|
+
Object.const_get(klass)
|
129
102
|
end
|
130
103
|
RUBY
|
131
104
|
|
132
|
-
|
133
|
-
|
134
|
-
|
105
|
+
@definitions.each do |name, (_block, _options)|
|
106
|
+
# override
|
107
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
108
|
+
def self.#{name}
|
109
|
+
@__#{name} ||= @definitions[:#{name}][:block].call
|
110
|
+
end
|
111
|
+
RUBY
|
112
|
+
|
113
|
+
define_method(name) do
|
114
|
+
unless instance_variable_defined?("@__#{name}")
|
115
|
+
instance_variable_set("@__#{name}", self.class.send(name))
|
116
|
+
end
|
117
|
+
instance_variable_get("@__#{name}")
|
135
118
|
end
|
136
|
-
instance_variable_get("@__#{name}")
|
137
119
|
end
|
138
|
-
end
|
139
120
|
|
140
|
-
|
141
|
-
|
121
|
+
self
|
122
|
+
end
|
142
123
|
|
143
|
-
|
144
|
-
queue = [self]
|
145
|
-
resolved = []
|
124
|
+
private
|
146
125
|
|
147
|
-
|
148
|
-
|
149
|
-
task_class.resolve(queue, resolved)
|
126
|
+
def __resolve__
|
127
|
+
@__resolve__ ||= {}
|
150
128
|
end
|
151
129
|
|
152
|
-
|
130
|
+
def resolve_dependencies
|
131
|
+
queue = [self]
|
132
|
+
resolved = []
|
133
|
+
|
134
|
+
while queue.any?
|
135
|
+
resolved << task_class = queue.shift
|
136
|
+
task_class.resolve(queue, resolved)
|
137
|
+
end
|
138
|
+
|
139
|
+
resolved
|
140
|
+
end
|
153
141
|
end
|
154
142
|
end
|
155
143
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: taski
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ahogappa
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 2025-05-18 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: Taski is a Ruby-based task runner currently under development. It allows
|
13
13
|
you to define small, composable tasks along with the outputs they depend on. Taski
|
@@ -21,9 +21,11 @@ executables: []
|
|
21
21
|
extensions: []
|
22
22
|
extra_rdoc_files: []
|
23
23
|
files:
|
24
|
+
- LICENSE
|
24
25
|
- README.md
|
25
26
|
- Rakefile
|
26
27
|
- lib/taski.rb
|
28
|
+
- lib/taski/utils.rb
|
27
29
|
- lib/taski/version.rb
|
28
30
|
- sig/taski.rbs
|
29
31
|
homepage: https://github.com/ahogappa/taski
|
@@ -46,7 +48,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
48
|
- !ruby/object:Gem::Version
|
47
49
|
version: '0'
|
48
50
|
requirements: []
|
49
|
-
rubygems_version: 3.6.
|
51
|
+
rubygems_version: 3.6.2
|
50
52
|
specification_version: 4
|
51
53
|
summary: A simple yet powerful Ruby task runner with static dependency resolution
|
52
54
|
(in development).
|