yuuki 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f43903b0871e192e128847cf0e772066dec93cfa0b882afb9ac53bb0dfbf3ca
4
- data.tar.gz: 1114888a7eb4a790792556ab6aa03b9ace4da9a15bd56201f8fdf98cf855df30
3
+ metadata.gz: 95e60a7be44a24d27a3340b1a0bf86df33927e32cd855dd9b99df4faa2bc1685
4
+ data.tar.gz: '055829249f336c32e1f88f73984bbd465c037ad6b6edb5a3e2bce68139b25976'
5
5
  SHA512:
6
- metadata.gz: 742ddbe4914aa114d780a8a5bdd6285ba04c6ca651fa23d62c995741b155a935c58742240eb51114f43c6ab82347ea1d457a92f40b63ae87324fbf81ae8073a7
7
- data.tar.gz: 6ae20a7613818d726fd3442a7057354355106656a42c45c9f8c8dcf1dd1eb8172905307f35b9559e7e15c4e3c0b4f60cfad74017ccd254edff93dabf8fab485f
6
+ metadata.gz: b44ff4617ffaaf0e18e0654d67113308518b04575bc74ced15b3afa1418c479e5a577fcbac30de3aa9ff32a6bdcf007c4669a742df0da03a5ce684f271936a58
7
+ data.tar.gz: 9decda81bcfc32138478d91015590d0da49734e7e3d246a87bc40101daa7691a883118f8ad09d889b94dd9ce225f286a40635c2f94a6b84d3b1035431c8064e5
data/.gitignore CHANGED
@@ -6,5 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
- /test/
10
9
  Gemfile.lock
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source "https://rubygems.org"
2
4
 
3
5
  # Specify your gem's dependencies in yuuki.gemspec
data/README.md CHANGED
@@ -20,7 +20,28 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- あとで書く。
23
+ ```ruby
24
+ class HogeClass
25
+ add :a
26
+ def a
27
+ puts 'a'
28
+ end
29
+
30
+ add :b
31
+ tag :b, :tag_b
32
+ def b
33
+ puts 'b'
34
+ end
35
+ end
36
+
37
+ yuuki = Yuuki::Caller.new(HogeClass)
38
+ yuuki.run
39
+ # a
40
+ # b
41
+
42
+ yuuki.run_tag(:tag_b)
43
+ # b
44
+ ```
24
45
 
25
46
  ## Contributing
26
47
 
data/Rakefile CHANGED
@@ -1,2 +1,12 @@
1
- require "bundler/gem_tasks"
2
- task :default => :spec
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
+ end
11
+
12
+ task default: %i[test]
data/lib/yuuki/caller.rb CHANGED
@@ -1,135 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
1
4
  require 'yuuki/runner'
2
5
 
3
6
  module Yuuki
4
- class Caller
5
- Runner = Struct.new(:method, :tags, :exclude, :threading, :priority)
7
+ class Caller
8
+ def self.require_dir(_dir, recursive: false)
9
+ Dir.glob(recursive ? "#{require_dir}/**/*.rb" : "#{require_dir}/*.rb"){|file| require file}
10
+ end
6
11
 
7
- def self.require_dir(dir, recursive = false)
8
- Dir.glob(recursive ? "#{require_dir}/**/*.rb" : "#{require_dir}/*.rb"){|file| require file}
9
- end
12
+ def initialize(*instances)
13
+ @instances = Set.new
14
+ @threads = []
15
+ add(*instances)
16
+ end
10
17
 
11
- def initialize(*klass)
12
- @runners = []
13
- @threads = []
14
- add(*klass)
18
+ def add(*instances)
19
+ instances.each do |instance|
20
+ # create instance if class is given
21
+ if instance.is_a?(Class)
22
+ klass = instance
23
+ instance = instance.new
24
+ else
25
+ klass = instance.class
15
26
  end
16
27
 
17
- def add(*klass)
18
- klass.each do |klass|
19
- instance = klass.new
20
- instance.instance_variable_set(:@caller, self)
21
- info = klass.instance_variable_get(:@runner_info) || {}
22
- methods = klass.public_instance_methods(false)
23
- methods |= klass.instance_variable_get(:@runner_adds) || []
24
- methods -= klass.instance_variable_get(:@runner_excepts) || []
25
- @runners += methods.map do |method|
26
- tags = info.dig(method, :tags) || []
27
- exclude = !!info.dig(method, :exclude)
28
- threading = !!info.dig(method, :threading)
29
- priority = info.dig(method, :priority) || 0
30
- Runner.new(instance.method(method), tags, exclude, threading, priority)
31
- end
32
- end
33
- @runners.sort_by!{|e| -e.priority}
34
- end
28
+ # check the klass is extended
29
+ raise Yuuki::Error, 'Runner instance must be extend Yuuki::Runner' unless klass.singleton_class.include?(Yuuki::Runner)
35
30
 
36
- def run(**args)
37
- run_internal(@runners.reject(&:exclude), args)
38
- end
31
+ # add @yuuki to the instance
32
+ instance.instance_variable_set(:@yuuki, self)
39
33
 
40
- def run_all(**args)
41
- run_internal(@runners, args)
42
- end
34
+ # regist
35
+ @instances << instance
36
+ end
37
+ end
43
38
 
44
- def run_class(*klass, **args)
45
- run_internal(@runners.select{|e| !e.exclude && klass.any?{|k| e.method.receiver.instance_of?(k)}}, args)
46
- end
39
+ def runners
40
+ list = @instances.flat_map do |instance|
41
+ methods = instance.class.instance_variable_get(:@yuuki_methods)
42
+ methods.select{|_sig, meta| meta[:enabled]}.map{|sig, meta| [instance.method(sig), meta]}
43
+ end
44
+ list.sort_by{|_method, meta| -(meta[:priority] || 0)}
45
+ end
47
46
 
48
- def run_class_all(*klass, **args)
49
- run_internal(@runners.select{|e| klass.any?{|k| e.method.receiver.instance_of?(k)}}, args)
50
- end
47
+ def run(**args, &block)
48
+ run_internal(runners, args, &block)
49
+ end
51
50
 
52
- def run_tag(*tags, **args)
53
- run_internal(@runners.select{|e| tags.any?{|t| e.tags.any?(t)}}, args)
54
- end
51
+ def run_select(proc_select, **args, &block)
52
+ run_internal(runners.select(&proc_select), args, &block)
53
+ end
55
54
 
56
- def run_select(**args)
57
- run_internal(@runners.select{|e| yield(e)}, args)
58
- end
55
+ def run_tag(*tags, **args, &block)
56
+ run_select(proc{|_method, meta| meta[:tags]&.intersect?(tags)}, **args, &block)
57
+ end
59
58
 
60
- def run_method(klass, method, **args)
61
- runners = klass ? @runners.select{|e| e.method.receiver.class == klass} : @runners
62
- runners.select!{|e| e.method.name == method} if method
63
- run_internal(runners, args)
64
- end
59
+ def run_method(klass, method_sig, **args, &block)
60
+ select_proc = proc do |method, _meta|
61
+ flag_klass = klass ? method.receiver.instance_of?(klass) : true
62
+ flag_method = method_sig ? method.name == method_sig : true
63
+ flag_klass && flag_method
64
+ end
65
+ run_select(select_proc, **args, &block)
66
+ end
65
67
 
66
- def join
67
- @threads.each(&:join)
68
- end
68
+ def join
69
+ @threads.each(&:join)
70
+ @threads.select!(&:alive?)
71
+ end
69
72
 
70
- def alive?
71
- @threads.any?(&:alive?)
72
- end
73
- alias running? alive?
74
-
75
- private
76
-
77
- def run_internal(runners, args)
78
- runners.each do |runner|
79
- if runner.threading
80
- @threads << Thread.new(runner.method, args) do |method, args|
81
- run_method_internal(method, args)
82
- end
83
- else
84
- run_method_internal(runner.method, args)
85
- end
86
- end
87
- @threads.select!(&:alive?)
73
+ def alive?
74
+ @threads.select!(&:alive?)
75
+ !@threads.empty?
76
+ end
77
+ alias running? alive?
78
+
79
+ private
80
+
81
+ def run_internal(runners, args, &block)
82
+ @threads.select!(&:alive?)
83
+ runners.each do |method, meta|
84
+ if meta[:thread]
85
+ thread = Thread.new(method, args, block) do |thread_method, thread_args, thread_block|
86
+ run_method_internal(thread_method, thread_args, &thread_block)
87
+ end
88
+ thread.priority = meta[:priority] || 0
89
+ @threads << thread
90
+ else
91
+ run_method_internal(method, args, &block)
88
92
  end
93
+ end
94
+ end
89
95
 
90
- def run_method_internal(method, args)
91
- params = method.parameters
92
- return method[] if params.size == 0
93
- params_array = []
94
- params_hash = {}
95
- params_block = nil
96
- nonspecified_last_opt = nil
97
- params.each do |type, name|
98
- case type
99
- when :req
100
- raise Yuuki::Error.new("A required argument '#{name}' was not found on running #{method.owner}::#{method.name}") unless args.key?(name)
101
- params_array << args[name]
102
- when :opt
103
- next nonspecified_last_opt = name unless args.key?(name)
104
- raise Yuuki::Error.new("A required argument '#{nonspecified_last_opt}' was not found"\
105
- " on running #{method.owner}::#{method.name}"" with optional argument '#{name}'") if nonspecified_last_opt
106
- params_array << args[name]
107
- when :rest
108
- next unless args.key?(name)
109
- raise Yuuki::Error.new("A required argument '#{nonspecified_last_opt}' not found"\
110
- " on running #{method.owner}::#{method.name}"" with rest argument '#{name}'") if nonspecified_last_opt
111
- if args[name].respond_to?(:to_ary)
112
- params_array += args[name]
113
- else
114
- params_array << args[name]
115
- end
116
- when :keyreq
117
- raise Yuuki::Error.new("A required key argument '#{name}' was not found on running #{method.owner}::#{method.name}") unless args.key?(name)
118
- params_hash[name] = args[name]
119
- when :key
120
- params_hash[name] = args[name] if args.key?(name)
121
- when :keyrest
122
- next unless args.key?(name)
123
- if args[name].respond_to?(:to_hash)
124
- params_hash.merge!(args[name])
125
- else
126
- params_hash[name] = args[name]
127
- end
128
- when :block
129
- params_block = args[name]
130
- end
131
- end
132
- params_hash.empty? ? method[*params_array, &params_block] : method[*params_array, **params_hash, &params_block]
96
+ def run_method_internal(method, args, &block)
97
+ params = method.parameters
98
+ return method[] if params.empty?
99
+ params_array = []
100
+ params_hash = {}
101
+ params_block = nil
102
+ nonspecified_last_opt = nil
103
+ params.each do |type, name|
104
+ case type
105
+ when :req
106
+ raise Yuuki::Error, "A required argument '#{name}' was not found on running #{method.owner}::#{method.name}" unless args.key?(name)
107
+ params_array << args[name]
108
+ when :opt
109
+ # if parameters do not contain the :opt argument, treat it as not specified
110
+ next nonspecified_last_opt = name unless args.key?(name)
111
+ if nonspecified_last_opt
112
+ # if there already exist non-specified :opt arguments, no more specified :opt argument is allowed
113
+ raise Yuuki::Error, "A required argument '#{nonspecified_last_opt}' was not found"\
114
+ " on running #{method.owner}::#{method.name}"" with optional argument '#{name}'"
115
+ end
116
+ params_array << args[name]
117
+ when :rest
118
+ next unless args.key?(name)
119
+ if nonspecified_last_opt
120
+ # if there already exist non-specified :opt arguments, the :rest argument cannot be handled
121
+ raise Yuuki::Error, "A required argument '#{nonspecified_last_opt}' not found"\
122
+ " on running #{method.owner}::#{method.name}"" with rest argument '#{name}'"
123
+ end
124
+ if args[name].respond_to?(:to_ary)
125
+ params_array += args[name]
126
+ else
127
+ params_array << args[name]
128
+ end
129
+ when :keyreq
130
+ raise Yuuki::Error, "A required key argument '#{name}' was not found on running #{method.owner}::#{method.name}" unless args.key?(name)
131
+ params_hash[name] = args[name]
132
+ when :key
133
+ params_hash[name] = args[name] if args.key?(name)
134
+ when :keyrest
135
+ next unless args.key?(name)
136
+ if args[name].respond_to?(:to_hash)
137
+ params_hash.merge!(args[name])
138
+ else
139
+ params_hash[name] = args[name]
140
+ end
141
+ when :block
142
+ params_block = args[name]
133
143
  end
144
+ end
145
+ params_block = block unless params.any?{|type, _| type == :block}
146
+ params_hash.empty? ? method[*params_array, &params_block] : method[*params_array, **params_hash, &params_block]
134
147
  end
148
+ end
135
149
  end
data/lib/yuuki/error.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yuuki
2
- class Error < StandardError
3
- end
4
+ class Error < StandardError
5
+ end
4
6
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yuuki/caller'
4
+ require 'yuuki/runner'
5
+
6
+ module Yuuki
7
+ module Runner
8
+ # set interval to the method
9
+ def periodic(method, interval)
10
+ @yuuki_methods ||= {}
11
+ @yuuki_methods[method] ||= {}
12
+ @yuuki_methods[method][:periodic] = interval
13
+ end
14
+
15
+ # set whether the method run at the first time
16
+ def first_run(method, enabled: true)
17
+ @yuuki_methods ||= {}
18
+ @yuuki_methods[method] ||= {}
19
+ @yuuki_methods[method][:first_run] = enabled
20
+ end
21
+ end
22
+ end
23
+
24
+ module Yuuki
25
+ class PeriodicCaller < Caller
26
+ attr_reader :first_run, :current_time
27
+
28
+ def initialize(*instances)
29
+ super
30
+ @first_run = true
31
+ end
32
+
33
+ def on_error(&block)
34
+ @on_error = block
35
+ end
36
+
37
+ def run(gmtoff = Time.now.gmtoff, **args, &block)
38
+ last_time = nil
39
+ loop do
40
+ @current_time = Time.now.to_f
41
+ begin
42
+ select_proc = proc do |_method, meta|
43
+ next true if @first_run && meta[:first_run]
44
+ next false unless meta[:periodic]
45
+ next false unless last_time
46
+ c = @current_time + gmtoff
47
+ l = last_time + gmtoff
48
+ next true if (l.div(meta[:periodic]) + 1) * meta[:periodic] <= c
49
+ end
50
+ run_select(select_proc, **args, &block)
51
+ rescue
52
+ @on_error ? @on_error[$!] : raise
53
+ end
54
+ @first_run = false
55
+
56
+ last_time = @current_time
57
+ ((@current_time + 1).floor - Time.now.to_f).tap{|e| sleep e if e > 0}
58
+ end
59
+ end
60
+ end
61
+ end
data/lib/yuuki/runner.rb CHANGED
@@ -1,48 +1,51 @@
1
- require 'yuuki/error'
1
+ # frozen_string_literal: true
2
2
 
3
- module Yuuki
4
- class RunnerBase
5
- class << self
6
- private
7
-
8
- def add(*methods)
9
- @runner_adds ||= []
10
- @runner_adds |= methods
11
- end
3
+ require 'set'
12
4
 
13
- def delete(*methods)
14
- @runner_excepts ||= []
15
- @runner_excepts |= methods
16
- end
5
+ module Yuuki
6
+ module Runner
7
+ # add method
8
+ def add(*methods)
9
+ @yuuki_methods ||= {}
10
+ methods.each do |method|
11
+ @yuuki_methods[method] ||= {}
12
+ @yuuki_methods[method][:enabled] = true
13
+ end
14
+ end
17
15
 
18
- def tag(method, *tags)
19
- @runner_info ||= {}
20
- @runner_info[method] ||= {}
21
- @runner_info[method][:tags] ||= []
22
- @runner_info[method][:tags] |= tags
23
- end
16
+ # delete method
17
+ def delete(*methods)
18
+ @yuuki_methods ||= {}
19
+ methods.each do |method|
20
+ @yuuki_methods[method] ||= {}
21
+ @yuuki_methods[method][:enabled] = false
22
+ end
23
+ end
24
24
 
25
- def exclude(*methods)
26
- @runner_info ||= {}
27
- methods.each do |method|
28
- @runner_info[method] ||= {}
29
- @runner_info[method][:exclude] = true
30
- end
31
- end
25
+ # add tags to the method
26
+ def tag(method, *tags)
27
+ @yuuki_methods ||= {}
28
+ @yuuki_methods[method] ||= {}
29
+ @yuuki_methods[method][:tags] ||= Set.new
30
+ @yuuki_methods[method][:tags].merge(tags)
31
+ end
32
32
 
33
- def threading(*methods)
34
- @runner_info ||= {}
35
- methods.each do |method|
36
- @runner_info[method] ||= {}
37
- @runner_info[method][:threading] = true
38
- end
39
- end
33
+ # enable threading to the method
34
+ def thread(*methods, enabled: true)
35
+ @yuuki_methods ||= {}
36
+ methods.each do |method|
37
+ @yuuki_methods[method] ||= {}
38
+ @yuuki_methods[method][:thread] = enabled
39
+ end
40
+ end
40
41
 
41
- def priority(method, priority)
42
- @runner_info ||= {}
43
- @runner_info[method] ||= {}
44
- @runner_info[method][:priority] = priority
45
- end
46
- end
42
+ # set priority to the method
43
+ def priority(*methods, priority)
44
+ @yuuki_methods ||= {}
45
+ methods.each do |method|
46
+ @yuuki_methods[method] ||= {}
47
+ @yuuki_methods[method][:priority] = priority
48
+ end
47
49
  end
50
+ end
48
51
  end
data/lib/yuuki/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Yuuki
2
- VERSION = '0.1.0'
4
+ VERSION = '0.1.1'
3
5
  end
data/lib/yuuki.rb CHANGED
@@ -1,4 +1,6 @@
1
- require 'yuuki/runner'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'yuuki/caller'
3
4
  require 'yuuki/error'
5
+ require 'yuuki/runner'
4
6
  require 'yuuki/version'
data/yuuki.gemspec CHANGED
@@ -1,28 +1,30 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $:.unshift(lib) unless $:.include?(lib)
3
5
  require 'yuuki/version'
4
6
 
5
7
  Gem::Specification.new do |spec|
6
- spec.name = 'yuuki'
7
- spec.version = Yuuki::VERSION
8
- spec.authors = ['Ishotihadus']
9
- spec.email = ['hanachan.pao@gmail.com']
8
+ spec.name = 'yuuki'
9
+ spec.version = Yuuki::VERSION
10
+ spec.authors = ['Ishotihadus']
11
+ spec.email = ['hanachan.pao@gmail.com']
10
12
 
11
- spec.summary = 'A caller / runner framework for Ruby'
12
- spec.description = 'A caller / runner framework for Ruby.'
13
- spec.homepage = 'https://github.com/ishotihadus/yuuki'
14
- spec.license = 'MIT'
13
+ spec.summary = 'A caller / runner framework for Ruby'
14
+ spec.description = 'A caller / runner framework for Ruby.'
15
+ spec.homepage = 'https://github.com/ishotihadus/yuuki'
16
+ spec.license = 'MIT'
15
17
 
16
- # Specify which files should be added to the gem when it is released.
17
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
19
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
- end
21
- spec.bindir = 'exe'
22
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = ['lib']
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
21
+ `git ls-files -z`.split("\x0").reject{|f| f.match(%r{^(test|spec|features)/})}
22
+ end
23
+ spec.bindir = 'exe'
24
+ spec.executables = spec.files.grep(%r{^exe/}){|f| File.basename(f)}
25
+ spec.require_paths = ['lib']
24
26
 
25
- spec.add_development_dependency 'bundler'
26
- spec.add_development_dependency 'rake'
27
- spec.add_development_dependency 'pry'
27
+ spec.add_development_dependency 'bundler'
28
+ spec.add_development_dependency 'pry'
29
+ spec.add_development_dependency 'rake'
28
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yuuki
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ishotihadus
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-03-16 00:00:00.000000000 Z
11
+ date: 2022-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: pry
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -69,6 +69,7 @@ files:
69
69
  - lib/yuuki.rb
70
70
  - lib/yuuki/caller.rb
71
71
  - lib/yuuki/error.rb
72
+ - lib/yuuki/periodic.rb
72
73
  - lib/yuuki/runner.rb
73
74
  - lib/yuuki/version.rb
74
75
  - yuuki.gemspec
@@ -76,7 +77,7 @@ homepage: https://github.com/ishotihadus/yuuki
76
77
  licenses:
77
78
  - MIT
78
79
  metadata: {}
79
- post_install_message:
80
+ post_install_message:
80
81
  rdoc_options: []
81
82
  require_paths:
82
83
  - lib
@@ -91,8 +92,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
92
  - !ruby/object:Gem::Version
92
93
  version: '0'
93
94
  requirements: []
94
- rubygems_version: 3.0.1
95
- signing_key:
95
+ rubygems_version: 3.3.3
96
+ signing_key:
96
97
  specification_version: 4
97
98
  summary: A caller / runner framework for Ruby
98
99
  test_files: []