Class: Yuuki::Caller

Inherits:
Object
  • Object
show all
Defined in:
lib/yuuki/caller.rb,
sig/yuuki/caller.rbs

Direct Known Subclasses

PeriodicCaller

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*instances, use_yoshinon: true) ⇒ Caller

Returns a new instance of Caller.

Parameters:

  • instances (Runner, Class)
  • use_yoshinon: (Boolean) (defaults to: true)


13
14
15
16
17
18
# File 'lib/yuuki/caller.rb', line 13

def initialize(*instances, use_yoshinon: true)
  @instances = Set.new
  @threads = []
  add(*instances)
  @use_yoshinon = use_yoshinon
end

Class Method Details

.require_dir(require_dir, recursive: false) ⇒ void

This method returns an undefined value.

Parameters:

  • require_dir (String)
  • recursive: (Boolean) (defaults to: false)


9
10
11
# File 'lib/yuuki/caller.rb', line 9

def self.require_dir(require_dir, recursive: false)
  Dir.glob(recursive ? "#{require_dir}/**/*.rb" : "#{require_dir}/*.rb").each { |file| require file }
end

Instance Method Details

#add(*instances) ⇒ void

This method returns an undefined value.

Parameters:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/yuuki/caller.rb', line 20

def add(*instances)
  instances.each do |instance|
    if instance.is_a?(Class)
      klass = instance
      raise Yuuki::Error, 'Runner instance must be inherit Yuuki::Runner' unless klass < Yuuki::Runner

      # allocate + explicit initialize so that @yuuki is already set when
      # the runner's #initialize runs
      instance = instance.allocate
      instance.instance_variable_set(:@yuuki, self)
      instance.send(:initialize)
    else
      raise Yuuki::Error, 'Runner instance must be inherit Yuuki::Runner' unless instance.is_a?(Yuuki::Runner)

      instance.instance_variable_set(:@yuuki, self)
    end
    @instances << instance
  end
end

#alive?Boolean Also known as: running?

Returns:

  • (Boolean)


67
68
69
70
# File 'lib/yuuki/caller.rb', line 67

def alive?
  @threads.select!(&:alive?)
  !@threads.empty?
end

#joinvoid

This method returns an undefined value.



62
63
64
65
# File 'lib/yuuki/caller.rb', line 62

def join
  @threads.each(&:join)
  @threads.select!(&:alive?)
end

#methodsArray[runner_entry]

NOTE: intentionally shadows Object#methods; returns [Method, meta] pairs sorted by priority (highest first)

Returns:

  • (Array[runner_entry])


42
43
44
45
46
47
48
49
# File 'lib/yuuki/caller.rb', line 42

def methods
  list = @instances.flat_map do |instance|
    methods = instance.class.yuuki_methods
    methods.map { |sig, meta| [instance.method(sig), meta] }
  end
  list.sort_by! { |_method, meta| -(meta[:priority] || 0) }
  list
end

#run(*tags, **args, &block) ⇒ void

This method returns an undefined value.

Parameters:

  • tags (Symbol)
  • args (Object)


51
52
53
54
55
56
# File 'lib/yuuki/caller.rb', line 51

def run(*tags, **args, &block)
  selector = proc do |_method, meta|
    meta[:tags] && !(meta[:tags] & tags).empty?
  end
  run_select(selector, **args, &block)
end

#run_internal(runners, args, &block) ⇒ void

This method returns an undefined value.

Parameters:

  • runners (Array[runner_entry])
  • args (Hash[Symbol, untyped])


75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/yuuki/caller.rb', line 75

def run_internal(runners, args, &block)
  @threads.select!(&:alive?)
  runners.each do |method, meta|
    if meta[:thread]
      thread = Thread.new(method, args, block) do |thread_method, thread_args, thread_block|
        run_method_internal(thread_method, thread_args, &thread_block)
      end
      thread.priority = meta[:priority] || 0
      @threads << thread
    else
      run_method_internal(method, args, &block)
    end
  end
end

#run_method_internal(method, args, &block) ⇒ void

This method returns an undefined value.

Maps the args hash onto the method's parameters by name. Positional optionals can only be filled left-to-right: once one is unspecified, passing any later :opt/:rest argument is an error (nonspecified_last_opt).

Parameters:

  • method (Method)
  • args (Hash[Symbol, untyped])


93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/yuuki/caller.rb', line 93

def run_method_internal(method, args, &block)
  yoshinon = Yoshinon.lock if @use_yoshinon
  params = method.parameters
  return method[&block] if params.empty?

  params_array = []
  params_hash = {}
  params_block = nil
  nonspecified_last_opt = nil
  params.each do |type, name|
    case type
    when :req
      unless args.key?(name)
        raise Yuuki::Error,
              "A required argument '#{name}' was not found on running #{method.owner}::#{method.name}"
      end

      params_array << args[name]
    when :opt
      # if parameters do not contain the :opt argument, treat it as not specified
      next nonspecified_last_opt = name unless args.key?(name)

      if nonspecified_last_opt
        # if there already exist non-specified :opt arguments, no more specified :opt argument is allowed
        raise Yuuki::Error, "A required argument '#{nonspecified_last_opt}' was not found " \
                            "on running #{method.owner}::#{method.name}" + " with optional argument '#{name}'"
      end
      params_array << args[name]
    when :rest
      next unless args.key?(name)

      if nonspecified_last_opt
        # if there already exist non-specified :opt arguments, the :rest argument cannot be handled
        raise Yuuki::Error, "A required argument '#{nonspecified_last_opt}' not found " \
                            "on running #{method.owner}::#{method.name}" + " with rest argument '#{name}'"
      end
      if args[name].respond_to?(:to_ary)
        params_array += args[name]
      else
        params_array << args[name]
      end
    when :keyreq
      unless args.key?(name)
        raise Yuuki::Error,
              "A required key argument '#{name}' was not found on running #{method.owner}::#{method.name}"
      end

      params_hash[name] = args[name]
    when :key
      params_hash[name] = args[name] if args.key?(name)
    when :keyrest
      next unless args.key?(name)

      if args[name].respond_to?(:to_hash)
        params_hash.merge!(args[name])
      else
        params_hash[name] = args[name]
      end
    when :block
      params_block = args[name] if args.key?(name)
    end
  end
  # fall back to the block passed to run unless one was explicitly given via args
  params_block = block unless params.any? { |type, name| type == :block && args.key?(name) }
  params_hash.empty? ? method[*params_array, &params_block] : method[*params_array, **params_hash, &params_block]
ensure
  yoshinon&.unlock
end

#run_select(selector, **args, &block) ⇒ void

This method returns an undefined value.

Parameters:

  • selector (selector)
  • args (Object)


58
59
60
# File 'lib/yuuki/caller.rb', line 58

def run_select(selector, **args, &block)
  run_internal(methods.select(&selector), args, &block)
end