Skip to content

Commit 8c1be71

Browse files
authored
Merge pull request #93 from ut-code/docs-ruby
rubyのドキュメントを追加
2 parents 1fa0d9a + 1c6af3e commit 8c1be71

File tree

21 files changed

+3419
-62
lines changed

21 files changed

+3419
-62
lines changed

app/pagesList.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,25 @@ export const pagesList = [
2020
{ id: 9, title: "ジェネレータとデコレータ" },
2121
],
2222
},
23+
{
24+
id: "ruby",
25+
lang: "Ruby",
26+
description: "hoge",
27+
pages: [
28+
{ id: 1, title: "rubyの世界へようこそ" },
29+
{ id: 2, title: "基本構文とデータ型" },
30+
{ id: 3, title: "制御構造とメソッド定義" },
31+
{ id: 4, title: "すべてがオブジェクト" },
32+
{ id: 5, title: "コレクション (Array, Hash, Range)" },
33+
{ id: 6, title: "ブロックとイテレータ" },
34+
{ id: 7, title: "クラスとオブジェクト" },
35+
{ id: 8, title: "モジュールとMix-in" },
36+
{ id: 9, title: "Proc, Lambda, クロージャ" },
37+
{ id: 10, title: "標準ライブラリの活用" },
38+
{ id: 11, title: "テスト文化入門" },
39+
{ id: 12, title: "メタプログラミング入門" },
40+
],
41+
},
2342
{
2443
id: "cpp",
2544
lang: "C++",

app/terminal/exec.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ export function ExecFile(props: ExecProps) {
4747
terminalInstanceRef.current!.write(systemMessageColor("実行中です..."));
4848
const outputs = await runFiles(props.filenames);
4949
clearTerminal(terminalInstanceRef.current!);
50-
writeOutput(terminalInstanceRef.current!, outputs, false);
50+
writeOutput(
51+
terminalInstanceRef.current!,
52+
outputs,
53+
false,
54+
undefined,
55+
props.language
56+
);
5157
// TODO: 1つのファイル名しか受け付けないところに無理やりコンマ区切りで全部のファイル名を突っ込んでいる
5258
setExecResult(props.filenames.join(","), outputs);
5359
setExecutionState("idle");
@@ -60,6 +66,7 @@ export function ExecFile(props: ExecProps) {
6066
runFiles,
6167
setExecResult,
6268
terminalInstanceRef,
69+
props.language,
6370
]);
6471

6572
return (

app/terminal/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const sampleConfig: Record<RuntimeLang, SampleConfig> = {
6060
},
6161
ruby: {
6262
repl: true,
63-
replInitContent: '>> puts "Hello, World!"\nHello, World!',
63+
replInitContent: 'irb(main):001:0> puts "Hello, World!"\nHello, World!',
6464
editor: {
6565
"main.rb": 'puts "Hello, World!"',
6666
},

app/terminal/repl.tsx

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export type SyntaxStatus = "complete" | "incomplete" | "invalid"; // 構文チ
2828
export function writeOutput(
2929
term: Terminal,
3030
outputs: ReplOutput[],
31-
endNewLine: boolean
31+
endNewLine: boolean,
32+
returnPrefix: string | undefined,
33+
language: RuntimeLang
3234
) {
3335
for (let i = 0; i < outputs.length; i++) {
3436
const output = outputs[i];
@@ -47,6 +49,12 @@ export function writeOutput(
4749
case "system":
4850
term.write(systemMessageColor(message));
4951
break;
52+
case "return":
53+
if (returnPrefix) {
54+
term.write(returnPrefix);
55+
}
56+
term.write(highlightCodeToAnsi(message, language));
57+
break;
5058
default:
5159
term.write(message);
5260
break;
@@ -77,7 +85,7 @@ export function ReplTerminal({
7785
checkSyntax,
7886
splitReplExamples,
7987
} = useRuntime(language);
80-
const { tabSize, prompt, promptMore } = langConstants(language);
88+
const { tabSize, prompt, promptMore, returnPrefix } = langConstants(language);
8189
if (!prompt) {
8290
console.warn(`prompt not defined for language: ${language}`);
8391
}
@@ -160,12 +168,18 @@ export function ReplTerminal({
160168
const handleOutput = useCallback(
161169
(outputs: ReplOutput[]) => {
162170
if (terminalInstanceRef.current) {
163-
writeOutput(terminalInstanceRef.current, outputs, true);
171+
writeOutput(
172+
terminalInstanceRef.current,
173+
outputs,
174+
true,
175+
returnPrefix,
176+
language
177+
);
164178
// 出力が終わったらプロンプトを表示
165179
updateBuffer(() => [""]);
166180
}
167181
},
168-
[updateBuffer, terminalInstanceRef]
182+
[updateBuffer, terminalInstanceRef, returnPrefix, language]
169183
);
170184

171185
const keyHandler = useCallback(
@@ -184,17 +198,10 @@ export function ReplTerminal({
184198
}
185199
} else if (code === 13) {
186200
// Enter
187-
const hasContent =
188-
inputBuffer.current[inputBuffer.current.length - 1].trim()
189-
.length > 0;
190201
const status = checkSyntax
191202
? await checkSyntax(inputBuffer.current.join("\n"))
192203
: "complete";
193-
if (
194-
(inputBuffer.current.length === 1 && status === "incomplete") ||
195-
(inputBuffer.current.length >= 2 && hasContent) ||
196-
!isLastChar
197-
) {
204+
if (status === "incomplete" || !isLastChar) {
198205
// 次の行に続く
199206
updateBuffer(() => [...inputBuffer.current, ""]);
200207
} else {

app/terminal/runtime.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export interface LangConstants {
3030
tabSize: number;
3131
prompt?: string;
3232
promptMore?: string;
33+
returnPrefix?: string;
3334
}
3435
export type RuntimeLang = "python" | "ruby" | "cpp" | "javascript";
3536

@@ -99,8 +100,10 @@ export function langConstants(lang: RuntimeLang | AceLang): LangConstants {
99100
case "ruby":
100101
return {
101102
tabSize: 2,
102-
prompt: ">> ",
103-
promptMore: "?> ",
103+
// TODO: 実際のirbのプロンプトは静的でなく、(main)や番号などの動的な表示がある
104+
prompt: "irb> ",
105+
promptMore: "irb* ",
106+
returnPrefix: "=> ",
104107
};
105108
case "javascript":
106109
return {

app/terminal/worker/ruby.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,26 @@ export function useRuby() {
2121
function splitReplExamples(content: string): ReplCommand[] {
2222
const initCommands: { command: string; output: ReplOutput[] }[] = [];
2323
for (const line of content.split("\n")) {
24-
if (line.startsWith(">> ")) {
25-
// Ruby IRB uses >> as the prompt
26-
initCommands.push({ command: line.slice(3), output: [] });
27-
} else if (line.startsWith("?> ")) {
28-
// Ruby IRB uses ?> for continuation
24+
if (line.startsWith("irb")) {
25+
if (
26+
initCommands.length > 0 &&
27+
initCommands[initCommands.length - 1].output.length === 0
28+
) {
29+
// 前のコマンド行と連続している場合つなげる
30+
initCommands[initCommands.length - 1].command +=
31+
"\n" + line.slice(line.indexOf(" ") + 1);
32+
} else {
33+
initCommands.push({
34+
command: line.slice(line.indexOf(" ") + 1),
35+
output: [],
36+
});
37+
}
38+
} else if (line.startsWith("=> ")) {
2939
if (initCommands.length > 0) {
30-
initCommands[initCommands.length - 1].command += "\n" + line.slice(3);
40+
initCommands[initCommands.length - 1].output.push({
41+
type: "return",
42+
message: line.slice(3),
43+
});
3144
}
3245
} else {
3346
// Lines without prompt are output from the previous command

public/docs/python-1.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
# 第1章: Pythonへようこそ:環境構築と基本
1+
# 第1章: Pythonへようこそ:環境構築と基本思想
22

33
プログラミング経験者であっても、言語ごとのツールや流儀を最初に理解することは重要です。この章では、Pythonの開発環境を整え、基本的なツールの使い方を学びます。
44

5+
## Pythonの思想と特徴: 「読みやすさ」は最優先
6+
7+
他の言語(Java, C++, PHPなど)と比較したとき、Pythonが最も重視するのは**コードの可読性(Readability)**です。
8+
9+
* **シンプルな文法:** C言語やJavaのような `{}`(波括弧)によるブロックや、行末の `;`(セミコロン)を必要としません。
10+
* **強制的なインデント:** Pythonは、**インデント(字下げ)**そのものでコードブロックを表現します。これは構文的なルールであり、オプションではありません。これにより、誰が書いても(ある程度)同じような見た目のコードになり、可読性が劇的に向上します。
11+
* **動的型付け (Dynamic Typing):** JavaやC++のように `int num = 10;` と変数の型を明示的に宣言する必要がありません。`num = 10` と書けば、Pythonが実行時に自動的に型を推論します。(これはJavaScriptやPHPと似ていますが、Pythonは型付けがより厳格(Strong Typing)で、例えば文字列と数値を暗黙的に連結しようとするとエラーになります)
12+
* **豊富な標準ライブラリ**: 「Batteries Included(バッテリー同梱)」という思想のもと、OS操作、ネットワーク、データ処理、JSON、正規表現など、多くの機能が最初から標準ライブラリとして提供されています。
13+
14+
**💡 The Zen of Python (Pythonの禅)** Pythonの設計思想は、`import this` というコマンドでいつでも確認できます。
15+
16+
* Beautiful is better than ugly. (醜いより美しいほうがいい)
17+
* Explicit is better than implicit. (暗黙的より明示的なほうがいい)
18+
* Simple is better than complex. (複雑であるよりシンプルなほうがいい)
19+
520
## Pythonのインストール方法
621

722
手元の環境で本格的に開発を進めるために、Pythonのインストール方法を紹介します。

public/docs/ruby-1.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# 第1章: Rubyの世界へようこそ - 環境構築と第一歩
2+
3+
Rubyへようこそ! 他の言語でのプログラミング経験をお持ちのあなたなら、Rubyの持つ柔軟性と「開発者の楽しさ」をすぐに感じ取れるはずです。この章では、Rubyの基本的な哲学に触れ、あなたのマシンに開発環境をセットアップし、最初のコードを実行します。
4+
5+
## Rubyの哲学と特徴
6+
7+
Rubyは、まつもとゆきひろ(Matz)氏によって開発された、**シンプルさ****生産性**を重視した動的オブジェクト指向言語です。
8+
9+
* **すべてがオブジェクト (Everything is an Object)**
10+
JavaやPythonでは`int``float`などのプリミティブ型がオブジェクトとは別に存在しますが、Rubyでは**すべてがメソッドを持つオブジェクト**です。`5`のような数値や`"hello"`のような文字列はもちろん、`nil`(nullに相当)や`true`/`false`さえもオブジェクトです。
11+
12+
```ruby-repl:1
13+
irb(main):001> 5.class
14+
=> Integer
15+
irb(main):002> "hello".upcase
16+
=> "HELLO"
17+
irb(main):003> nil.nil?
18+
=> true
19+
```
20+
21+
* **開発者を楽しませる (MINASWAN)**
22+
Rubyの設計思想の核は、プログラマがストレスなく、楽しくコーディングできることを最適化する点にあります。これはしばしば「**MINASWAN** (Matz Is Nice And So We Are Nice)」というコミュニティの標語にも表れています。言語仕様が厳格さよりも「驚き最小の原則」や表現力を優先することがあります。
23+
24+
* **柔軟性と表現力**
25+
Rubyは非常に柔軟で、既存のクラスを後から変更する(オープンクラス)ことや、コードによってコードを操作するメタプログラミングが容易です。これにより、Ruby on Railsのような強力なフレームワークや、RSpecのようなDSL(ドメイン固有言語)が生まれています。
26+
27+
28+
## 他言語との簡単な比較
29+
30+
あなたの経験言語とRubyを少し比べてみましょう。
31+
32+
* **Pythonistaへ**:
33+
34+
* インデントは構文的な意味を持ちません(単なる可読性のため)。
35+
* ブロック(コードのまとまり)は`:`とインデントではなく、`do...end`キーワード、または`{...}`(ブレース)で定義します。
36+
* メソッド呼び出しの丸括弧`()`は、曖昧さがなければ省略可能です。`print "hello"`のように書けます。
37+
38+
* **Java/C\# Developerへ**:
39+
40+
* Rubyは静的型付けではなく**動的型付け**です。変数の型宣言(`int i = 10`)は不要で、`i = 10`と書くだけです。
41+
* コンパイルステップは不要です。スクリプトとして直接実行されます。
42+
* `public`, `private`, `protected`のアクセス修飾子はありますが、Javaなどとは少し動作が異なります(特に`private`)。
43+
44+
* **JavaScript Developerへ**:
45+
46+
* Rubyもクラスベースのオブジェクト指向です(ただし、内部的にはJSのプロトタイプチェーンに似た特異クラスの仕組みも持ちます)。
47+
* `this`の代わりに`self`を使いますが、`self`のコンテキストはJSの`this`ほど複雑に変化せず、より直感的です。
48+
* `true`, `false`, `nil` 以外はすべて「Truthy(真)」として扱われます(`0`や空文字列`""`も真です)。
49+
50+
## 環境構築:バージョン管理ツールの導入
51+
52+
システム(OS)に最初から入っているRubyを直接使うのではなく、**バージョン管理ツール**(`rbenv`や`RVM`)を導入することを強く推奨します。
53+
54+
プロジェクトごとに異なるRubyバージョン(例: 2.7.x と 3.3.x)を簡単に切り替えることができ、システムをクリーンに保てます。
55+
56+
ここでは`rbenv`を使った一般的な流れを紹介します。(詳細なインストール手順はOSによって異なりますので、`rbenv`のGitHubリポジトリなどを参照してください。)
57+
58+
1. **rbenv と ruby-build のインストール**:
59+
Homebrew (macOS) や apt/yum (Linux) など、お使いのパッケージマネージャでインストールします。
60+
61+
2. **Rubyのインストール**:
62+
63+
```bash
64+
# インストール可能なバージョン一覧を確認
65+
$ rbenv install -l
66+
67+
# 最新の安定版(例: 3.3.0)をインストール
68+
$ rbenv install 3.3.0
69+
```
70+
71+
3. **使用するバージョンの設定**:
72+
73+
```bash
74+
# このPCでのデフォルトバージョンとして設定
75+
$ rbenv global 3.3.0
76+
77+
# バージョンが切り替わったか確認
78+
$ ruby -v
79+
ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [arm64-darwin23]
80+
```
81+
82+
## 対話型シェル(irb)の活用
83+
84+
Rubyのインストールが完了したら、`irb` (Interactive Ruby) を起動してみましょう。これはRubyのREPL (Read-Eval-Print Loop) で、コード片を試したり、ドキュメント代わりに使ったりするのに非常に便利です。
85+
86+
ターミナルで`irb`と入力することで起動できます。
87+
88+
このウェブサイトではドキュメント内にRubyの実行環境を埋め込んでいます。
89+
以下のように青枠で囲われたコード例には自由にRubyコードを書いて試すことができます。
90+
91+
```ruby-repl:2
92+
irb(main):001> 10 * (5 + 3)
93+
=> 80
94+
irb(main):002> "Ruby".length
95+
=> 4
96+
irb(main):003> 3.times { puts "Welcome!" }
97+
Welcome!
98+
Welcome!
99+
Welcome!
100+
=> 3
101+
```
102+
103+
`=>` の右側に表示されているのが、式の**評価結果(返り値)**です。
104+
105+
`3.times`の例に注目してください。`puts "Welcome!"`が3回実行(出力)されていますが、`=> 3`と表示されています。これは、`3.times`というメソッド自体の返り値が`3`(レシーバである数値)であることを示しています。Rubyではすべての式が値を返すことを覚えておいてください。
106+
107+
## "Hello, World\!" とスクリプトの実行
108+
109+
最後に、定番の "Hello, World\!" を2通りの方法で実行してみましょう。
110+
111+
### irbでの実行
112+
113+
`puts`("put string")は、引数を標準出力(ターミナル)に出力し、最後に改行を追加するメソッドです。
114+
115+
```ruby-repl:3
116+
irb(main):001> puts "Hello, World!"
117+
Hello, World!
118+
=> nil
119+
```
120+
121+
`puts`メソッド自体の返り値は、常に`nil`です。)
122+
123+
### スクリプトファイルでの実行
124+
125+
エディタで`hello.rb`という名前のファイルを作成します。
126+
127+
```ruby:hello.rb
128+
#!/usr/bin/env ruby
129+
# 1行目はShebang(シーバン)と言い、Unix系OSでスクリプトとして直接実行する際に使われます。
130+
131+
# 変数に文字列を代入
132+
message = "Hello from script file!"
133+
134+
# 変数の内容を出力
135+
puts message
136+
```
137+
138+
このファイルを実行するには、ターミナルで`ruby`コマンドの引数にファイル名を渡します。
139+
140+
このウェブサイト上では以下のように実行ボタンをクリックするとスクリプトの実行結果が表示されます。上の hello.rb のコードを変更して再度実行すると結果も変わるはずです。試してみてください。
141+
142+
```ruby-exec:hello.rb
143+
Hello from script file!
144+
```
145+
146+
おめでとうございます! これでRubyの世界への第一歩を踏み出しました。
147+
次の章では、Rubyの基本的な構文、データ型、そして他の言語にはない特徴的な「シンボル」について詳しく学んでいきます。

0 commit comments

Comments
 (0)