rbcsv 0.1.7 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -0
- data/Cargo.lock +1 -1
- data/DEVELOPMENT.md +259 -55
- data/README.md +103 -20
- data/docs/exe_upgrade_version.md +124 -0
- data/docs/release_process_v0.1.8.md +298 -0
- data/docs/special_character_bug_fix.md +257 -0
- data/docs/write_functionality_implementation.md +197 -0
- data/examples/README.md +221 -0
- data/{test.rb → examples/basic/basic_usage.rb} +2 -1
- data/{test_fixed.rb → examples/basic/test_fixed.rb} +1 -1
- data/examples/benchmarks/benchmark.rb +372 -0
- data/{output_comparison.rb → examples/benchmarks/output_comparison.rb} +41 -26
- data/examples/benchmarks/sample.csv +1001 -0
- data/examples/features/test_typed_functionality.rb +109 -0
- data/examples/features/test_write_functionality.rb +225 -0
- data/ext/rbcsv/Cargo.toml +1 -1
- data/ext/rbcsv/src/error.rs +16 -2
- data/ext/rbcsv/src/lib.rs +9 -1
- data/ext/rbcsv/src/parser.rs +202 -24
- data/ext/rbcsv/src/ruby_api.rs +115 -3
- data/ext/rbcsv/src/value.rs +87 -0
- data/lib/rbcsv/version.rb +1 -1
- metadata +16 -7
- data/benchmark.rb +0 -190
- /data/{quick_test.rb → examples/basic/quick_test.rb} +0 -0
- /data/{test_install.rb → examples/basic/test_install.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c08a56224baf175930ae9bdd61da9e29464dfb35f0336f5fcd234073d96d8200
|
|
4
|
+
data.tar.gz: f77af48e7ea72618f2e7b6476e87a78cd50f5641391f31fa571b5914d622ad5d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f22832f042dbc8e1ccbed9bef74b05ba314484dc781d7fb3901d00d4f8bac85ebf9d2e68f2e4ec25669643b1a18dc831925ec5088e1a58e365705c313730ac99
|
|
7
|
+
data.tar.gz: cd35bf0419a08e31409210b759a584df0f3f851780530b1bdad724edaa58e357919833aab1097c256119be18ea8513a2381413ecb41ccf8efd11c07057b168f6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2025-10-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Type-aware CSV parsing**: New methods for automatic type conversion
|
|
7
|
+
- `parse_typed` and `parse_typed!` for string parsing with type detection
|
|
8
|
+
- `read_typed` and `read_typed!` for file reading with type detection
|
|
9
|
+
- Automatically converts numeric strings to Integer or Float types
|
|
10
|
+
- Preserves strings for non-numeric values
|
|
11
|
+
- **Performance benchmarks**: Comprehensive benchmark suite
|
|
12
|
+
- 2.4-3.8x faster than Ruby's standard CSV library for parse operations
|
|
13
|
+
- ~140x faster for type conversion compared to manual Ruby conversion
|
|
14
|
+
- Added benchmark results to README
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Improved README with cleaner, more concise documentation
|
|
18
|
+
- Added author information and contribution guidelines
|
|
19
|
+
- Updated API reference section for better clarity
|
|
20
|
+
|
|
21
|
+
### Technical
|
|
22
|
+
- Added `CsvValue` enum for type-safe value handling
|
|
23
|
+
- Implemented efficient type detection algorithm (Integer → Float → String)
|
|
24
|
+
- Added comprehensive tests for type conversion functionality
|
|
25
|
+
|
|
26
|
+
## [0.1.8] - 2025-01-28
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
- CSV file writing functionality with `RbCsv.write(file_path, data)` method
|
|
30
|
+
- Comprehensive data validation (empty data check, field count consistency)
|
|
31
|
+
- Enhanced error handling for write operations (permission errors, invalid data)
|
|
32
|
+
- Full test coverage for write functionality with executable test script
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **CRITICAL**: Fixed special character handling in CSV parsing
|
|
36
|
+
- Removed problematic `escape_sanitize` function that interfered with standard CSV escaping
|
|
37
|
+
- Now properly preserves backslashes, newlines, tabs, and other special characters
|
|
38
|
+
- Ensures perfect round-trip fidelity for write/read operations
|
|
39
|
+
- Updated RSpec tests to reflect correct CSV parsing behavior
|
|
40
|
+
|
|
3
41
|
## [0.1.7] - 2025-01-28
|
|
4
42
|
|
|
5
43
|
### Changed
|
data/Cargo.lock
CHANGED
data/DEVELOPMENT.md
CHANGED
|
@@ -16,39 +16,6 @@
|
|
|
16
16
|
- **Linux**: x86_64 / aarch64
|
|
17
17
|
- **Windows**: x86_64(実験的サポート)
|
|
18
18
|
|
|
19
|
-
## プロジェクト構成
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
r_csv/
|
|
23
|
-
├── lib/
|
|
24
|
-
│ ├── rbcsv.rb # メインのRubyエントリーポイント
|
|
25
|
-
│ └── rbcsv/
|
|
26
|
-
│ ├── version.rb # バージョン定義
|
|
27
|
-
│ └── rbcsv.bundle # コンパイル済みネイティブ拡張(生成される)
|
|
28
|
-
├── ext/
|
|
29
|
-
│ └── rbcsv/
|
|
30
|
-
│ ├── src/
|
|
31
|
-
│ │ ├── lib.rs # Rust拡張のエントリーポイント、Magnus初期化
|
|
32
|
-
│ │ ├── parser.rs # CSV解析コア、CsvParseOptions定義
|
|
33
|
-
│ │ ├── ruby_api.rs # Ruby APIバインディング、オプション処理
|
|
34
|
-
│ │ └── error.rs # エラーハンドリング
|
|
35
|
-
│ ├── Cargo.toml # Rust依存関係(Magnus 0.8.1使用)
|
|
36
|
-
│ └── extconf.rb # Ruby拡張ビルド設定
|
|
37
|
-
├── spec/
|
|
38
|
-
│ ├── rbcsv_spec.rb # メインのRubyテスト
|
|
39
|
-
│ └── spec_helper.rb # テスト設定
|
|
40
|
-
├── docs/ # ドキュメント
|
|
41
|
-
├── target/ # Rustビルド出力(git無視)
|
|
42
|
-
├── tmp/ # Ruby拡張ビルド中間ファイル(git無視)
|
|
43
|
-
├── *.gem # ビルド済みgemファイル
|
|
44
|
-
├── rbcsv.gemspec # Gem仕様
|
|
45
|
-
├── Rakefile # ビルドタスク(rb_sys使用)
|
|
46
|
-
├── Gemfile # Ruby依存関係
|
|
47
|
-
├── Cargo.toml # ワークスペース設定
|
|
48
|
-
├── CHANGELOG.md # 変更履歴
|
|
49
|
-
├── README.md # 使用法ガイド
|
|
50
|
-
└── DEVELOPMENT.md # このファイル
|
|
51
|
-
```
|
|
52
19
|
|
|
53
20
|
## 開発環境のセットアップ
|
|
54
21
|
|
|
@@ -77,16 +44,156 @@ bundle exec rake compile
|
|
|
77
44
|
|
|
78
45
|
### 4. 動作確認
|
|
79
46
|
|
|
47
|
+
#### CSV パース機能
|
|
48
|
+
|
|
80
49
|
```bash
|
|
81
|
-
#
|
|
82
|
-
ruby -I lib -e "require 'rbcsv'; p RbCsv.parse('a,b\n1,2'
|
|
50
|
+
# 基本的なパース
|
|
51
|
+
ruby -I lib -e "require 'rbcsv'; p RbCsv.parse('a,b\n1,2')"
|
|
83
52
|
# 期待される出力: [["a", "b"], ["1", "2"]]
|
|
84
53
|
|
|
85
|
-
#
|
|
86
|
-
ruby -I lib -e "require 'rbcsv'; p RbCsv.parse(' a , b \n 1 , 2 '
|
|
54
|
+
# trim機能付きパース
|
|
55
|
+
ruby -I lib -e "require 'rbcsv'; p RbCsv.parse!(' a , b \n 1 , 2 ')"
|
|
87
56
|
# 期待される出力: [["a", "b"], ["1", "2"]]
|
|
88
57
|
```
|
|
89
58
|
|
|
59
|
+
#### CSV ファイル読み込み機能
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# CSVファイル読み込み
|
|
63
|
+
ruby -I lib -e "require 'rbcsv'; p RbCsv.read('spec/fixtures/test.csv')"
|
|
64
|
+
# 期待される出力: [["name", "age", "city"], ["Alice", "25", "Tokyo"], ...]
|
|
65
|
+
|
|
66
|
+
# trim機能付きファイル読み込み
|
|
67
|
+
ruby -I lib -e "require 'rbcsv'; p RbCsv.read!('spec/fixtures/test_with_spaces.csv')"
|
|
68
|
+
# 期待される出力: [["name", "age", "city"], ["Alice", "25", "Tokyo"], ...]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
#### CSV ファイル書き込み機能
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# 基本的なファイル書き込み
|
|
75
|
+
ruby -I lib -e "
|
|
76
|
+
require 'rbcsv'
|
|
77
|
+
data = [['name', 'age', 'city'], ['Alice', '25', 'Tokyo'], ['Bob', '30', 'Osaka']]
|
|
78
|
+
RbCsv.write('/tmp/test_output.csv', data)
|
|
79
|
+
puts 'File written successfully!'
|
|
80
|
+
puts File.read('/tmp/test_output.csv')
|
|
81
|
+
"
|
|
82
|
+
# 期待される出力:
|
|
83
|
+
# File written successfully!
|
|
84
|
+
# name,age,city
|
|
85
|
+
# Alice,25,Tokyo
|
|
86
|
+
# Bob,30,Osaka
|
|
87
|
+
|
|
88
|
+
#### CSV 型認識機能
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 基本的な型認識パース
|
|
92
|
+
ruby -I lib -e "
|
|
93
|
+
require 'rbcsv'
|
|
94
|
+
csv_data = 'name,age,score\nAlice,25,85.5\nBob,30,92'
|
|
95
|
+
result = RbCsv.parse_typed(csv_data)
|
|
96
|
+
p result
|
|
97
|
+
puts \"Age type: #{result[1][1].class}\"
|
|
98
|
+
puts \"Score type: #{result[1][2].class}\"
|
|
99
|
+
"
|
|
100
|
+
# 期待される出力:
|
|
101
|
+
# [["name", "age", "score"], ["Alice", 25, 85.5], ["Bob", 30, 92]]
|
|
102
|
+
# Age type: Integer
|
|
103
|
+
# Score type: Float
|
|
104
|
+
|
|
105
|
+
# trim機能付き型認識パース
|
|
106
|
+
ruby -I lib -e "
|
|
107
|
+
require 'rbcsv'
|
|
108
|
+
csv_data = ' name , age , score \n Alice , 25 , 85.5 '
|
|
109
|
+
result = RbCsv.parse_typed!(csv_data)
|
|
110
|
+
p result
|
|
111
|
+
puts \"Age type: #{result[1][1].class}\"
|
|
112
|
+
"
|
|
113
|
+
# 期待される出力:
|
|
114
|
+
# [["name", "age", "score"], ["Alice", 25, 85.5]]
|
|
115
|
+
# Age type: Integer
|
|
116
|
+
|
|
117
|
+
# 型認識ファイル読み込み
|
|
118
|
+
ruby -I lib -e "
|
|
119
|
+
require 'rbcsv'
|
|
120
|
+
result = RbCsv.read_typed('spec/fixtures/test.csv')
|
|
121
|
+
p result[1] # 2行目のデータ
|
|
122
|
+
puts \"Age type: #{result[1][1].class}\"
|
|
123
|
+
"
|
|
124
|
+
# 期待される出力: ["Alice", 25, "Tokyo"] (ageが数値型)
|
|
125
|
+
|
|
126
|
+
# 型認識の詳細テスト
|
|
127
|
+
ruby -I lib -e "
|
|
128
|
+
require 'rbcsv'
|
|
129
|
+
test_data = 'type,value\ninteger,123\nfloat,45.6\nscientific,1.23e-4\nstring,hello\nempty,'
|
|
130
|
+
result = RbCsv.parse_typed(test_data)
|
|
131
|
+
result.each_with_index do |row, i|
|
|
132
|
+
next if i == 0 # ヘッダーをスキップ
|
|
133
|
+
value = row[1]
|
|
134
|
+
puts \"#{row[0]}: #{value.inspect} (#{value.class})\"
|
|
135
|
+
end
|
|
136
|
+
"
|
|
137
|
+
# 期待される出力:
|
|
138
|
+
# integer: 123 (Integer)
|
|
139
|
+
# float: 45.6 (Float)
|
|
140
|
+
# scientific: 0.000123 (Float)
|
|
141
|
+
# string: "hello" (String)
|
|
142
|
+
# empty: "" (String)
|
|
143
|
+
|
|
144
|
+
# 書き込み→読み込みの往復テスト
|
|
145
|
+
ruby -I lib -e "
|
|
146
|
+
require 'rbcsv'
|
|
147
|
+
data = [['product', 'price'], ['Apple', '100'], ['Orange', '80']]
|
|
148
|
+
RbCsv.write('/tmp/roundtrip.csv', data)
|
|
149
|
+
result = RbCsv.read('/tmp/roundtrip.csv')
|
|
150
|
+
puts 'Original data:'
|
|
151
|
+
p data
|
|
152
|
+
puts 'Read back data:'
|
|
153
|
+
p result
|
|
154
|
+
puts 'Match: #{data == result}'
|
|
155
|
+
"
|
|
156
|
+
# 期待される出力: Match: true
|
|
157
|
+
|
|
158
|
+
# エラーハンドリングテスト(空データ)
|
|
159
|
+
ruby -I lib -e "
|
|
160
|
+
require 'rbcsv'
|
|
161
|
+
begin
|
|
162
|
+
RbCsv.write('/tmp/empty.csv', [])
|
|
163
|
+
rescue => e
|
|
164
|
+
puts 'Error caught: #{e.message}'
|
|
165
|
+
end
|
|
166
|
+
"
|
|
167
|
+
# 期待される出力: Error caught: Invalid Data Error: CSV data is empty
|
|
168
|
+
|
|
169
|
+
# エラーハンドリングテスト(フィールド数不一致)
|
|
170
|
+
ruby -I lib -e "
|
|
171
|
+
require 'rbcsv'
|
|
172
|
+
begin
|
|
173
|
+
data = [['name', 'age'], ['Alice', '25', 'Tokyo']]
|
|
174
|
+
RbCsv.write('/tmp/mismatch.csv', data)
|
|
175
|
+
rescue => e
|
|
176
|
+
puts 'Error caught: #{e.message}'
|
|
177
|
+
end
|
|
178
|
+
"
|
|
179
|
+
# 期待される出力: Error caught: Invalid Data Error: Field count mismatch at line 2: expected 2 fields, got 3 fields
|
|
180
|
+
|
|
181
|
+
# ファイル上書きテスト
|
|
182
|
+
ruby -I lib -e "
|
|
183
|
+
require 'rbcsv'
|
|
184
|
+
# 最初のデータを書き込み
|
|
185
|
+
RbCsv.write('/tmp/overwrite_test.csv', [['old'], ['data']])
|
|
186
|
+
puts 'First write:'
|
|
187
|
+
puts File.read('/tmp/overwrite_test.csv')
|
|
188
|
+
|
|
189
|
+
# 新しいデータで上書き
|
|
190
|
+
RbCsv.write('/tmp/overwrite_test.csv', [['new', 'data'], ['updated', 'content']])
|
|
191
|
+
puts 'After overwrite:'
|
|
192
|
+
puts File.read('/tmp/overwrite_test.csv')
|
|
193
|
+
"
|
|
194
|
+
# 期待される出力: 最初にold,dataが出力され、その後new,data形式に変わる
|
|
195
|
+
```
|
|
196
|
+
|
|
90
197
|
## ビルドプロセス
|
|
91
198
|
|
|
92
199
|
### 自動ビルド(推奨)
|
|
@@ -184,10 +291,10 @@ cd ../..
|
|
|
184
291
|
|
|
185
292
|
```bash
|
|
186
293
|
# ベンチマーク実行
|
|
187
|
-
ruby benchmark.rb
|
|
294
|
+
ruby examples/benchmarks/benchmark.rb
|
|
188
295
|
|
|
189
296
|
# カスタムテストファイルでのテスト
|
|
190
|
-
ruby
|
|
297
|
+
ruby examples/basic/basic_usage.rb
|
|
191
298
|
```
|
|
192
299
|
|
|
193
300
|
### コードスタイルチェック
|
|
@@ -205,28 +312,116 @@ cd ../..
|
|
|
205
312
|
|
|
206
313
|
## API設計
|
|
207
314
|
|
|
208
|
-
### 現在のAPI(v0.1.
|
|
315
|
+
### 現在のAPI(v0.1.7+)
|
|
209
316
|
|
|
210
317
|
```ruby
|
|
211
|
-
#
|
|
212
|
-
|
|
213
|
-
|
|
318
|
+
# 関数ベースAPI(`!`サフィックスでtrim機能分離)
|
|
319
|
+
|
|
320
|
+
# 文字列専用パース(従来型)
|
|
321
|
+
RbCsv.parse(csv_string) # 通常のパース(すべて文字列)
|
|
322
|
+
RbCsv.parse!(csv_string) # trim機能付きパース(すべて文字列)
|
|
323
|
+
|
|
324
|
+
# 型認識パース(新機能 v0.1.8+)
|
|
325
|
+
RbCsv.parse_typed(csv_string) # 数値を数値型として返す
|
|
326
|
+
RbCsv.parse_typed!(csv_string) # trim + 数値を数値型として返す
|
|
327
|
+
|
|
328
|
+
# ファイル読み込み
|
|
329
|
+
RbCsv.read(file_path) # 通常のファイル読み込み(すべて文字列)
|
|
330
|
+
RbCsv.read!(file_path) # trim機能付きファイル読み込み(すべて文字列)
|
|
331
|
+
RbCsv.read_typed(file_path) # 型認識ファイル読み込み
|
|
332
|
+
RbCsv.read_typed!(file_path) # trim + 型認識ファイル読み込み
|
|
214
333
|
|
|
215
|
-
#
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
334
|
+
# ファイル書き込み
|
|
335
|
+
RbCsv.write(file_path, data) # CSVファイル書き込み
|
|
336
|
+
|
|
337
|
+
# データ形式
|
|
338
|
+
# 従来型(すべて文字列)
|
|
339
|
+
string_data = [
|
|
340
|
+
["header1", "header2", "header3"], # ヘッダー行
|
|
341
|
+
["value1", "value2", "value3"], # データ行(すべて文字列)
|
|
342
|
+
# ...
|
|
343
|
+
]
|
|
344
|
+
|
|
345
|
+
# 型認識版(数値は数値型)
|
|
346
|
+
typed_data = [
|
|
347
|
+
["name", "age", "score"], # ヘッダー行(文字列)
|
|
348
|
+
["Alice", 25, 85.5], # データ行(文字列, 整数, 浮動小数点)
|
|
349
|
+
["Bob", 30, 92], # データ行(文字列, 整数, 整数)
|
|
350
|
+
# ...
|
|
351
|
+
]
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### API進化の履歴
|
|
355
|
+
|
|
356
|
+
#### v0.1.6以前(オプションベース)
|
|
357
|
+
```ruby
|
|
358
|
+
RbCsv.parse(csv_string, {trim: true})
|
|
359
|
+
RbCsv.read(file_path, {trim: false})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### v0.1.7+(関数ベース)
|
|
363
|
+
```ruby
|
|
364
|
+
RbCsv.parse!(csv_string) # trim版
|
|
365
|
+
RbCsv.write(file_path, data) # 新機能
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### v0.1.8+(型認識機能追加)
|
|
369
|
+
```ruby
|
|
370
|
+
RbCsv.parse_typed(csv_string) # 数値型自動変換
|
|
371
|
+
RbCsv.parse_typed!(csv_string) # trim + 数値型自動変換
|
|
372
|
+
RbCsv.read_typed(file_path) # ファイル読み込み + 数値型自動変換
|
|
373
|
+
RbCsv.read_typed!(file_path) # trim + ファイル読み込み + 数値型自動変換
|
|
222
374
|
```
|
|
223
375
|
|
|
224
376
|
### 実装アーキテクチャ
|
|
225
377
|
|
|
226
|
-
1. **parser.rs**:
|
|
227
|
-
2. **
|
|
228
|
-
3. **
|
|
229
|
-
4. **
|
|
378
|
+
1. **parser.rs**: CSV解析・書き込みコア機能、エラーハンドリング
|
|
379
|
+
2. **value.rs**: CSV値の型定義(CsvValue enum)と型変換ロジック
|
|
380
|
+
3. **ruby_api.rs**: Ruby API関数、Magnus バインディング
|
|
381
|
+
4. **lib.rs**: Magnus初期化と関数登録
|
|
382
|
+
5. **error.rs**: 包括的なエラーハンドリングとRuby例外変換
|
|
383
|
+
|
|
384
|
+
#### モジュール詳細
|
|
385
|
+
|
|
386
|
+
**parser.rs**
|
|
387
|
+
- 文字列専用関数: `parse_csv_core`, `parse_csv_file`, `write_csv_file`
|
|
388
|
+
- 型認識関数: `parse_csv_typed`, `parse_csv_file_typed`
|
|
389
|
+
|
|
390
|
+
**value.rs**
|
|
391
|
+
- `CsvValue` enum: Integer(i64), Float(f64), String(String)
|
|
392
|
+
- 型変換メソッド: `from_str`, `from_str_trimmed`, `to_ruby`
|
|
393
|
+
- 優先順位: 整数 → 浮動小数点 → 文字列
|
|
394
|
+
|
|
395
|
+
**ruby_api.rs**
|
|
396
|
+
- 文字列版: `parse`, `parse_trim`, `read`, `read_trim`, `write`
|
|
397
|
+
- 型認識版: `parse_typed`, `parse_typed_trim`, `read_typed`, `read_typed_trim`
|
|
398
|
+
|
|
399
|
+
### 開発時の重要な注意点
|
|
400
|
+
|
|
401
|
+
#### Ruby拡張ライブラリの特殊性
|
|
402
|
+
|
|
403
|
+
```bash
|
|
404
|
+
# ❌ 避けるべき: cargo buildは直接使用しない
|
|
405
|
+
# cargo build は通常のRustライブラリ用で、Ruby拡張では適切にリンクされない
|
|
406
|
+
|
|
407
|
+
# ✅ 推奨される開発フロー:
|
|
408
|
+
cd ext/rbcsv
|
|
409
|
+
cargo check # 構文チェック(リンクなし)
|
|
410
|
+
cargo test # Rust単体テスト
|
|
411
|
+
cd ../..
|
|
412
|
+
bundle exec rake compile # Ruby拡張ビルド
|
|
413
|
+
bundle exec rspec # Ruby統合テスト
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
#### ビルドコマンドの使い分け
|
|
417
|
+
|
|
418
|
+
| コマンド | 用途 | 場所 | 備考 |
|
|
419
|
+
|---------|------|------|------|
|
|
420
|
+
| `cargo check` | 構文・型チェック | ext/rbcsv | 高速、リンクなし |
|
|
421
|
+
| `cargo test` | Rust単体テスト | ext/rbcsv | Rubyシンボル不要 |
|
|
422
|
+
| `cargo build` | **使用不可** | - | リンクエラーが発生 |
|
|
423
|
+
| `bundle exec rake compile` | Ruby拡張ビルド | プロジェクトルート | 本番用ビルド |
|
|
424
|
+
| `bundle exec rspec` | 統合テスト | プロジェクトルート | 完全な機能テスト |
|
|
230
425
|
|
|
231
426
|
## リリース手順
|
|
232
427
|
|
|
@@ -361,9 +556,18 @@ cargo doc --open
|
|
|
361
556
|
### テスト戦略
|
|
362
557
|
|
|
363
558
|
1. **単体テスト**: 各Rustモジュールに対するcargo test
|
|
559
|
+
- `value.rs`: 型変換ロジックの単体テスト
|
|
560
|
+
- `parser.rs`: CSV解析・型認識機能の単体テスト
|
|
364
561
|
2. **統合テスト**: Ruby APIレベルでのRSpecテスト
|
|
562
|
+
- 基本的なCSV処理機能
|
|
563
|
+
- 型認識機能(parse_typed, read_typed系)
|
|
564
|
+
- エラーハンドリング
|
|
365
565
|
3. **パフォーマンステスト**: 大きなCSVファイルでのベンチマーク
|
|
366
566
|
4. **エッジケーステスト**: 不正なCSV、空ファイル、エンコーディング問題
|
|
567
|
+
5. **型認識テスト**: 数値文字列の正確な型変換
|
|
568
|
+
- 整数、浮動小数点、科学記法
|
|
569
|
+
- 混在型のCSVデータ
|
|
570
|
+
- trim機能との組み合わせ
|
|
367
571
|
|
|
368
572
|
### デバッグ
|
|
369
573
|
|
|
@@ -488,4 +692,4 @@ cd ../..
|
|
|
488
692
|
- [rb_sys Documentation](https://docs.rs/rb_sys/)
|
|
489
693
|
- [Ruby Extension Guide](https://docs.ruby-lang.org/en/master/extension_rdoc.html)
|
|
490
694
|
- [Cargo Book](https://doc.rust-lang.org/cargo/)
|
|
491
|
-
- [RubyGems Guides](https://guides.rubygems.org/)
|
|
695
|
+
- [RubyGems Guides](https://guides.rubygems.org/)
|
data/README.md
CHANGED
|
@@ -1,43 +1,126 @@
|
|
|
1
|
-
#
|
|
1
|
+
# RbCsv
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/r_csv`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
3
|
+
Fast CSV library for Ruby powered by Rust.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Install the gem and add to the application's Gemfile by executing:
|
|
7
|
+
Add this line to your Gemfile:
|
|
12
8
|
|
|
13
|
-
```
|
|
14
|
-
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'rbcsv'
|
|
15
11
|
```
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
Or install directly:
|
|
18
14
|
|
|
19
15
|
```bash
|
|
20
|
-
gem install
|
|
16
|
+
gem install rbcsv
|
|
21
17
|
```
|
|
22
18
|
|
|
23
19
|
## Usage
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
```ruby
|
|
22
|
+
require 'rbcsv'
|
|
23
|
+
|
|
24
|
+
# Parse CSV string
|
|
25
|
+
csv_data = "name,age,city\nAlice,25,Tokyo\nBob,30,Osaka"
|
|
26
|
+
result = RbCsv.parse(csv_data)
|
|
27
|
+
# => [["name", "age", "city"], ["Alice", "25", "Tokyo"], ["Bob", "30", "Osaka"]]
|
|
28
|
+
|
|
29
|
+
# Parse with whitespace trimming
|
|
30
|
+
result = RbCsv.parse!(" name , age \n Alice , 25 ")
|
|
31
|
+
# => [["name", "age"], ["Alice", "25"]]
|
|
32
|
+
|
|
33
|
+
# Read from file
|
|
34
|
+
result = RbCsv.read("data.csv")
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
# Write to file
|
|
37
|
+
data = [["name", "age"], ["Alice", "25"], ["Bob", "30"]]
|
|
38
|
+
RbCsv.write("output.csv", data)
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
# Type-aware parsing (converts numbers automatically)
|
|
41
|
+
result = RbCsv.parse_typed("name,age,score\nAlice,25,85.5")
|
|
42
|
+
# => [["name", "age", "score"], ["Alice", 25, 85.5]]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API Reference
|
|
46
|
+
|
|
47
|
+
### Basic Methods
|
|
48
|
+
- `RbCsv.parse(string)` - Parse CSV string
|
|
49
|
+
- `RbCsv.parse!(string)` - Parse with trimming
|
|
50
|
+
- `RbCsv.read(filepath)` - Read CSV file
|
|
51
|
+
- `RbCsv.read!(filepath)` - Read with trimming
|
|
52
|
+
- `RbCsv.write(filepath, data)` - Write CSV file
|
|
53
|
+
|
|
54
|
+
### Type-aware Methods
|
|
55
|
+
- `RbCsv.parse_typed(string)` - Parse with type conversion
|
|
56
|
+
- `RbCsv.parse_typed!(string)` - Parse with trimming and type conversion
|
|
57
|
+
- `RbCsv.read_typed(filepath)` - Read with type conversion
|
|
58
|
+
- `RbCsv.read_typed!(filepath)` - Read with trimming and type conversion
|
|
59
|
+
|
|
60
|
+
## Benchmark
|
|
61
|
+
|
|
62
|
+
Currently, we achieve 2.4 to 3.8 times faster processing for parse operations, with even greater speed improvements for type conversion.
|
|
63
|
+
|
|
64
|
+
Compared to casting to arbitrary forms using Ruby methods, parse_typed with pre-defined type conversion delivers approximately 140 times faster results.
|
|
65
|
+
|
|
66
|
+
*exec data 2025/10/04*
|
|
67
|
+
```sh
|
|
68
|
+
% ruby examples/benchmarks/benchmark.rb
|
|
69
|
+
|
|
70
|
+
file: bench.csv
|
|
71
|
+
file size: 16457 bytes
|
|
72
|
+
recode: 100
|
|
73
|
+
🚀 parse (1000 times)
|
|
74
|
+
--------------------------------------------------
|
|
75
|
+
user system total real
|
|
76
|
+
Ruby CSV.parse 0.258438 0.002993 0.261431 ( 0.261500)
|
|
77
|
+
Ruby CSV.parse (headers: true) 0.329098 0.001348 0.330446 ( 0.330398)
|
|
78
|
+
RbCsv.parse 0.085052 0.000358 0.085410 ( 0.085403)
|
|
79
|
+
RbCsv.parse! (with trim) 0.112470 0.000246 0.112716 ( 0.112703)
|
|
80
|
+
RbCsv.parse_typed 0.096728 0.000512 0.097240 ( 0.097227)
|
|
81
|
+
RbCsv.parse_typed! (typed + trim) 0.128616 0.000478 0.129094 ( 0.129075)
|
|
82
|
+
|
|
83
|
+
📁 read (1000 times)
|
|
84
|
+
--------------------------------------------------
|
|
85
|
+
user system total real
|
|
86
|
+
Ruby CSV.read 0.273029 0.030768 0.303797 ( 0.398752)
|
|
87
|
+
Ruby CSV.read (headers: true) 0.360198 0.027133 0.387331 ( 0.478200)
|
|
88
|
+
RbCsv.read 0.088287 0.021659 0.109946 ( 0.169075)
|
|
89
|
+
RbCsv.read! (with trim) 0.119157 0.016301 0.135458 ( 0.149894)
|
|
90
|
+
RbCsv.read_typed 0.105971 0.016317 0.122288 ( 0.136625)
|
|
91
|
+
RbCsv.read_typed! (typed + trim) 0.137821 0.017739 0.155560 ( 0.174861)
|
|
92
|
+
|
|
93
|
+
✏️ write (1000 times)
|
|
94
|
+
--------------------------------------------------
|
|
95
|
+
user system total real
|
|
96
|
+
Ruby CSV.open (write) 0.409897 0.355344 0.765241 ( 1.894642)
|
|
97
|
+
RbCsv.write 0.097875 0.505652 0.603527 ( 1.595586)
|
|
98
|
+
|
|
99
|
+
test data createing
|
|
100
|
+
ccreated: large_sample.csv (831947 bytes)
|
|
101
|
+
size: 831947 bytes
|
|
102
|
+
|
|
103
|
+
🔢 parse_typed (1000 times )
|
|
104
|
+
--------------------------------------------------
|
|
105
|
+
user system total real
|
|
106
|
+
Manual conversion (CSV) 6.093143 0.011365 6.104508 ( 6.104026)
|
|
107
|
+
Manual conversion (RbCsv) 6.217609 0.023237 6.240846 ( 6.287587)
|
|
108
|
+
Automatic conversion (RbCsv typed) 0.000041 0.000001 0.000042 ( 0.000041)
|
|
109
|
+
```
|
|
30
110
|
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
32
111
|
|
|
33
112
|
## Contributing
|
|
34
113
|
|
|
35
|
-
|
|
114
|
+
Found a bug or have a suggestion? Please open an issue on [GitHub](https://github.com/fujitanisora/r_csv/issues).
|
|
36
115
|
|
|
37
|
-
|
|
116
|
+
Pull requests are welcome! For major changes, please open an issue first to discuss what you would like to change.
|
|
117
|
+
|
|
118
|
+
## Author
|
|
38
119
|
|
|
39
|
-
|
|
120
|
+
**Fujitani Sora**
|
|
121
|
+
📧 fujitanisora0414@gmail.com
|
|
122
|
+
🐙 [@fujitanisora](https://github.com/fujitanisora)
|
|
40
123
|
|
|
41
|
-
##
|
|
124
|
+
## License
|
|
42
125
|
|
|
43
|
-
|
|
126
|
+
MIT License. See [LICENSE](https://opensource.org/licenses/MIT) for details.
|