io_daemonizer 3 → 6

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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/io_daemonizer.rb +79 -16
  3. metadata +5 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2fe90c198a168ddb60ba8664074e8ba81eb4e08fbfac69e86af72edea74e937f
4
- data.tar.gz: a386e4e1e76bbf154944d579d9220a35bfe7123d4725ef20bc22a2c3950a3fd1
3
+ metadata.gz: 9221b86c37a20ab3d79c7fb1fee60e9a7c74aa083d3aff30da43e755c9a02256
4
+ data.tar.gz: f0b41de14e06824a163ded73f2b70d45bc54c1c086ee8c2419b83af4d1ab3c58
5
5
  SHA512:
6
- metadata.gz: 2804cf8d50674070fe9bd9c6c8fb951bd6868acd7ee01a3862963b6e22f122ad09ea56113e892e93185a0d2caf58a30dcf24b570bcfbce86db62cc35f0a389d9
7
- data.tar.gz: 291594bc93f1a5b269c8a668815ff3bc9f1ebc18cb01c164d014307701e1d72224e89b950b5ef1a4b02b7cdc655a22770ad90fda506b140357be90403d289df7
6
+ metadata.gz: cbcfd15d8a147dc074f4003e7d1a6f2ee17979644220abb54044b740c7023f0f51bab528c395b38b053efb685abac807c4648387b9aa5c1e3cad0d0e45f0ef5f
7
+ data.tar.gz: 250baadd6ca2b943920408f431a66b5920f1655cfd44b4026f7c08409b23dcf9ad19972e2aae7f900e09268d4f9c472c33e80455a62ddf982e78751da9433f44
data/io_daemonizer.rb CHANGED
@@ -1,33 +1,44 @@
1
- # io_daemonizer v.3 https://github.com/joeyschoblaska/io_daemonizer
1
+ # io_daemonizer v.6 https://github.com/joeyschoblaska/io_daemonizer
2
2
 
3
+ require "json"
3
4
  require "shellwords"
4
5
  require "socket"
5
6
  require "stringio"
6
7
 
7
8
  class IODaemonizer
8
- PORT = ENV["IO_DAEMONIZER_PORT"] || 5289
9
-
10
- def self.wrap(setup: -> {}, run: -> {})
9
+ def self.wrap(port:, setup:, run:, autostart: true)
11
10
  case ARGV[0]
12
11
  when "start"
13
12
  puts "starting server..."
14
- Daemon.run(setup: setup, run: run)
13
+ Daemon.run(port: port, setup: setup, run: run)
15
14
  when "stop"
16
15
  puts "stopping server..."
17
- send_request(ARGV)
16
+ send_request(port: port, args: ARGV)
18
17
  else
19
- send_request(ARGV)
18
+ begin
19
+ send_request(port: port, args: ARGV)
20
+ rescue Errno::ECONNREFUSED => e
21
+ raise(e) unless autostart
22
+ daemon = Daemon.new(port: port, setup: setup, run: run)
23
+ daemon.setup
24
+ fork { daemon.start }
25
+ sleep 0.1
26
+ send_request(port: port, args: ARGV)
27
+ end
20
28
  end
21
29
  rescue Errno::ECONNREFUSED
22
30
  puts "server not running or not responding"
23
31
  end
24
32
 
25
- def self.send_request(args)
26
- TCPSocket.open("127.0.0.1", PORT) do |socket|
33
+ def self.send_request(port:, args:)
34
+ consumer = LabeledIOConsumer.new
35
+
36
+ TCPSocket.open("127.0.0.1", port) do |socket|
27
37
  socket.puts args.shelljoin
28
38
  socket.write $stdin.tty? ? "" : $stdin.read
29
39
  socket.close_write
30
- STDOUT.write(socket.read(4096)) until socket.eof?
40
+
41
+ consumer.write(socket.read) until socket.eof?
31
42
  end
32
43
  end
33
44
 
@@ -41,13 +52,14 @@ class IODaemonizer
41
52
  end
42
53
 
43
54
  class Daemon
44
- def self.run(setup: -> {}, run: -> {})
45
- daemon = new(setup: setup, run: run)
55
+ def self.run(port:, setup:, run:)
56
+ daemon = new(port: port, setup: setup, run: run)
46
57
  daemon.setup
47
58
  daemon.start
48
59
  end
49
60
 
50
- def initialize(setup: -> {}, run: -> {})
61
+ def initialize(port:, setup:, run:)
62
+ @port = port
51
63
  @setup = setup
52
64
  @run = run
53
65
  @context = Object.new
@@ -58,7 +70,7 @@ class IODaemonizer
58
70
  end
59
71
 
60
72
  def start
61
- @server = TCPServer.open("127.0.0.1", PORT)
73
+ @server = TCPServer.open("127.0.0.1", @port)
62
74
  Process.daemon(true)
63
75
  read_socket(@server.accept) until @server.closed?
64
76
  end
@@ -74,14 +86,65 @@ class IODaemonizer
74
86
  else
75
87
  IODaemonizer.redirect(
76
88
  stdin: StringIO.new(body.join),
77
- stdout: socket,
78
- stderr: socket
89
+ stdout: IOLabeler.new(1, socket, "stdout"),
90
+ stderr: IOLabeler.new(2, socket, "stderr")
79
91
  ) { @context.instance_exec args, &@run }
80
92
  end
81
93
  rescue => e
82
94
  socket.write e.inspect
95
+ raise e
83
96
  ensure
97
+ socket.close_write
84
98
  socket.close
85
99
  end
86
100
  end
101
+
102
+ class IOLabeler < IO
103
+ attr_reader :label
104
+
105
+ def initialize(fd, socket, label)
106
+ super(fd)
107
+ @socket = socket
108
+ @label = label
109
+ end
110
+
111
+ def write(chunk)
112
+ @socket.write({ @label => chunk }.to_json)
113
+ end
114
+
115
+ def reopen(io)
116
+ @label = io&.label || @label
117
+ end
118
+ end
119
+
120
+ class LabeledIOConsumer
121
+ def initialize
122
+ @buffer = ""
123
+ end
124
+
125
+ def write(chunk)
126
+ chunk.chars.each do |ch|
127
+ @buffer << ch
128
+ process_buffer
129
+ end
130
+ end
131
+
132
+ private
133
+
134
+ def process_buffer
135
+ parsed = JSON.parse(@buffer)
136
+ key = parsed.keys[0]
137
+ value = parsed.values[0]
138
+
139
+ case key
140
+ when "stdout"
141
+ $stdout.write(value)
142
+ when "stderr"
143
+ $stderr.write(value)
144
+ end
145
+
146
+ @buffer = ""
147
+ rescue JSON::ParserError
148
+ end
149
+ end
87
150
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: io_daemonizer
3
3
  version: !ruby/object:Gem::Version
4
- version: '3'
4
+ version: '6'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joey Schoblaska
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-09-06 00:00:00.000000000 Z
11
+ date: 2022-09-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Wrap a Ruby script that speaks IO in a daemon so that you only pay the
14
- startup overhead once.
13
+ description: Wrap a Ruby script that speaks stdio in a daemon so that you only pay
14
+ the startup overhead once.
15
15
  email: joey.schoblaska@gmail.com
16
16
  executables: []
17
17
  extensions: []
@@ -37,7 +37,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
37
37
  - !ruby/object:Gem::Version
38
38
  version: '0'
39
39
  requirements: []
40
- rubygems_version: 3.3.3
40
+ rubygems_version: 3.2.3
41
41
  signing_key:
42
42
  specification_version: 4
43
43
  summary: IO Daemonizer