File: spawn_options.rb

package info (click to toggle)
ruby-process-executer 4.0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 408 kB
  • sloc: ruby: 873; makefile: 4
file content (144 lines) | stat: -rw-r--r-- 5,676 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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
# frozen_string_literal: true

require_relative 'base'
require_relative 'option_definition'

module ProcessExecuter
  module Options
    # Defines and validates options accepted by `Process.spawn`
    #
    # Allows subclasses to add additional options that are not passed to `Process.spawn`.
    #
    # Provides a method (#spawn_options) to retrieve only those options directly
    # applicable to Process.spawn.
    #
    # @api public
    #
    class SpawnOptions < Base
      # Options that are passed to Process.spawn
      #
      # They are not passed if the value is :not_set
      #
      # @return [Array<OptionDefinition>]
      #
      SPAWN_OPTIONS = [
        OptionDefinition.new(:unsetenv_others, default: :not_set),
        OptionDefinition.new(:pgroup, default: :not_set),
        OptionDefinition.new(:new_pgroup, default: :not_set),
        OptionDefinition.new(:rlimit_resourcename, default: :not_set),
        OptionDefinition.new(:umask, default: :not_set),
        OptionDefinition.new(:close_others, default: :not_set),
        OptionDefinition.new(:chdir, default: :not_set)
      ].freeze

      # Returns the options to be passed to Process.spawn
      #
      # Any options added by subclasses that are not part of the SPAWN_OPTIONS or
      # are not a redirection option will not be included in the returned hash.
      #
      # @example
      #   options = ProcessExecuter::Options::SpawnOptions.new(out: $stdout, chdir: '/tmp')
      #   options.spawn_options # => { out: $stdout, chdir: '/tmp' }
      #
      # @return [Hash]
      #
      def spawn_options
        {}.tap do |spawn_options|
          options_hash.each do |option_key, value|
            spawn_options[option_key] = value if include_spawn_option?(option_key, value)
          end
        end
      end

      # Determine if the given option key indicates a redirection option
      # @param option_key [Symbol, Integer, IO, Array] the option key to be tested
      # @return [Boolean]
      # @api private
      def redirection?(option_key)
        test = ->(key) { %i[in out err].include?(key) || key.is_a?(Integer) || (key.is_a?(IO) && !key.fileno.nil?) }
        test.call(option_key) || (option_key.is_a?(Array) && option_key.all? { |key| test.call(key) })
      end

      # Does option_key indicate a standard redirection such as stdin, stdout, or stderr
      # @param option_key [Symbol, Integer, IO, Array] the option key to be tested
      # @param symbol [:in, :out, :err] the symbol to test for
      # @param fileno [Integer] the file descriptor number to test for
      # @return [Boolean]
      # @api private
      def std_redirection?(option_key, symbol, fileno)
        test = ->(key) { key == symbol || key == fileno || (key.is_a?(IO) && key.fileno == fileno) }
        test.call(option_key) || (option_key.is_a?(Array) && option_key.any? { |key| test.call(key) })
      end

      # Determine if the given option key indicates a redirection option for stdout
      # @param option_key [Symbol, Integer, IO, Array] the option key to be tested
      # @return [Boolean]
      # @api private
      def stdout_redirection?(option_key) = std_redirection?(option_key, :out, 1)

      # Determine the option key that indicates a redirection option for stdout
      # @return [Symbol, Integer, IO, Array, nil] nil if not found
      # @api private
      def stdout_redirection_source
        options_hash.keys.find { |option_key| option_key if stdout_redirection?(option_key) }
      end

      # Return the redirection destination for stdout
      # @return [Symbol, Integer, IO, Array, nil] nil if stdout is not redirected
      # @api private
      def stdout_redirection_destination
        (key = stdout_redirection_source) ? options_hash[key] : nil
      end

      # Determine if the given option key indicates a redirection option for stderr
      # @param option_key [Symbol, Integer, IO, Array] the option key to be tested
      # @return [Boolean]
      # @api private
      def stderr_redirection?(option_key) = std_redirection?(option_key, :err, 2)

      # Determine the option key that indicates a redirection option for stderr
      # @return [Symbol, Integer, IO, Array, nil] nil if not found
      # @api private
      def stderr_redirection_source
        options_hash.keys.find { |option_key| option_key if stderr_redirection?(option_key) }
      end

      # Determine redirection destination for stderr if it exists
      # @return [Symbol, Integer, IO, Array, nil] nil if stderr is not redirected
      # @api private
      def stderr_redirection_destination
        (key = stderr_redirection_source) ? options_hash[key] : nil
      end

      private

      # Define the allowed options
      #
      # @return [Array<OptionDefinition>]
      #
      # @api private
      def define_options
        [*super, *SPAWN_OPTIONS].freeze
      end

      # Determine if the given option should be passed to `Process.spawn`
      # @param option_key [Object] the option to be tested
      # @param value [Object] the value of the option
      # @return [Boolean] true if the given option should be passed to `Process.spawn`
      # @api private
      def include_spawn_option?(option_key, value)
        return false if value == :not_set

        redirection?(option_key) || SPAWN_OPTIONS.any? { |o| o.name == option_key }
      end

      # Spawn allows IO object and integers as options
      # @param option_key [Object] the option to be tested
      # @return [Boolean] true if the given option is a valid option
      # @api private
      def valid_option?(option_key)
        super || redirection?(option_key)
      end
    end
  end
end