consolle 0.2.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.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +52 -0
- data/.gitignore +6 -0
- data/.mise.toml +9 -0
- data/.rspec +4 -0
- data/.version +1 -0
- data/CHANGELOG.md +29 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +39 -0
- data/LICENSE +9 -0
- data/README.md +190 -0
- data/bin/cone +6 -0
- data/bin/consolle +6 -0
- data/consolle.gemspec +40 -0
- data/lib/consolle/adapters/rails_console.rb +295 -0
- data/lib/consolle/cli.rb +718 -0
- data/lib/consolle/server/console_socket_server.rb +201 -0
- data/lib/consolle/server/console_supervisor.rb +556 -0
- data/lib/consolle/server/request_broker.rb +247 -0
- data/lib/consolle/version.rb +5 -0
- data/lib/consolle.rb +13 -0
- data/mise/release.sh +120 -0
- data/rule.ko.md +117 -0
- data/rule.md +117 -0
- metadata +115 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thread"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "logger"
|
|
6
|
+
|
|
7
|
+
module Consolle
|
|
8
|
+
module Server
|
|
9
|
+
class RequestBroker
|
|
10
|
+
attr_reader :supervisor, :logger
|
|
11
|
+
|
|
12
|
+
def initialize(supervisor:, logger: nil)
|
|
13
|
+
@supervisor = supervisor
|
|
14
|
+
@logger = logger || Logger.new(STDOUT)
|
|
15
|
+
@queue = Queue.new
|
|
16
|
+
@running = false
|
|
17
|
+
@worker_thread = nil
|
|
18
|
+
@request_map = {}
|
|
19
|
+
@mutex = Mutex.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def start
|
|
23
|
+
return if @running
|
|
24
|
+
|
|
25
|
+
@running = true
|
|
26
|
+
@worker_thread = start_worker
|
|
27
|
+
logger.info "[RequestBroker] Started"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def stop
|
|
31
|
+
return unless @running
|
|
32
|
+
|
|
33
|
+
@running = false
|
|
34
|
+
|
|
35
|
+
# Push poison pill to wake up worker
|
|
36
|
+
@queue.push(nil)
|
|
37
|
+
|
|
38
|
+
# Wait for worker to finish
|
|
39
|
+
@worker_thread&.join(5)
|
|
40
|
+
|
|
41
|
+
logger.info "[RequestBroker] Stopped"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def process_request(request)
|
|
45
|
+
request_id = request["request_id"] || SecureRandom.uuid
|
|
46
|
+
|
|
47
|
+
# Create future for response
|
|
48
|
+
future = RequestFuture.new
|
|
49
|
+
|
|
50
|
+
# Store in map
|
|
51
|
+
@mutex.synchronize do
|
|
52
|
+
@request_map[request_id] = future
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Queue request
|
|
56
|
+
@queue.push({
|
|
57
|
+
id: request_id,
|
|
58
|
+
request: request,
|
|
59
|
+
timestamp: Time.now
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
# Wait for response (with timeout)
|
|
63
|
+
begin
|
|
64
|
+
future.get(timeout: request["timeout"] || 30)
|
|
65
|
+
rescue Timeout::Error
|
|
66
|
+
{
|
|
67
|
+
"success" => false,
|
|
68
|
+
"error" => "RequestTimeout",
|
|
69
|
+
"message" => "Request timed out",
|
|
70
|
+
"request_id" => request_id
|
|
71
|
+
}
|
|
72
|
+
ensure
|
|
73
|
+
# Clean up
|
|
74
|
+
@mutex.synchronize do
|
|
75
|
+
@request_map.delete(request_id)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def start_worker
|
|
83
|
+
Thread.new do
|
|
84
|
+
logger.info "[RequestBroker] Worker thread started"
|
|
85
|
+
|
|
86
|
+
while @running
|
|
87
|
+
begin
|
|
88
|
+
# Get next request
|
|
89
|
+
item = @queue.pop
|
|
90
|
+
|
|
91
|
+
# Check for poison pill
|
|
92
|
+
break if item.nil? && !@running
|
|
93
|
+
next if item.nil?
|
|
94
|
+
|
|
95
|
+
# Process request
|
|
96
|
+
process_item(item)
|
|
97
|
+
rescue StandardError => e
|
|
98
|
+
logger.error "[RequestBroker] Worker error: #{e.message}"
|
|
99
|
+
logger.error e.backtrace.join("\n")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
logger.info "[RequestBroker] Worker thread stopped"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def process_item(item)
|
|
108
|
+
request_id = item[:id]
|
|
109
|
+
request = item[:request]
|
|
110
|
+
|
|
111
|
+
logger.debug "[RequestBroker] Processing request: #{request_id}"
|
|
112
|
+
|
|
113
|
+
# Get future
|
|
114
|
+
future = @mutex.synchronize { @request_map[request_id] }
|
|
115
|
+
return unless future
|
|
116
|
+
|
|
117
|
+
# Process based on request type
|
|
118
|
+
response = case request["action"]
|
|
119
|
+
when "eval", "exec"
|
|
120
|
+
process_eval_request(request)
|
|
121
|
+
when "status"
|
|
122
|
+
process_status_request
|
|
123
|
+
when "restart"
|
|
124
|
+
process_restart_request
|
|
125
|
+
else
|
|
126
|
+
{
|
|
127
|
+
"success" => false,
|
|
128
|
+
"error" => "UnknownAction",
|
|
129
|
+
"message" => "Unknown action: #{request["action"]}"
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Add request_id to response
|
|
134
|
+
response["request_id"] = request_id
|
|
135
|
+
|
|
136
|
+
# Set future result
|
|
137
|
+
future.set(response)
|
|
138
|
+
|
|
139
|
+
logger.debug "[RequestBroker] Request completed: #{request_id}"
|
|
140
|
+
rescue StandardError => e
|
|
141
|
+
logger.error "[RequestBroker] Error processing request #{request_id}: #{e.message}"
|
|
142
|
+
|
|
143
|
+
error_response = {
|
|
144
|
+
"success" => false,
|
|
145
|
+
"error" => e.class.name,
|
|
146
|
+
"message" => e.message,
|
|
147
|
+
"request_id" => request_id
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
future&.set(error_response)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def process_eval_request(request)
|
|
154
|
+
code = request["code"]
|
|
155
|
+
timeout = request["timeout"] || 30
|
|
156
|
+
|
|
157
|
+
unless code
|
|
158
|
+
return {
|
|
159
|
+
"success" => false,
|
|
160
|
+
"error" => "MissingParameter",
|
|
161
|
+
"message" => "Missing required parameter: code"
|
|
162
|
+
}
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Execute through supervisor
|
|
166
|
+
result = @supervisor.eval(code, timeout: timeout)
|
|
167
|
+
|
|
168
|
+
# Format response
|
|
169
|
+
if result[:success]
|
|
170
|
+
{
|
|
171
|
+
"success" => true,
|
|
172
|
+
"result" => result[:output],
|
|
173
|
+
"execution_time" => result[:execution_time]
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
{
|
|
177
|
+
"success" => false,
|
|
178
|
+
"error" => "ExecutionError",
|
|
179
|
+
"message" => result[:output]
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def process_status_request
|
|
185
|
+
{
|
|
186
|
+
"success" => true,
|
|
187
|
+
"running" => @supervisor.running?,
|
|
188
|
+
"pid" => @supervisor.pid,
|
|
189
|
+
"rails_root" => @supervisor.rails_root,
|
|
190
|
+
"rails_env" => @supervisor.rails_env
|
|
191
|
+
}
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def process_restart_request
|
|
195
|
+
begin
|
|
196
|
+
# Restart the Rails console subprocess
|
|
197
|
+
new_pid = @supervisor.restart
|
|
198
|
+
|
|
199
|
+
{
|
|
200
|
+
"success" => true,
|
|
201
|
+
"message" => "Rails console subprocess restarted",
|
|
202
|
+
"pid" => new_pid,
|
|
203
|
+
"rails_root" => @supervisor.rails_root,
|
|
204
|
+
"rails_env" => @supervisor.rails_env
|
|
205
|
+
}
|
|
206
|
+
rescue StandardError => e
|
|
207
|
+
logger.error "[RequestBroker] Restart failed: #{e.message}"
|
|
208
|
+
logger.error e.backtrace.join("\n")
|
|
209
|
+
|
|
210
|
+
{
|
|
211
|
+
"success" => false,
|
|
212
|
+
"error" => "RestartFailed",
|
|
213
|
+
"message" => "Failed to restart Rails console: #{e.message}"
|
|
214
|
+
}
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Simple future implementation
|
|
219
|
+
class RequestFuture
|
|
220
|
+
def initialize
|
|
221
|
+
@mutex = Mutex.new
|
|
222
|
+
@condition = ConditionVariable.new
|
|
223
|
+
@value = nil
|
|
224
|
+
@set = false
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def set(value)
|
|
228
|
+
@mutex.synchronize do
|
|
229
|
+
@value = value
|
|
230
|
+
@set = true
|
|
231
|
+
@condition.signal
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def get(timeout: nil)
|
|
236
|
+
@mutex.synchronize do
|
|
237
|
+
unless @set
|
|
238
|
+
@condition.wait(@mutex, timeout)
|
|
239
|
+
raise Timeout::Error, "Future timed out" unless @set
|
|
240
|
+
end
|
|
241
|
+
@value
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
data/lib/consolle.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "consolle/version"
|
|
4
|
+
require_relative "consolle/cli"
|
|
5
|
+
|
|
6
|
+
# Server components
|
|
7
|
+
require_relative "consolle/server/console_socket_server"
|
|
8
|
+
require_relative "consolle/server/console_supervisor"
|
|
9
|
+
require_relative "consolle/server/request_broker"
|
|
10
|
+
|
|
11
|
+
module Consolle
|
|
12
|
+
class Error < StandardError; end
|
|
13
|
+
end
|
data/mise/release.sh
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Set default version increment if not provided
|
|
6
|
+
VERSION_INCREMENT="${1:-0.0.1}"
|
|
7
|
+
|
|
8
|
+
# Validate version increment format
|
|
9
|
+
if [[ ! "$VERSION_INCREMENT" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
10
|
+
echo "Error: Version increment must be in format X.Y.Z (e.g., 0.0.1)"
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
# Change to the project root directory
|
|
15
|
+
cd "$(dirname "$0")/.."
|
|
16
|
+
|
|
17
|
+
# Check if git repository is clean
|
|
18
|
+
if [ -n "$(git status --porcelain)" ]; then
|
|
19
|
+
echo "Error: Git repository is not clean. Please commit or stash your changes."
|
|
20
|
+
git status --short
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
# Clean up old gem files
|
|
25
|
+
echo "Cleaning up old gem files..."
|
|
26
|
+
rm -f consolle-*.gem
|
|
27
|
+
|
|
28
|
+
# Read current version from .version file
|
|
29
|
+
if [ ! -f .version ]; then
|
|
30
|
+
echo "Error: .version file not found"
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
CURRENT_VERSION=$(cat .version)
|
|
35
|
+
echo "Current version: $CURRENT_VERSION"
|
|
36
|
+
|
|
37
|
+
# Parse version components
|
|
38
|
+
IFS='.' read -ra CURRENT_PARTS <<< "$CURRENT_VERSION"
|
|
39
|
+
IFS='.' read -ra INCREMENT_PARTS <<< "$VERSION_INCREMENT"
|
|
40
|
+
|
|
41
|
+
# Validate current version format
|
|
42
|
+
if [ ${#CURRENT_PARTS[@]} -ne 3 ]; then
|
|
43
|
+
echo "Error: Current version must be in format X.Y.Z"
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Calculate new version
|
|
48
|
+
MAJOR=$((CURRENT_PARTS[0] + INCREMENT_PARTS[0]))
|
|
49
|
+
MINOR=$((CURRENT_PARTS[1] + INCREMENT_PARTS[1]))
|
|
50
|
+
PATCH=$((CURRENT_PARTS[2] + INCREMENT_PARTS[2]))
|
|
51
|
+
|
|
52
|
+
# Handle carry-over
|
|
53
|
+
if [ $PATCH -ge 10 ]; then
|
|
54
|
+
MINOR=$((MINOR + PATCH / 10))
|
|
55
|
+
PATCH=$((PATCH % 10))
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
if [ $MINOR -ge 10 ]; then
|
|
59
|
+
MAJOR=$((MAJOR + MINOR / 10))
|
|
60
|
+
MINOR=$((MINOR % 10))
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
64
|
+
echo "New version: $NEW_VERSION"
|
|
65
|
+
|
|
66
|
+
# Update .version file FIRST
|
|
67
|
+
echo "$NEW_VERSION" > .version
|
|
68
|
+
|
|
69
|
+
# Update Gemfile.lock with new version
|
|
70
|
+
echo "Updating Gemfile.lock..."
|
|
71
|
+
bundle install
|
|
72
|
+
|
|
73
|
+
# Build the gem with new version
|
|
74
|
+
echo "Building gem with new version..."
|
|
75
|
+
if gem build consolle.gemspec; then
|
|
76
|
+
echo "Gem built successfully: consolle-$NEW_VERSION.gem"
|
|
77
|
+
|
|
78
|
+
# Git operations
|
|
79
|
+
echo "Committing version bump..."
|
|
80
|
+
git add .version Gemfile.lock
|
|
81
|
+
git commit -m "Bump version to $NEW_VERSION"
|
|
82
|
+
|
|
83
|
+
echo "Creating git tag..."
|
|
84
|
+
git tag -a "v$NEW_VERSION" -m "Release version $NEW_VERSION"
|
|
85
|
+
|
|
86
|
+
echo "Pushing to remote..."
|
|
87
|
+
git push origin
|
|
88
|
+
git push origin "v$NEW_VERSION"
|
|
89
|
+
|
|
90
|
+
# Ask about publishing to RubyGems.org
|
|
91
|
+
echo ""
|
|
92
|
+
read -p "Publish to RubyGems.org? (y/N): " publish_confirm
|
|
93
|
+
|
|
94
|
+
if [[ "$publish_confirm" =~ ^[Yy]$ ]]; then
|
|
95
|
+
echo "Publishing to RubyGems.org..."
|
|
96
|
+
if gem push consolle-$NEW_VERSION.gem; then
|
|
97
|
+
echo "✓ Gem published successfully to RubyGems.org"
|
|
98
|
+
echo ""
|
|
99
|
+
echo "View your gem at: https://rubygems.org/gems/consolle"
|
|
100
|
+
else
|
|
101
|
+
echo "Error: RubyGems.org push failed"
|
|
102
|
+
echo "You can manually publish with:"
|
|
103
|
+
echo " gem push consolle-$NEW_VERSION.gem"
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
else
|
|
107
|
+
echo "Skipping RubyGems.org publishing."
|
|
108
|
+
echo "You can manually publish later with:"
|
|
109
|
+
echo " gem push consolle-$NEW_VERSION.gem"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
echo "Release complete!"
|
|
113
|
+
echo "Version $NEW_VERSION has been released."
|
|
114
|
+
else
|
|
115
|
+
echo "Error: Gem build failed with new version"
|
|
116
|
+
# Revert version change
|
|
117
|
+
echo "$CURRENT_VERSION" > .version
|
|
118
|
+
bundle install # Revert Gemfile.lock
|
|
119
|
+
exit 1
|
|
120
|
+
fi
|
data/rule.ko.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Cone 명령어 가이드
|
|
2
|
+
|
|
3
|
+
consolle는 Rails Console을 서버로 제공하는 래퍼인 `cone` 명령어를 제공합니다.
|
|
4
|
+
|
|
5
|
+
`cone` 명령어를 사용하여 Rails console 세션을 시작하고 Rails에서 코드를 실행할 수 있습니다.
|
|
6
|
+
|
|
7
|
+
Rails console과 마찬가지로 세션 내에서 실행한 결과는 유지되며, 명시적으로 종료해야만 사라집니다.
|
|
8
|
+
|
|
9
|
+
사용 전에는 `status`로 상태를 확인하고, 작업 종료 후에는 `stop`해야 합니다.
|
|
10
|
+
|
|
11
|
+
## Cone의 용도
|
|
12
|
+
|
|
13
|
+
Cone은 디버깅, 데이터 탐색, 그리고 개발 보조 도구로 사용됩니다.
|
|
14
|
+
|
|
15
|
+
개발 보조 도구로 사용할 때는 수정한 코드의 반영 여부를 항상 의식해야 합니다.
|
|
16
|
+
|
|
17
|
+
코드를 수정한 경우 서버를 재시작하거나 `reload!`를 사용해야 최신 코드가 반영됩니다.
|
|
18
|
+
|
|
19
|
+
기존 객체 역시 이전 코드를 참조하므로, 새롭게 만들어야 최신 코드를 사용합니다.
|
|
20
|
+
|
|
21
|
+
## Cone 서버 시작과 중지
|
|
22
|
+
|
|
23
|
+
`start` 명령어로 cone을 시작할 수 있으며, `-e`로 실행 환경을 지정할 수 있습니다.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
$ cone start # 서버 시작
|
|
27
|
+
$ cone start -e test # test 환경에서 console 시작
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
중지와 재시작 명령어도 제공합니다.
|
|
31
|
+
|
|
32
|
+
Cone은 한 번에 하나의 세션만 제공하며, 실행 환경을 변경하려면 반드시 중지 후 재시작해야 합니다.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
$ cone stop # 서버 중지
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
작업을 마치면 반드시 종료해 주세요.
|
|
39
|
+
|
|
40
|
+
## Cone 서버 상태 확인
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
$ cone status
|
|
44
|
+
✓ Rails console is running
|
|
45
|
+
PID: 36384
|
|
46
|
+
Environment: test
|
|
47
|
+
Session: /Users/ben/syncthing/workspace/karrot-inhouse/ehr/tmp/cone/cone.socket
|
|
48
|
+
Ready for input: Yes
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## 코드 실행
|
|
52
|
+
|
|
53
|
+
코드를 평가하고 출력한 결과가 반환됩니다. 평가 결과는 `=> ` 접두사와 함께 출력됩니다.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
$ cone exec 'User.count'
|
|
57
|
+
=> 1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
변수를 사용하는 예제 (세션이 유지됩니다):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
$ cone exec 'u = User.last'
|
|
64
|
+
=> #<User id: 1, email: "user@example.com", created_at: "2025-07-17 15:16:34.685972000 +0900", updated_at: "2025-07-17 15:16:34.685972000 +0900">
|
|
65
|
+
|
|
66
|
+
$ cone exec 'puts u'
|
|
67
|
+
#<User:0x00000001104bbd18>
|
|
68
|
+
=> nil
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`-f` 옵션을 사용하여 Ruby 파일을 직접 실행할 수도 있습니다. Rails Runner와 달리 IRB 세션에서 실행됩니다.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
$ cone exec -f example.rb
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
디버깅을 위한 `-v` 옵션(Verbose 출력)이 제공됩니다.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
$ cone exec -v 'puts "hello, world"'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 코드 입력 모범 사례
|
|
84
|
+
|
|
85
|
+
### 홑따옴표 사용 (강력 권장)
|
|
86
|
+
|
|
87
|
+
`cone exec`에 코드를 전달할 때는 **항상 홑따옴표를 사용하세요**. 이는 모든 cone 사용자에게 권장되는 방법입니다:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
$ cone exec 'User.where(active: true).count'
|
|
91
|
+
$ cone exec 'puts "Hello, world!"'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### --raw 옵션 사용
|
|
95
|
+
|
|
96
|
+
**Claude Code 사용자 주의: --raw 옵션을 사용하지 마세요.** 이 옵션은 Claude Code 환경에서는 필요하지 않습니다.
|
|
97
|
+
|
|
98
|
+
### 멀티라인 코드 지원
|
|
99
|
+
|
|
100
|
+
Cone은 멀티라인 코드 실행을 완벽하게 지원합니다. 멀티라인 코드를 실행하는 방법은 여러 가지가 있습니다:
|
|
101
|
+
|
|
102
|
+
#### 방법 1: 홑따옴표를 사용한 멀티라인 문자열
|
|
103
|
+
```bash
|
|
104
|
+
$ cone exec '
|
|
105
|
+
users = User.active
|
|
106
|
+
puts "Active users: #{users.count}"
|
|
107
|
+
users.first
|
|
108
|
+
'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### 방법 2: 파일 사용
|
|
112
|
+
복잡한 멀티라인 코드는 파일에 저장하세요:
|
|
113
|
+
```bash
|
|
114
|
+
$ cone exec -f complex_task.rb
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
모든 방법은 세션 상태를 유지하므로 변수와 객체가 실행 간에 지속됩니다.
|
data/rule.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Cone Command Guide
|
|
2
|
+
|
|
3
|
+
consolle provides the `cone` command, a wrapper that serves Rails Console as a server.
|
|
4
|
+
|
|
5
|
+
Using the `cone` command, you can start a Rails console session and execute code in Rails.
|
|
6
|
+
|
|
7
|
+
Similar to Rails console, results executed within the session are maintained and only disappear when explicitly terminated.
|
|
8
|
+
|
|
9
|
+
Before use, check the status with `status`, and after finishing work, you should `stop` it.
|
|
10
|
+
|
|
11
|
+
## Purpose of Cone
|
|
12
|
+
|
|
13
|
+
Cone is used for debugging, data exploration, and as a development assistant tool.
|
|
14
|
+
|
|
15
|
+
When using it as a development assistant tool, you must always be aware of whether modified code has been reflected.
|
|
16
|
+
|
|
17
|
+
When code is modified, you need to restart the server or use `reload!` to reflect the latest code.
|
|
18
|
+
|
|
19
|
+
Existing objects also reference old code, so you need to create new ones to use the latest code.
|
|
20
|
+
|
|
21
|
+
## Starting and Stopping Cone Server
|
|
22
|
+
|
|
23
|
+
You can start cone with the `start` command and specify the execution environment with `-e`.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
$ cone start # Start server
|
|
27
|
+
$ cone start -e test # Start console in test environment
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
It also provides stop and restart commands.
|
|
31
|
+
|
|
32
|
+
Cone provides only one session at a time, and to change the execution environment, you must stop and restart.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
$ cone stop # Stop server
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Always terminate when you finish your work.
|
|
39
|
+
|
|
40
|
+
## Checking Cone Server Status
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
$ cone status
|
|
44
|
+
✓ Rails console is running
|
|
45
|
+
PID: 36384
|
|
46
|
+
Environment: test
|
|
47
|
+
Session: /Users/ben/syncthing/workspace/karrot-inhouse/ehr/tmp/cone/cone.socket
|
|
48
|
+
Ready for input: Yes
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Executing Code
|
|
52
|
+
|
|
53
|
+
The evaluated and output results of code are returned. The evaluation result is output with the `=> ` prefix.
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
$ cone exec 'User.count'
|
|
57
|
+
=> 1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Example using variables (session is maintained):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
$ cone exec 'u = User.last'
|
|
64
|
+
=> #<User id: 1, email: "user@example.com", created_at: "2025-07-17 15:16:34.685972000 +0900", updated_at: "2025-07-17 15:16:34.685972000 +0900">
|
|
65
|
+
|
|
66
|
+
$ cone exec 'puts u'
|
|
67
|
+
#<User:0x00000001104bbd18>
|
|
68
|
+
=> nil
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
You can also execute Ruby files directly using the `-f` option. Unlike Rails Runner, this is executed in an IRB session.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
$ cone exec -f example.rb
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A `-v` option (Verbose output) is provided for debugging.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
$ cone exec -v 'puts "hello, world"'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Best Practices for Code Input
|
|
84
|
+
|
|
85
|
+
### Using Single Quotes (Strongly Recommended)
|
|
86
|
+
|
|
87
|
+
**Always use single quotes** when passing code to `cone exec`. This is the recommended practice for all cone users:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
$ cone exec 'User.where(active: true).count'
|
|
91
|
+
$ cone exec 'puts "Hello, world!"'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Using the --raw Option
|
|
95
|
+
|
|
96
|
+
**Note for Claude Code users: DO NOT use the --raw option.** This option is not needed in Claude Code environments.
|
|
97
|
+
|
|
98
|
+
### Multi-line Code Support
|
|
99
|
+
|
|
100
|
+
Cone fully supports multi-line code execution. There are several ways to execute multi-line code:
|
|
101
|
+
|
|
102
|
+
#### Method 1: Multi-line String with Single Quotes
|
|
103
|
+
```bash
|
|
104
|
+
$ cone exec '
|
|
105
|
+
users = User.active
|
|
106
|
+
puts "Active users: #{users.count}"
|
|
107
|
+
users.first
|
|
108
|
+
'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Method 2: Using a File
|
|
112
|
+
For complex multi-line code, save it in a file:
|
|
113
|
+
```bash
|
|
114
|
+
$ cone exec -f complex_task.rb
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
All methods maintain the session state, so variables and objects persist between executions.
|