rbcsv 0.1.8 → 0.2.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.
@@ -0,0 +1,197 @@
1
+ # CSV書き込み機能実装ガイド
2
+
3
+ ## 概要
4
+
5
+ 本ドキュメントは、RbCsv gemにおけるCSV書き込み機能(`RbCsv.write()`)の実装プロセスを詳細に記録したものです。実装から重要なバグ修正、そして最終的なリリースまでの一連の流れを説明しています。
6
+
7
+ ## 実装目標
8
+
9
+ CSVデータを配列からファイルに書き込む機能を追加し、既存の読み込み機能との完全な互換性を確保する。
10
+
11
+ ```ruby
12
+ # 目標API
13
+ data = [
14
+ ['name', 'age', 'city'],
15
+ ['Alice', '25', 'Tokyo'],
16
+ ['Bob', '30', 'Osaka']
17
+ ]
18
+ RbCsv.write('output.csv', data)
19
+ ```
20
+
21
+ ## 実装アーキテクチャ
22
+
23
+ ### 1. コア実装層(Rust)
24
+
25
+ #### `ext/rbcsv/src/parser.rs`
26
+ ```rust
27
+ pub fn write_csv_file(file_path: &str, data: &[Vec<String>]) -> Result<(), CsvError>
28
+ ```
29
+
30
+ **主要機能:**
31
+ - データ検証(空配列チェック、フィールド数整合性)
32
+ - ファイルパス検証(親ディレクトリ存在確認)
33
+ - CSV Writer設定とデータ書き込み
34
+ - エラーハンドリング(権限エラー、IOエラー)
35
+
36
+ #### `ext/rbcsv/src/ruby_api.rs`
37
+ ```rust
38
+ pub fn write(ruby: &Ruby, file_path: String, data: Vec<Vec<String>>) -> Result<(), MagnusError>
39
+ ```
40
+
41
+ **役割:**
42
+ - RubyとRust間のAPIブリッジ
43
+ - データ型変換(Ruby Array → Rust Vec)
44
+ - エラー変換(Rust Error → Ruby Exception)
45
+
46
+ #### `ext/rbcsv/src/error.rs`
47
+ ```rust
48
+ // 新規追加エラータイプ
49
+ WritePermission, // 書き込み権限エラー
50
+ InvalidData, // 無効なデータエラー
51
+ ```
52
+
53
+ ### 2. Ruby API層
54
+
55
+ #### `ext/rbcsv/src/lib.rs`
56
+ ```rust
57
+ module.define_singleton_method("write", magnus::function!(write, 2))?;
58
+ ```
59
+
60
+ Ruby側からの呼び出しを可能にするAPIメソッド登録。
61
+
62
+ ## データ検証ロジック
63
+
64
+ ### 1. 空データチェック
65
+ ```rust
66
+ if data.is_empty() {
67
+ return Err(CsvError::invalid_data("CSV data is empty"));
68
+ }
69
+ ```
70
+
71
+ ### 2. フィールド数整合性チェック
72
+ ```rust
73
+ let expected_len = data[0].len();
74
+ for (line_num, row) in data.iter().enumerate() {
75
+ if row.len() != expected_len {
76
+ let error_msg = format!(
77
+ "Field count mismatch at line {}: expected {} fields, got {} fields",
78
+ line_num + 1, expected_len, row.len()
79
+ );
80
+ return Err(CsvError::invalid_data(error_msg));
81
+ }
82
+ }
83
+ ```
84
+
85
+ ### 3. ファイルパス検証
86
+ ```rust
87
+ let path = Path::new(file_path);
88
+ if let Some(parent) = path.parent() {
89
+ if !parent.exists() {
90
+ return Err(CsvError::io(format!("Parent directory does not exist: {}", parent.display())));
91
+ }
92
+ }
93
+ ```
94
+
95
+ ## エラーハンドリング戦略
96
+
97
+ ### 1. 権限エラー
98
+ ```rust
99
+ if e.kind() == std::io::ErrorKind::PermissionDenied {
100
+ return Err(CsvError::write_permission(format!("Permission denied: {}", file_path)));
101
+ }
102
+ ```
103
+
104
+ ### 2. データ検証エラー
105
+ - 空データ:`CSV data is empty`
106
+ - フィールド数不一致:`Field count mismatch at line X: expected Y fields, got Z fields`
107
+
108
+ ### 3. IOエラー
109
+ - ファイル作成失敗:`Failed to create file 'path': error`
110
+ - フラッシュ失敗:`Failed to flush data to file 'path': error`
111
+
112
+ ## テスト戦略
113
+
114
+ ### 1. Rust単体テスト(`ext/rbcsv/src/parser.rs`)
115
+ ```rust
116
+ #[test]
117
+ fn test_write_csv_file_basic() { /* 基本書き込みテスト */ }
118
+
119
+ #[test]
120
+ fn test_write_csv_file_empty_data() { /* 空データエラーテスト */ }
121
+
122
+ #[test]
123
+ fn test_write_csv_file_field_count_mismatch() { /* フィールド数不一致テスト */ }
124
+ ```
125
+
126
+ ### 2. RSpec統合テスト(`spec/rbcsv_spec.rb`)
127
+ ```ruby
128
+ describe ".write" do
129
+ it "writes CSV data to file"
130
+ it "overwrites existing file"
131
+ it "raises error for empty data"
132
+ it "raises error for inconsistent field count"
133
+ it "can write and read back the same data"
134
+ end
135
+ ```
136
+
137
+ ### 3. 実行可能テストスクリプト(`test_write_functionality.rb`)
138
+ 8つの包括的テストケース:
139
+ - 基本的なCSV書き込み
140
+ - 往復テスト(write → read)
141
+ - ファイル上書き
142
+ - エラーハンドリング(空データ、フィールド数不一致)
143
+ - Unicode文字対応
144
+ - 特殊文字処理
145
+
146
+ ## CSV標準準拠
147
+
148
+ ### RFC 4180準拠
149
+ - カンマ区切り
150
+ - ダブルクォート内のダブルクォートエスケープ(`"""`)
151
+ - 改行文字の適切な処理
152
+ - フィールド内のカンマ、改行の適切なクォート
153
+
154
+ ### csv crateの活用
155
+ ```rust
156
+ let mut writer = csv::WriterBuilder::new()
157
+ .has_headers(false)
158
+ .from_writer(file);
159
+ ```
160
+
161
+ Rustの`csv` crateを使用することで、標準準拠のCSV出力を保証。
162
+
163
+ ## 実装完了基準
164
+
165
+ 1. ✅ **機能実装**: write機能の完全実装
166
+ 2. ✅ **データ検証**: 包括的な入力データ検証
167
+ 3. ✅ **エラーハンドリング**: 詳細なエラーメッセージ
168
+ 4. ✅ **テストカバレッジ**: Rust + Ruby + 実行可能テスト
169
+ 5. ✅ **ドキュメント**: DEVELOPMENT.mdの更新
170
+ 6. ✅ **往復互換性**: write/readの完全互換性
171
+
172
+ ## パフォーマンス考慮事項
173
+
174
+ ### 1. メモリ効率
175
+ - ストリーミング書き込み(大容量データ対応)
176
+ - 不要なデータコピーの回避
177
+
178
+ ### 2. エラー早期検出
179
+ - データ検証を書き込み前に実行
180
+ - 失敗時のリソース無駄遣い防止
181
+
182
+ ### 3. ファイルハンドリング
183
+ - 適切なファイルフラッシュ
184
+ - エラー時のファイル状態管理
185
+
186
+ ## 次のステップ
187
+
188
+ 1. **パフォーマンステスト**: 大容量データでのベンチマーク
189
+ 2. **設定オプション追加**: 区切り文字カスタマイズ等
190
+ 3. **ストリーミングAPI**: メモリ効率の更なる向上
191
+ 4. **非同期書き込み**: 大容量データの非同期処理
192
+
193
+ ## 参考資料
194
+
195
+ - [RFC 4180 - CSV形式仕様](https://tools.ietf.org/html/rfc4180)
196
+ - [Rust csv crate documentation](https://docs.rs/csv/)
197
+ - [Magnus framework documentation](https://docs.rs/magnus/)
@@ -0,0 +1,221 @@
1
+ # RbCsv Examples
2
+
3
+ このディレクトリには、RbCsvライブラリの使用方法を示すサンプルコードとテストスクリプトが含まれています。
4
+
5
+ ## ディレクトリ構成
6
+
7
+ ```
8
+ examples/
9
+ ├── basic/ # 基本的な使用例
10
+ ├── features/ # 特定機能のテスト・デモ
11
+ ├── benchmarks/ # パフォーマンステストとベンチマーク
12
+ └── README.md # このファイル
13
+ ```
14
+
15
+ ## 実行方法
16
+
17
+ 全ての例を実行する前に、ライブラリがビルドされていることを確認してください:
18
+
19
+ ```bash
20
+ # プロジェクトルートで実行
21
+ bundle exec rake compile
22
+ ```
23
+
24
+ ## 📁 basic/ - 基本的な使用例
25
+
26
+ ### `basic_usage.rb`
27
+ RbCsvの基本的な機能(parse, read, write)の使用例です。
28
+
29
+ ```bash
30
+ cd examples/basic
31
+ ruby basic_usage.rb
32
+ ```
33
+
34
+ **機能:**
35
+ - CSV文字列のパース
36
+ - CSVファイルの読み込み
37
+ - CSVファイルの書き込み
38
+
39
+ ### `quick_test.rb`
40
+ 簡単な動作確認用スクリプトです。
41
+
42
+ ```bash
43
+ cd examples/basic
44
+ ruby quick_test.rb
45
+ ```
46
+
47
+ ### `test_fixed.rb`
48
+ 特定のバグ修正後の動作確認用スクリプトです。
49
+
50
+ ```bash
51
+ cd examples/basic
52
+ ruby test_fixed.rb
53
+ ```
54
+
55
+ ### `test_install.rb`
56
+ インストール後の動作確認用スクリプトです。
57
+
58
+ ```bash
59
+ cd examples/basic
60
+ ruby test_install.rb
61
+ ```
62
+
63
+ ## 🔧 features/ - 特定機能のテスト・デモ
64
+
65
+ ### `test_typed_functionality.rb`
66
+ 型認識機能(parse_typed, read_typed系)の詳細なテストとデモです。
67
+
68
+ ```bash
69
+ cd examples/features
70
+ ruby test_typed_functionality.rb
71
+ ```
72
+
73
+ **機能:**
74
+ - `parse_typed()`: 数値文字列を数値型に自動変換
75
+ - `parse_typed!()`: trim + 型変換
76
+ - `read_typed()`: ファイル読み込み + 型変換
77
+ - `read_typed!()`: ファイル読み込み + trim + 型変換
78
+
79
+ **型変換例:**
80
+ - `"123"` → `123` (Integer)
81
+ - `"45.6"` → `45.6` (Float)
82
+ - `"1.23e-4"` → `0.000123` (Float)
83
+ - `"hello"` → `"hello"` (String)
84
+
85
+ ### `test_write_functionality.rb`
86
+ CSV書き込み機能の包括的なテストです。
87
+
88
+ ```bash
89
+ cd examples/features
90
+ ruby test_write_functionality.rb
91
+ ```
92
+
93
+ **機能:**
94
+ - 基本的なCSV書き込み
95
+ - ファイル上書き動作
96
+ - エラーハンドリング(空データ、フィールド数不一致など)
97
+ - 書き込み→読み込みの往復テスト
98
+
99
+ ## 📊 benchmarks/ - パフォーマンステスト
100
+
101
+ ### `benchmark.rb`
102
+ 標準ライブラリのCSVとRbCsvの包括的なパフォーマンス比較です。
103
+
104
+ ```bash
105
+ cd examples/benchmarks
106
+ ruby benchmark.rb
107
+ ```
108
+
109
+ **自動機能:**
110
+ - サンプルCSVファイルを自動生成(1000レコード)
111
+ - 大容量テストデータを自動作成(50,000レコード)
112
+ - ベンチマーク後の自動クリーンアップ
113
+
114
+ **測定項目:**
115
+ - **基本パース性能**: `parse` vs `parse_typed` vs Ruby CSV
116
+ - **ファイル読み込み性能**: `read` vs `read_typed` vs Ruby CSV
117
+ - **大容量データ処理**: 50,000レコードでの性能比較
118
+ - **メモリ使用量**: パース処理でのメモリ効率性
119
+ - **データ処理性能**: フィルタリングと検索の速度比較
120
+ - **型変換比較**: 手動変換 vs 自動型認識の性能差
121
+ - **データ精度検証**: 結果の正確性確認
122
+
123
+ **期待される結果:**
124
+ - RbCsv は Ruby標準CSV より 2-4倍 高速
125
+ - `parse_typed` は型変換コストを大幅削減
126
+ - メモリ使用量も効率的
127
+
128
+ ### `output_comparison.rb`
129
+ 標準ライブラリのCSVとRbCsvの出力形式比較です。
130
+
131
+ ```bash
132
+ cd examples/benchmarks
133
+ ruby output_comparison.rb
134
+ ```
135
+
136
+ **要求事項:**
137
+ - `sample.csv` ファイルが必要
138
+
139
+ ## 🚀 実行例
140
+
141
+ ### 基本的なCSV処理
142
+
143
+ ```ruby
144
+ require_relative '../../lib/rbcsv'
145
+
146
+ # CSV文字列のパース
147
+ csv_data = "name,age,city\nAlice,25,Tokyo\nBob,30,Osaka"
148
+ result = RbCsv.parse(csv_data)
149
+ # => [["name", "age", "city"], ["Alice", "25", "Tokyo"], ["Bob", "30", "Osaka"]]
150
+ ```
151
+
152
+ ### 型認識パース
153
+
154
+ ```ruby
155
+ require_relative '../../lib/rbcsv'
156
+
157
+ # 数値を自動的に数値型に変換
158
+ csv_data = "name,age,score\nAlice,25,85.5\nBob,30,92"
159
+ result = RbCsv.parse_typed(csv_data)
160
+ # => [["name", "age", "score"], ["Alice", 25, 85.5], ["Bob", 30, 92]]
161
+ # 注意: 25は整数、85.5は浮動小数点数として返される
162
+ ```
163
+
164
+ ### CSVファイルの書き込みと読み込み
165
+
166
+ ```ruby
167
+ require_relative '../../lib/rbcsv'
168
+
169
+ # データの準備
170
+ data = [
171
+ ["product", "price", "quantity"],
172
+ ["Apple", "100", "50"],
173
+ ["Orange", "80.5", "30"]
174
+ ]
175
+
176
+ # ファイルに書き込み
177
+ RbCsv.write("output.csv", data)
178
+
179
+ # ファイルから読み込み(型認識付き)
180
+ result = RbCsv.read_typed("output.csv")
181
+ # => [["product", "price", "quantity"], ["Apple", 100, 50], ["Orange", 80.5, 30]]
182
+ # 注意: price と quantity が数値型として読み込まれる
183
+ ```
184
+
185
+ ## 💡 トラブルシューティング
186
+
187
+ ### よくあるエラー
188
+
189
+ **1. LoadError: cannot load such file**
190
+ ```bash
191
+ # 解決方法: ライブラリをビルドしてください
192
+ bundle exec rake compile
193
+ ```
194
+
195
+ **2. RuntimeError: Invalid Data Error**
196
+ ```bash
197
+ # 原因: CSVデータの形式が不正
198
+ # 解決方法: データの整合性を確認してください(空データ、フィールド数不一致など)
199
+ ```
200
+
201
+ **3. 相対パスエラー**
202
+ ```bash
203
+ # 解決方法: examples/ディレクトリのサブディレクトリから実行してください
204
+ cd examples/basic
205
+ ruby basic_usage.rb
206
+ ```
207
+
208
+ ## 📝 新しい例の追加
209
+
210
+ 新しい例を追加する場合:
211
+
212
+ 1. 適切なディレクトリを選択(basic, features, benchmarks)
213
+ 2. `require_relative '../../lib/rbcsv'` を使用
214
+ 3. スクリプトの先頭にコメントで目的と使用方法を記述
215
+ 4. このREADMEを更新
216
+
217
+ ## 🔗 関連ドキュメント
218
+
219
+ - [メインREADME](../README.md) - 基本的な使用方法
220
+ - [開発ガイド](../DEVELOPMENT.md) - 開発者向け詳細情報
221
+ - [RSpecテスト](../spec/) - 自動テストスイート
@@ -1,4 +1,4 @@
1
- require_relative 'lib/rbcsv'
1
+ require_relative '../../lib/rbcsv'
2
2
 
3
3
  # バージョン確認
4
4
  # puts RCsv.version_info
@@ -17,3 +17,4 @@ puts "Parsed data:"
17
17
  parsed_data.each { |row| puts row.inspect }
18
18
 
19
19
  puts "csv parse done."
20
+
@@ -1,4 +1,4 @@
1
- require_relative 'lib/r_csv'
1
+ require_relative '../../lib/rbcsv'
2
2
 
3
3
  # バージョン確認
4
4
  # puts RCsv.version_info