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 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]
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Taski
4
+ VERSION = "0.1.0"
5
+ end
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
@@ -0,0 +1,4 @@
1
+ module Taski
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []