console_ipc 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: 184ebdc1e89525ad7d1b5572a1fc6283b6ab5a16a19cf4bf35cafcbeb11f0941
4
+ data.tar.gz: ddb7d85809958e6e92ea6f7c8ec1be49e33c0a0d16452469a05c73410f50b622
5
+ SHA512:
6
+ metadata.gz: f94f1222cbf44a7ec76977b28a9346149282d254b95c4263fc179e1d15fd789a577f73342260aef3aec1b4d9406a0322412831f1bcaa1f30d3ea285f6e2a75c9
7
+ data.tar.gz: 92d396c212ed6ba1b512314c193e405e1784d1b53bd3e165d88aa7c8f5f3cd89bdcffc0763246e606f58bf1acf0e89054a779eb3e4b710f1b4a78ed7e2543043
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Jean-Francis Bastien
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,173 @@
1
+ # Console IPC
2
+
3
+ Fast, stateful `rails runner` alternative over a Unix socket.
4
+
5
+ ## Why use it?
6
+
7
+ `console-ipc` gives you a scriptable Rails console that stays warm.
8
+
9
+ Instead of paying Rails boot cost for every `rails runner` command, start one background console and send Ruby snippets
10
+ to it over a Unix socket. Each snippet runs in the same top-level console binding, so local variables, helper methods,
11
+ loaded records, and exploratory state can survive across commands.
12
+
13
+ ```bash
14
+ bundle exec console-ipc start > log/console-ipc.log 2>&1
15
+ bundle exec console-ipc run 'account = Account.last'
16
+ bundle exec console-ipc run 'account.email'
17
+ bundle exec console-ipc run --json 'account.attributes.slice("id", "email")'
18
+ ```
19
+
20
+ It is built for tight local feedback loops:
21
+
22
+ - Inspect production-shaped Rails data locally without repeating setup code.
23
+ - Keep expensive objects, query results, and helper methods alive between probes.
24
+ - Run quick snippets from shell scripts, editors, terminals, and AI coding agents.
25
+ - Produce fewer output tokens for agent workflows because setup, helper definitions, and repeated context can stay in
26
+ the warm console instead of being printed again for every command.
27
+ - Ask Ruby and Rails where code lives: `source_location`, `const_source_location`, `ancestors`, `methods`, and related
28
+ reflection calls.
29
+ - Edit source, call `reload`, and keep exploring without restarting the console.
30
+ - Use `--json` when another tool needs structured `stdout`, `stderr`, result, and error data.
31
+
32
+ The result is faster turnaround, less terminal noise, and smaller agent transcripts. Instead of rerunning a large setup
33
+ script, you build context once and reuse it.
34
+
35
+ ## Install
36
+
37
+ Add the gem to a Rails app:
38
+
39
+ ```ruby
40
+ gem "console_ipc", group: :development
41
+ ```
42
+
43
+ Then run:
44
+
45
+ ```bash
46
+ bundle install
47
+ ```
48
+
49
+ ## Quick start
50
+
51
+ Start it from the Rails app root. The command daemonizes by default and returns only after Rails is loaded and the
52
+ socket is listening:
53
+
54
+ ```bash
55
+ bundle exec console-ipc start > log/console-ipc.log 2>&1
56
+ ```
57
+
58
+ Run snippets against the warm console:
59
+
60
+ ```bash
61
+ bundle exec console-ipc run 'emails = Account.last(10).map(&:email)'
62
+ bundle exec console-ipc run 'puts emails'
63
+ bundle exec console-ipc run --json 'Account.count'
64
+ ```
65
+
66
+ State persists:
67
+
68
+ ```bash
69
+ bundle exec console-ipc run 'def explain(record); record.attributes.slice("id", "created_at"); end'
70
+ bundle exec console-ipc run --json 'explain(Account.last)'
71
+ ```
72
+
73
+ Send code through stdin when that is easier than shell quoting:
74
+
75
+ ```bash
76
+ cat <<'RUBY' | bundle exec console-ipc run --json
77
+ account = Account.last
78
+ {
79
+ id: account.id,
80
+ email: account.email,
81
+ changes: account.previous_changes
82
+ }
83
+ RUBY
84
+ ```
85
+
86
+ ## What it can do
87
+
88
+ ### Stateful Rails probing
89
+
90
+ Every `run` command evaluates in the same console context:
91
+
92
+ ```bash
93
+ bundle exec console-ipc run 'scope = Account.where(active: true)'
94
+ bundle exec console-ipc run 'scope.count'
95
+ bundle exec console-ipc run 'sample = scope.order(:created_at).last'
96
+ bundle exec console-ipc run 'sample.class'
97
+ ```
98
+
99
+ That makes repetitive inspection much cheaper than rebuilding the same scope every time.
100
+
101
+ ### Reflection and source discovery
102
+
103
+ Use normal Ruby and Rails reflection from the terminal:
104
+
105
+ ```bash
106
+ bundle exec console-ipc run 'Account.instance_method(:save).source_location'
107
+ bundle exec console-ipc run 'Object.const_source_location("Account")'
108
+ bundle exec console-ipc run 'Account.ancestors'
109
+ bundle exec console-ipc run 'Account.new.public_methods.grep(/email/)'
110
+ ```
111
+
112
+ ### Machine-readable output
113
+
114
+ `--json` prints the raw response object:
115
+
116
+ ```bash
117
+ bundle exec console-ipc run --json 'Account.count'
118
+ ```
119
+
120
+ Successful responses include:
121
+
122
+ - `ok`: whether evaluation succeeded.
123
+ - `stdout`: captured `$stdout`.
124
+ - `stderr`: captured `$stderr`.
125
+ - `result`: `inspect` output for the last expression.
126
+ - `output`: console-style combined output.
127
+
128
+ Error responses include `ok: false`, captured output, and the exception message with a short backtrace.
129
+
130
+ ### Source reloads
131
+
132
+ Use `reload` after source edits. It calls Rails console `reload!` inside the running process:
133
+
134
+ ```bash
135
+ bundle exec console-ipc reload
136
+ ```
137
+
138
+ ### Process lifecycle
139
+
140
+ Use `reset` for a clean process and `stop` when done:
141
+
142
+ ```bash
143
+ bundle exec console-ipc reset
144
+ bundle exec console-ipc stop
145
+ ```
146
+
147
+ Use `--foreground` when debugging the console process directly or when you want Ctrl-C to stop it:
148
+
149
+ ```bash
150
+ bundle exec console-ipc start --foreground
151
+ ```
152
+
153
+ ## Tips
154
+
155
+ Start the console once and reuse `run` for later snippets. Do not stop or restart it between runs unless the socket is
156
+ stale or `run` reports that no console is listening. If that happens, remove the stale socket and start it again:
157
+
158
+ ```bash
159
+ rm -f tmp/console_ipc.sock
160
+ bundle exec console-ipc start > log/console-ipc.log 2>&1
161
+ ```
162
+
163
+ Use `reset` when state gets confusing:
164
+
165
+ ```bash
166
+ bundle exec console-ipc reset
167
+ ```
168
+
169
+ ## Security model
170
+
171
+ `console-ipc` is intended for local development and trusted automation. It evaluates arbitrary Ruby in your Rails app.
172
+ The socket is created with `0600` permissions, but anyone who can access the socket as your user can run code in that
173
+ Rails process. Do not expose the socket over the network.
data/exe/console-ipc ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'console_ipc/cli'
5
+
6
+ exit ConsoleIpc::CLI.new(ARGV).call
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'io/wait'
5
+ require 'optparse'
6
+
7
+ require_relative 'connection'
8
+
9
+ module ConsoleIpc
10
+ class CLI
11
+ COMMANDS = %w[reload reset run start stop].freeze
12
+ START_WAIT_TIMEOUT_SECONDS = 120
13
+
14
+ def initialize(argv, output: $stdout, error: $stderr)
15
+ @argv = argv.dup
16
+ @output = output
17
+ @error = error
18
+ @socket_path = Connection.socket_path
19
+ end
20
+
21
+ def call
22
+ command = @argv.shift
23
+
24
+ return usage(1) unless COMMANDS.include?(command)
25
+
26
+ call_command(command)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :argv, :output, :error, :socket_path
32
+
33
+ def call_command(command)
34
+ case command
35
+ when 'reload'
36
+ reload
37
+ when 'reset'
38
+ reset
39
+ when 'run'
40
+ run_code
41
+ when 'start'
42
+ start
43
+ when 'stop'
44
+ stop
45
+ end
46
+ end
47
+
48
+ def start
49
+ foreground = parse_start_options
50
+ return start_server if foreground
51
+
52
+ start_daemon
53
+ end
54
+
55
+ def start_server(&ready_callback)
56
+ load_rails_console
57
+ Server.new(context: TOPLEVEL_BINDING, socket_path: socket_path).start(&ready_callback)
58
+ 0
59
+ end
60
+
61
+ def start_daemon
62
+ read_pipe, write_pipe = IO.pipe
63
+
64
+ pid = fork do
65
+ read_pipe.close
66
+ detach_from_shell
67
+
68
+ begin
69
+ start_server { signal_ready(write_pipe) }
70
+ rescue StandardError => e
71
+ signal_start_error(write_pipe, e)
72
+ raise
73
+ ensure
74
+ write_pipe.close unless write_pipe.closed?
75
+ end
76
+ end
77
+
78
+ write_pipe.close
79
+ Process.detach(pid)
80
+ wait_until_daemon_ready(read_pipe)
81
+ end
82
+
83
+ def run_code
84
+ json = parse_run_options
85
+ code = read_code
86
+
87
+ abort_with('console-ipc run requires Ruby code as an argument or stdin') if code.empty?
88
+
89
+ response = Connection.execute(code, socket_path: socket_path)
90
+ write_response(response, json: json)
91
+ rescue ConnectionError => e
92
+ error.puts e.message
93
+ 1
94
+ end
95
+
96
+ def parse_run_options
97
+ json = false
98
+
99
+ OptionParser.new do |opts|
100
+ opts.banner = 'Usage: console-ipc run [options] RUBY_CODE'
101
+
102
+ opts.on('--json', 'Print raw JSON response') do
103
+ json = true
104
+ end
105
+
106
+ opts.on('--socket PATH', 'Unix socket path') do |path|
107
+ @socket_path = File.expand_path(path)
108
+ end
109
+ end.parse!(argv)
110
+
111
+ json
112
+ end
113
+
114
+ def read_code
115
+ code = argv.join(' ')
116
+ code = $stdin.read if code.empty? && !$stdin.tty?
117
+ code
118
+ end
119
+
120
+ def stop
121
+ parse_socket_option('Usage: console-ipc stop [--socket PATH]')
122
+
123
+ response = Connection.stop(socket_path: socket_path)
124
+ output.print response.fetch('output')
125
+ Connection.wait_until_stopped(socket_path: socket_path)
126
+ response.fetch('ok') ? 0 : 1
127
+ rescue ConnectionError => e
128
+ error.puts e.message
129
+ 1
130
+ end
131
+
132
+ def reset
133
+ parse_socket_option('Usage: console-ipc reset [--socket PATH]')
134
+ stop_existing_console
135
+ load_rails_console
136
+ Server.new(context: TOPLEVEL_BINDING, socket_path: socket_path).start
137
+ 0
138
+ end
139
+
140
+ def reload
141
+ parse_socket_option('Usage: console-ipc reload [--socket PATH]')
142
+
143
+ response = Connection.reload(socket_path: socket_path)
144
+ output.print response.fetch('output')
145
+ response.fetch('ok') ? 0 : 1
146
+ rescue ConnectionError => e
147
+ error.puts e.message
148
+ 1
149
+ end
150
+
151
+ def parse_socket_option(banner)
152
+ OptionParser.new do |opts|
153
+ opts.banner = banner
154
+
155
+ opts.on('--socket PATH', 'Unix socket path') do |path|
156
+ @socket_path = File.expand_path(path)
157
+ end
158
+ end.parse!(argv)
159
+ end
160
+
161
+ def parse_start_options
162
+ foreground = false
163
+
164
+ OptionParser.new do |opts|
165
+ opts.banner = 'Usage: console-ipc start [--foreground] [--socket PATH]'
166
+
167
+ opts.on('--foreground', 'Keep the console process attached to the current shell') do
168
+ foreground = true
169
+ end
170
+
171
+ opts.on('--socket PATH', 'Unix socket path') do |path|
172
+ @socket_path = File.expand_path(path)
173
+ end
174
+ end.parse!(argv)
175
+
176
+ foreground
177
+ end
178
+
179
+ def stop_existing_console
180
+ response = Connection.stop(socket_path: socket_path)
181
+ output.print response.fetch('output')
182
+ Connection.wait_until_stopped(socket_path: socket_path)
183
+ rescue ConnectionError
184
+ nil
185
+ end
186
+
187
+ def detach_from_shell
188
+ Process.setsid
189
+ trap('HUP', 'IGNORE') if Signal.list.key?('HUP')
190
+ rescue Errno::EPERM
191
+ nil
192
+ end
193
+
194
+ def signal_ready(write_pipe)
195
+ write_pipe.puts('ready')
196
+ write_pipe.close
197
+ rescue IOError, SystemCallError
198
+ nil
199
+ end
200
+
201
+ def signal_start_error(write_pipe, exception)
202
+ write_pipe.puts("error: #{exception.class}: #{exception.message}") unless write_pipe.closed?
203
+ rescue IOError, SystemCallError
204
+ nil
205
+ end
206
+
207
+ def wait_until_daemon_ready(read_pipe)
208
+ ready = read_pipe.wait_readable(START_WAIT_TIMEOUT_SECONDS)
209
+
210
+ return daemon_start_timeout(read_pipe) unless ready
211
+
212
+ status = read_pipe.gets
213
+ read_pipe.close
214
+
215
+ case status&.chomp
216
+ when 'ready'
217
+ 0
218
+ when /^error: /
219
+ error.puts status
220
+ 1
221
+ else
222
+ error.puts 'console-ipc daemon exited before it was ready'
223
+ 1
224
+ end
225
+ end
226
+
227
+ def daemon_start_timeout(read_pipe)
228
+ read_pipe.close
229
+ error.puts "Timed out waiting for console-ipc to start on #{socket_path}"
230
+ 1
231
+ end
232
+
233
+ def load_rails_console
234
+ require_relative 'server'
235
+ require File.expand_path('config/environment', Connection.root_path)
236
+ Rails.application.load_console
237
+ TOPLEVEL_BINDING.receiver.extend(Rails::ConsoleMethods)
238
+ end
239
+
240
+ def write_response(response, json:)
241
+ if json
242
+ output.puts JSON.generate(response)
243
+ else
244
+ output.print response.fetch('output')
245
+ end
246
+
247
+ response.fetch('ok') ? 0 : 1
248
+ end
249
+
250
+ def usage(exit_code)
251
+ error.puts <<~USAGE
252
+ Usage: console-ipc COMMAND [options]
253
+
254
+ Commands:
255
+ start Start a Rails console server
256
+ run Run Ruby code in the running console
257
+ stop Stop the running console
258
+ reset Stop the running console, then start a new one
259
+ reload Call reload! in the running console
260
+
261
+ Start options:
262
+ --foreground Keep the console process attached to the current shell
263
+ USAGE
264
+ exit_code
265
+ end
266
+
267
+ def abort_with(message)
268
+ error.puts message
269
+ 1
270
+ end
271
+ end
272
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'socket'
5
+
6
+ module ConsoleIpc
7
+ class ConnectionError < StandardError; end
8
+
9
+ class Connection
10
+ DEFAULT_SOCKET_PATH = 'tmp/console_ipc.sock'
11
+ STOP_WAIT_TIMEOUT_SECONDS = 5
12
+
13
+ def self.socket_path
14
+ ENV.fetch('CONSOLE_IPC_SOCKET') do
15
+ File.expand_path(DEFAULT_SOCKET_PATH, root_path)
16
+ end
17
+ end
18
+
19
+ def self.root_path
20
+ bundle_gemfile = ENV.fetch('BUNDLE_GEMFILE', nil)
21
+
22
+ return File.dirname(File.expand_path(bundle_gemfile)) if bundle_gemfile
23
+
24
+ Dir.pwd
25
+ end
26
+
27
+ def self.execute(code, socket_path: self.socket_path)
28
+ request({ action: 'execute', code: code }, socket_path: socket_path)
29
+ end
30
+
31
+ def self.reload(socket_path: self.socket_path)
32
+ request({ action: 'reload' }, socket_path: socket_path)
33
+ end
34
+
35
+ def self.stop(socket_path: self.socket_path)
36
+ request({ action: 'stop' }, socket_path: socket_path)
37
+ end
38
+
39
+ def self.wait_until_stopped(socket_path: self.socket_path, timeout: STOP_WAIT_TIMEOUT_SECONDS)
40
+ deadline = monotonic_time + timeout
41
+
42
+ while listening?(socket_path)
43
+ raise ConnectionError, "Timed out waiting for background console to stop on #{socket_path}" if monotonic_time >= deadline
44
+
45
+ sleep 0.05
46
+ end
47
+ end
48
+
49
+ def self.monotonic_time
50
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
51
+ end
52
+
53
+ def self.listening?(socket_path = self.socket_path)
54
+ UNIXSocket.open(socket_path).close
55
+ true
56
+ rescue Errno::ENOENT, Errno::ECONNREFUSED
57
+ false
58
+ end
59
+
60
+ def self.request(payload, socket_path: self.socket_path)
61
+ UNIXSocket.open(socket_path) do |socket|
62
+ socket.puts(JSON.generate(payload))
63
+ JSON.parse(socket.read)
64
+ end
65
+ rescue Errno::ENOENT, Errno::ECONNREFUSED => e
66
+ raise ConnectionError, "No background console is listening on #{socket_path} (#{e.message})"
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'stringio'
4
+
5
+ module ConsoleIpc
6
+ class Evaluator
7
+ ERROR_BACKTRACE_LINES = 10
8
+
9
+ def initialize(context)
10
+ @context = context
11
+ end
12
+
13
+ def call(code)
14
+ stdout = StringIO.new
15
+ stderr = StringIO.new
16
+
17
+ with_output(stdout, stderr) do
18
+ result = @context.eval(code, '(console-ipc)')
19
+ response(stdout: stdout, stderr: stderr, result: result)
20
+ end
21
+ rescue StandardError => e
22
+ error_response(stdout: stdout, stderr: stderr, error: e)
23
+ end
24
+
25
+ private
26
+
27
+ def response(stdout:, stderr:, result:)
28
+ result_output = "=> #{result.inspect}\n"
29
+
30
+ {
31
+ ok: true,
32
+ stdout: stdout.string,
33
+ stderr: stderr.string,
34
+ result: result.inspect,
35
+ output: "#{stdout.string}#{stderr.string}#{result_output}"
36
+ }
37
+ end
38
+
39
+ def error_response(stdout:, stderr:, error:)
40
+ message = error_message(error)
41
+
42
+ {
43
+ ok: false,
44
+ stdout: stdout.string,
45
+ stderr: stderr.string,
46
+ error: message,
47
+ output: "#{stdout.string}#{stderr.string}#{message}\n"
48
+ }
49
+ end
50
+
51
+ def error_message(error)
52
+ backtrace = error.backtrace&.first(ERROR_BACKTRACE_LINES)&.join("\n")
53
+ "#{error.class}: #{error.message}\n#{backtrace}"
54
+ end
55
+
56
+ def with_output(stdout, stderr)
57
+ original_stdout = $stdout
58
+ original_stderr = $stderr
59
+ $stdout = stdout
60
+ $stderr = stderr
61
+ yield
62
+ ensure
63
+ $stdout = original_stdout
64
+ $stderr = original_stderr
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'json'
5
+ require 'socket'
6
+
7
+ require_relative 'connection'
8
+ require_relative 'evaluator'
9
+
10
+ module ConsoleIpc
11
+ class Server
12
+ def initialize(context:, socket_path: Connection.socket_path, output: $stdout)
13
+ @evaluator = Evaluator.new(context)
14
+ @socket_path = socket_path
15
+ @output = output
16
+ end
17
+
18
+ def start
19
+ prepare_socket
20
+ install_signal_handlers
21
+
22
+ UNIXServer.open(socket_path) do |server|
23
+ File.chmod(0o600, socket_path)
24
+ @server = server
25
+ @output.puts "console-ipc listening on #{socket_path}"
26
+ yield if block_given?
27
+ serve(server)
28
+ end
29
+ ensure
30
+ cleanup_socket
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :socket_path
36
+
37
+ def prepare_socket
38
+ FileUtils.mkdir_p(File.dirname(socket_path))
39
+
40
+ return unless File.exist?(socket_path)
41
+
42
+ raise "console-ipc is already listening on #{socket_path}" if Connection.listening?(socket_path)
43
+
44
+ File.delete(socket_path)
45
+ end
46
+
47
+ def install_signal_handlers
48
+ %w[INT TERM].each do |signal|
49
+ trap(signal) do
50
+ @server&.close
51
+ cleanup_socket
52
+ exit
53
+ end
54
+ end
55
+
56
+ trap('HUP', 'IGNORE') if Signal.list.key?('HUP')
57
+ end
58
+
59
+ def serve(server)
60
+ loop do
61
+ handle_client(server.accept)
62
+ rescue IOError
63
+ break
64
+ end
65
+ end
66
+
67
+ def handle_client(client)
68
+ request_body = client.gets
69
+ return if request_body.nil?
70
+
71
+ request = JSON.parse(request_body)
72
+ response = response_for(request)
73
+ write_response(client, response)
74
+ rescue JSON::ParserError, KeyError, TypeError => e
75
+ write_response(client, ok: false, error: e.message, output: "#{e.class}: #{e.message}\n")
76
+ ensure
77
+ client&.close
78
+ @server&.close if @stopping
79
+ end
80
+
81
+ def write_response(client, response)
82
+ client.write(JSON.generate(response))
83
+ rescue IOError, SystemCallError
84
+ nil
85
+ end
86
+
87
+ def response_for(request)
88
+ case request.fetch('action', 'execute')
89
+ when 'execute'
90
+ @evaluator.call(request.fetch('code'))
91
+ when 'reload'
92
+ @evaluator.call('reload!')
93
+ when 'stop'
94
+ @stopping = true
95
+ { ok: true, output: "console-ipc stopped\n" }
96
+ else
97
+ { ok: false, output: "Unknown console-ipc action: #{request.fetch('action')}\n" }
98
+ end
99
+ end
100
+
101
+ def cleanup_socket
102
+ File.delete(socket_path) if File.socket?(socket_path)
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConsoleIpc
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'console_ipc/version'
4
+ require_relative 'console_ipc/cli'
5
+ require_relative 'console_ipc/connection'
6
+ require_relative 'console_ipc/evaluator'
7
+ require_relative 'console_ipc/server'
8
+
9
+ module ConsoleIpc
10
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: console_ipc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jean-Francis Bastien
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Console IPC loads a Rails console once and runs snippets against the
13
+ same process, preserving state between runs while avoiding repeated Rails boot time.
14
+ email:
15
+ - bhacaz@gmail.com
16
+ executables:
17
+ - console-ipc
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - LICENSE.txt
22
+ - README.md
23
+ - exe/console-ipc
24
+ - lib/console_ipc.rb
25
+ - lib/console_ipc/cli.rb
26
+ - lib/console_ipc/connection.rb
27
+ - lib/console_ipc/evaluator.rb
28
+ - lib/console_ipc/server.rb
29
+ - lib/console_ipc/version.rb
30
+ homepage: https://github.com/jfbastien/console_ipc
31
+ licenses:
32
+ - MIT
33
+ metadata:
34
+ allowed_push_host: https://rubygems.org
35
+ source_code_uri: https://github.com/jfbastien/console_ipc
36
+ rubygems_mfa_required: 'true'
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 2.7.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.6.9
52
+ specification_version: 4
53
+ summary: Fast, stateful rails runner alternative over a Unix socket.
54
+ test_files: []