rbcsv 0.1.3 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7642f2718a3887f7f1c93d81555b354f80b9324822a6da7efc1f877f7381f66
4
- data.tar.gz: 2a704520e1dcccb552d378708ac27b8bd99a22a8afa28ac372bb35512ba26d43
3
+ metadata.gz: 502b63a87094fd5315834ce7772b29e6debaf6b18b48a88f60eae202ffd008ec
4
+ data.tar.gz: d56609a5352b69b365e87cc0bc6106a579d34927e919db0dfa8bc22228d14620
5
5
  SHA512:
6
- metadata.gz: 254312f4b7239085910f4ef853586ce5fc42ec80b5bb435819f68dc5c8e143511a9db80227b970f153d019bc02facf5f2043b97747b55d312794a1c91bd71d26
7
- data.tar.gz: 74fbec2b486933f924f480cc6c3dc06d748616cdadc12b371d5e48f428346b6b4562b63f5b9c53c32c0bfb09df9091a8e50a2e9d73b09c433e322e496ef40a52
6
+ metadata.gz: b1e2e790b9a546cad7dfa343bb32feacc590f369f4f16fa736ea63147ec1781a6107a3bf1120bcd360af3fa907459409bd3b92b0d55b7af39cd027e81aaee49d
7
+ data.tar.gz: 2ffea00a8972c26615e7bc58da5f77c6b2d7b6c1de15f31d0b064b0a2e1b3994b933c2b23297a219e2cf9c78a95ecde7694d67aa4369b4785dd091c9a64b8ba8
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.6
1
+ 3.4.5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.6] - 2025-09-27
4
+
5
+ ### Changed
6
+ - Version bump for gem release
7
+
8
+ ## [0.1.5] - 2025-09-27
9
+
10
+ ### Fixed
11
+ - Fixed Magnus init function compatibility with version 0.6
12
+ - Added missing rb-sys dependency for proper Ruby header linking
13
+ - Fixed module name inconsistency in RSpec tests (RCsv → RbCsv)
14
+
15
+ ### Changed
16
+ - Updated Magnus initialization to use `#[magnus::init]` attribute
17
+ - Improved error handling with Magnus::Error type
18
+ - Cleaned up unused imports
19
+
20
+ ### Added
21
+ - Documentation for AI-driven gem version upgrade process
22
+ - Tests for parse_with_trim functionality
23
+
24
+ ## [0.1.4] - 2025-09-20
25
+
26
+ - **Fixed**: CSV parse method returning empty array due to incorrect header handling
27
+ - **Fixed**: CSV reader now processes all rows instead of skipping header row
28
+ - **Improved**: Added proper test coverage for simple CSV parsing
29
+
30
+ ## [0.1.3] - 2025-09-20
31
+
32
+ - Internal version bump
33
+
34
+ ## [0.1.2] - 2025-09-20
35
+
36
+ - Internal version bump
37
+
3
38
  ## [0.1.1] - 2025-09-20
4
39
 
5
40
  - Fixed Cargo workspace configuration
data/Cargo.lock CHANGED
@@ -238,6 +238,7 @@ version = "0.1.0"
238
238
  dependencies = [
239
239
  "csv",
240
240
  "magnus",
241
+ "rb-sys",
241
242
  ]
242
243
 
243
244
  [[package]]
data/DEVELOPMENT.md ADDED
@@ -0,0 +1,247 @@
1
+ # 開発ガイド
2
+
3
+ このドキュメントでは、rbcsvの開発環境のセットアップ、ビルド方法、リリース手順について説明します。
4
+
5
+ ## 必要な環境
6
+
7
+ - Ruby 3.0以降
8
+ - Rust(最新の安定版を推奨)
9
+ - Bundler gem
10
+ - Git
11
+
12
+ ## 開発環境のセットアップ
13
+
14
+ ### 1. リポジトリのクローン
15
+
16
+ ```bash
17
+ git clone <repository-url>
18
+ cd r_csv
19
+ ```
20
+
21
+ ### 2. 依存関係のインストール
22
+
23
+ ```bash
24
+ bundle install
25
+ ```
26
+
27
+ ### 3. ネイティブ拡張のビルド
28
+
29
+ ```bash
30
+ rake compile
31
+ ```
32
+
33
+ ### 4. テストの実行
34
+
35
+ #### Rubyテスト
36
+ ```bash
37
+ bundle exec rspec
38
+ ```
39
+
40
+ #### Rustテスト
41
+ ```bash
42
+ cd ext/rbcsv
43
+ cargo test
44
+ cd ../..
45
+ ```
46
+
47
+ ### 5. 動作確認テスト
48
+
49
+ ```bash
50
+ ruby -I lib -e "require 'rbcsv'; p RbCsv.parse('a,b\n1,2')"
51
+ ```
52
+
53
+ 期待される出力: `[["a", "b"], ["1", "2"]]`
54
+
55
+ ## プロジェクト構成
56
+
57
+ ```
58
+ r_csv/
59
+ ├── lib/
60
+ │ ├── rbcsv.rb # メインのRubyモジュール
61
+ │ └── rbcsv/
62
+ │ ├── version.rb # バージョン定義
63
+ │ └── rbcsv.bundle # コンパイル済みネイティブ拡張
64
+ ├── ext/
65
+ │ └── rbcsv/
66
+ │ ├── src/
67
+ │ │ └── lib.rs # Rust実装
68
+ │ ├── Cargo.toml # Rust依存関係
69
+ │ └── extconf.rb # Ruby拡張の設定
70
+ ├── spec/ # Rubyテスト
71
+ ├── rbcsv.gemspec # Gem仕様
72
+ ├── Rakefile # ビルドタスク
73
+ └── DEVELOPMENT.md # このファイル
74
+ ```
75
+
76
+ ## ビルドプロセス
77
+
78
+ このプロジェクトは、`rb_sys`クレートとRubyの拡張メカニズムを通じてコンパイルされるRustベースのネイティブ拡張を使用しています。
79
+
80
+ ### 手動ビルド手順
81
+
82
+ 1. **前回のビルドをクリーン**(必要に応じて):
83
+ ```bash
84
+ rm -rf lib/rbcsv/rbcsv.bundle tmp/
85
+ ```
86
+
87
+ 2. **拡張のコンパイル**:
88
+ ```bash
89
+ rake compile
90
+ ```
91
+
92
+ 3. **代替ビルド方法**(rakeが失敗する場合):
93
+ ```bash
94
+ cd ext/rbcsv
95
+ cargo build --release
96
+ cp ../../target/release/librbcsv.dylib ../../lib/rbcsv/rbcsv.bundle
97
+ cd ../..
98
+ ```
99
+
100
+ ### ビルドのトラブルシューティング
101
+
102
+ #### ABIバージョンの不一致
103
+ 「incompatible ABI version」エラーが発生した場合:
104
+
105
+ 1. ビルドと実行で同じRubyバージョンを使用していることを確認
106
+ 2. クリーンして再ビルド:
107
+ ```bash
108
+ rm -rf lib/rbcsv/rbcsv.bundle tmp/
109
+ rake compile
110
+ ```
111
+
112
+ #### Rubyバージョンの競合
113
+ 開発版Ruby(3.5.0devなど)を使用する場合、プリコンパイル済みのgemは動作しません。必ずソースから再ビルドしてください。
114
+
115
+ ## リリース手順
116
+
117
+ ### 1. バージョンの更新
118
+
119
+ `lib/rbcsv/version.rb`を編集:
120
+ ```ruby
121
+ module RbCsv
122
+ VERSION = "x.y.z" # バージョン番号を更新
123
+ end
124
+ ```
125
+
126
+ ### 2. CHANGELOG.mdの更新
127
+
128
+ リリース用の新しいセクションを追加:
129
+ ```markdown
130
+ ## [x.y.z] - YYYY-MM-DD
131
+
132
+ - **修正**: バグ修正の説明
133
+ - **追加**: 新機能の説明
134
+ - **変更**: 変更点の説明
135
+ - **削除**: 削除された機能の説明
136
+ ```
137
+
138
+ ### 3. ビルドとテスト
139
+
140
+ ```bash
141
+ # クリーンビルド
142
+ rm -rf lib/rbcsv/rbcsv.bundle tmp/
143
+
144
+ # 拡張の再ビルド
145
+ rake compile
146
+
147
+ # 機能テスト
148
+ ruby -I lib -e "require 'rbcsv'; p RbCsv.parse('a,b\n1,2')"
149
+
150
+ # テストスイートの実行
151
+ bundle exec rspec
152
+ ```
153
+
154
+ ### 4. Gemのビルド
155
+
156
+ ```bash
157
+ gem build rbcsv.gemspec
158
+ ```
159
+
160
+ これにより、現在のディレクトリに`rbcsv-x.y.z.gem`が作成されます。
161
+
162
+ ### 5. 変更のコミット
163
+
164
+ ```bash
165
+ git add -A
166
+ git commit -m "Release vx.y.z
167
+
168
+ - 主な変更点の簡潔な説明
169
+ - 重要な改善や修正のリスト"
170
+ ```
171
+
172
+ ### 6. Gitタグの作成
173
+
174
+ ```bash
175
+ git tag vx.y.z
176
+ ```
177
+
178
+ ### 7. リポジトリへのプッシュ
179
+
180
+ ```bash
181
+ git push origin main
182
+ git push origin vx.y.z
183
+ ```
184
+
185
+ ### 8. Gemの公開(オプション)
186
+
187
+ RubyGems.orgに公開する場合:
188
+
189
+ ```bash
190
+ gem push rbcsv-x.y.z.gem
191
+ ```
192
+
193
+ **注意**: RubyGems.orgの適切な認証情報が設定されていることを確認してください。
194
+
195
+ ## 開発のヒント
196
+
197
+ ### コードスタイル
198
+
199
+ - 標準的なRubyとRustのフォーマット規約に従う
200
+ - Rustコードのフォーマットには`cargo fmt`を使用
201
+ - 適切なRubyリンティングツールを使用
202
+
203
+ ### テスト
204
+
205
+ - 新機能のテストを`spec/`に追加
206
+ - Rustユニットテストを`ext/rbcsv/src/lib.rs`に追加
207
+ - コミット前にすべてのテストが通ることを確認
208
+
209
+ ### デバッグ
210
+
211
+ Rustコードにデバッグ出力を追加する(開発ビルド用):
212
+
213
+ ```rust
214
+ #[cfg(not(test))]
215
+ eprintln!("デバッグ情報: {:?}", variable);
216
+ ```
217
+
218
+ これはテスト以外のビルドでのみ出力され、本番環境には影響しません。
219
+
220
+ ## よくある問題
221
+
222
+ ### 拡張が読み込めない
223
+
224
+ 1. 正しいRubyバージョン用に拡張がビルドされているか確認
225
+ 2. 拡張ファイルの存在を確認: `lib/rbcsv/rbcsv.bundle`
226
+ 3. 再ビルドを試す: `rake compile`
227
+
228
+ ### 空配列が返される
229
+
230
+ これはv0.1.4で修正された既知の問題です。CSVリーダーで`has_headers(false)`設定を使用した最新バージョンを使用していることを確認してください。
231
+
232
+ ### ビルドの失敗
233
+
234
+ 1. Rustがインストールされ、最新であることを確認
235
+ 2. Ruby開発ヘッダーが利用可能であることを確認
236
+ 3. ビルドキャッシュをクリア: `rm -rf tmp/`
237
+
238
+ ## コントリビューション
239
+
240
+ 1. リポジトリをフォーク
241
+ 2. 機能ブランチを作成
242
+ 3. 変更を実施
243
+ 4. 新機能のテストを追加
244
+ 5. すべてのテストが通ることを確認
245
+ 6. プルリクエストを送信
246
+
247
+ コードは既存のスタイルに従い、適切なテストを含めてください。
data/ext/rbcsv/Cargo.toml CHANGED
@@ -12,3 +12,4 @@ crate-type = ["cdylib"]
12
12
  [dependencies]
13
13
  csv = "1.3.1"
14
14
  magnus = { version = "0.6.2" }
15
+ rb-sys = { version = "0.9", features = ["link-ruby"] }
data/ext/rbcsv/src/lib.rs CHANGED
@@ -1,35 +1,92 @@
1
- #[cfg(not(test))]
2
- use magnus::{Error, exception, function, prelude::*, Ruby};
1
+ use core::fmt;
3
2
 
4
- #[cfg(test)]
5
- type Error = Box<dyn std::error::Error>;
3
+ use std::error::Error as StdError;
4
+
5
+ use magnus::{Error as MagnusError, Object};
6
+
7
+ #[derive(Debug)]
8
+ struct CustomError {
9
+ message: String,
10
+ kind: ErrorKind,
11
+ }
12
+
13
+ #[derive(Debug)]
14
+ enum ErrorKind {
15
+ Io,
16
+ _Parse,
17
+ _Network,
18
+ _Other,
19
+ }
20
+
21
+ impl fmt::Display for CustomError {
22
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23
+ write!(f, "{:?}: {}", self.kind, self.message)
24
+ }
25
+ }
26
+
27
+ impl StdError for CustomError {}
28
+
29
+ impl CustomError {
30
+ fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
31
+ CustomError {
32
+ message: message.into(),
33
+ kind,
34
+ }
35
+ }
36
+ }
37
+
38
+ type Error = CustomError;
39
+
40
+ fn escape_sanitize(s: String) -> String {
41
+ s.replace("\\n", "\n")
42
+ .replace("\\r", "\r")
43
+ .replace("\\t", "\t")
44
+ .replace("\\\"", "\"")
45
+ .replace("\\\\", "\\")
46
+ }
47
+
48
+ fn parse(s: String) -> Result<Vec<Vec<String>>, MagnusError> {
49
+ parse_with_options(s, false) // デフォルトはトリムなし
50
+ .map_err(|e| MagnusError::new(magnus::exception::runtime_error(), e.to_string()))
51
+ }
52
+
53
+ fn parse_with_trim(s: String, trim_whitespace: bool) -> Result<Vec<Vec<String>>, MagnusError> {
54
+ parse_with_options(s, trim_whitespace)
55
+ .map_err(|e| MagnusError::new(magnus::exception::runtime_error(), e.to_string()))
56
+ }
57
+
58
+ fn parse_with_options(s: String, trim_whitespace: bool) -> Result<Vec<Vec<String>>, Error> {
59
+ // エスケープシーケンスを実際の文字に変換
60
+ let processed = escape_sanitize(s);
61
+
62
+ let mut reader = csv::ReaderBuilder::new()
63
+ .has_headers(false) // ヘッダーを無効にして、すべての行 を読み込む
64
+ .trim(if trim_whitespace { csv::Trim::All } else { csv::Trim::None })
65
+ .from_reader(processed.as_bytes());
6
66
 
7
- fn parse(s: String) -> Result<Vec<Vec<String>>, Error> {
8
- let mut reader = csv::Reader::from_reader(s.as_bytes());
9
67
  let mut records = Vec::new();
10
68
 
11
69
  for result in reader.records() {
12
70
  match result {
13
71
  Ok(record) => {
14
72
  let row: Vec<String> = record.iter().map(|field| field.to_string()).collect();
73
+ println!("row {:?}", row);
15
74
  records.push(row);
16
75
  }
17
- #[cfg(not(test))]
18
- Err(e) => return Err(Error::new(exception::runtime_error(), format!("CSV parse error: {}", e))),
19
- #[cfg(test)]
20
- Err(e) => return Err(Box::new(e)),
76
+ Err(_e) => {
77
+ return Err(Error::new(ErrorKind::Io, "File not found"));
78
+ }
21
79
  }
22
80
  }
23
81
 
24
82
  Ok(records)
25
83
  }
26
84
 
27
- #[cfg(not(test))]
28
85
  #[magnus::init]
29
- fn init(ruby: &Ruby) -> Result<(), Error> {
30
- let module = ruby.define_module("RbCsv")?;
31
- module.define_singleton_method("parse", function!(parse, 1))?;
32
- Ok(())
86
+ fn init() {
87
+ let module = magnus::define_module("RbCsv").expect("Failed to define module");
88
+ module.define_singleton_method("parse", magnus::function!(parse, 1)).expect("Failed to define parse method");
89
+ module.define_singleton_method("parse_with_trim", magnus::function!(parse_with_trim, 2)).expect("Failed to define parse_with_trim method");
33
90
  }
34
91
 
35
92
  #[cfg(test)]
@@ -38,15 +95,55 @@ mod tests {
38
95
  use super::*;
39
96
 
40
97
  #[test]
41
- fn test_parse() {
42
- let csv_data = "name,age,city\nAlice,25,Tokyo\nBob,30,Osaka";
98
+ fn test_parse_simple() {
99
+ let csv_data = "a,b\n1,2";
43
100
  let result = parse(csv_data.to_string());
44
101
 
45
102
  assert!(result.is_ok());
46
103
 
47
104
  let records = result.unwrap();
48
105
  assert_eq!(records.len(), 2);
49
- assert_eq!(records[0], vec!["Alice", "25", "Tokyo"]);
50
- assert_eq!(records[1], vec!["Bob", "30", "Osaka"]);
106
+ assert_eq!(records[0], vec!["a", "b"]);
107
+ assert_eq!(records[1], vec!["1", "2"]);
108
+ }
109
+
110
+ #[test]
111
+ /// - シングルクォートの文字列のようなエスケープされた改行をテスト
112
+ fn test_parse_with_escaped_newline() {
113
+ let csv_data = "a,b\\n1,2";
114
+ let result = parse(csv_data.to_string());
115
+
116
+ assert!(result.is_ok());
117
+
118
+ let records = result.unwrap();
119
+ assert_eq!(records.len(), 2);
120
+ assert_eq!(records[0], vec!["a", "b"]);
121
+ assert_eq!(records[1], vec!["1", "2"]);
122
+ }
123
+
124
+ #[test]
125
+ fn test_parse_with_trim() {
126
+ let csv_data = " a , b \n 1 , 2 ";
127
+ let result = parse_with_trim(csv_data.to_string(), true);
128
+
129
+ assert!(result.is_ok());
130
+
131
+ let records = result.unwrap();
132
+ assert_eq!(records.len(), 2);
133
+ assert_eq!(records[0], vec!["a", "b"]);
134
+ assert_eq!(records[1], vec!["1", "2"]);
135
+ }
136
+
137
+ #[test]
138
+ fn test_parse_without_trim() {
139
+ let csv_data = " a , b \n 1 , 2 ";
140
+ let result = parse_with_trim(csv_data.to_string(), false);
141
+
142
+ assert!(result.is_ok());
143
+
144
+ let records = result.unwrap();
145
+ assert_eq!(records.len(), 2);
146
+ assert_eq!(records[0], vec![" a ", " b "]);
147
+ assert_eq!(records[1], vec![" 1 ", " 2 "]);
51
148
  }
52
149
  }
data/lib/rbcsv/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RbCsv
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.6"
5
5
  end
data/quick_test.rb ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Gemが利用可能か確認するためのワンライナーテスト
5
+
6
+ puts "Testing rbcsv gem installation..."
7
+
8
+ # gemコマンドでrbcsvが利用可能か確認
9
+ system_result = system("ruby -e \"require 'rbcsv'; puts 'rbcsv loaded successfully: version ' + RbCsv::VERSION\"")
10
+
11
+ if system_result
12
+ puts "\n✅ rbcsv gem is working!"
13
+ else
14
+ puts "\n❌ rbcsv gem is not working properly"
15
+
16
+ # デバッグ情報
17
+ puts "\nDebugging information:"
18
+ puts "Current Ruby: #{`ruby -v`.strip}"
19
+ puts "Gem list:"
20
+ system("gem list rbcsv")
21
+ puts "\nGem paths:"
22
+ system("ruby -e \"puts $LOAD_PATH.grep(/gem/)\"")
23
+ end
data/test_install.rb ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'rbcsv'
5
+
6
+ puts "rbcsv gem version: #{RbCsv::VERSION}"
7
+ puts "Testing CSV parsing..."
8
+
9
+ # Simple CSV data for testing
10
+ csv_data = <<~CSV
11
+ name,age,city
12
+ Alice,30,Tokyo
13
+ Bob,25,Osaka
14
+ Carol,35,Kyoto
15
+ CSV
16
+
17
+ begin
18
+ result = RbCsv.parse(csv_data)
19
+
20
+ puts "\nParsing successful!"
21
+ puts "Number of records: #{result.length}"
22
+ puts "\nParsed data:"
23
+ result.each_with_index do |row, index|
24
+ puts "Row #{index}: #{row.inspect}"
25
+ end
26
+
27
+ puts "\nVerifying structure:"
28
+ puts "First row: #{result[0]}"
29
+ puts "Second row: #{result[1]}"
30
+ puts "Name from first record: #{result[0][0]}"
31
+ puts "Age from first record: #{result[0][1]}"
32
+
33
+ puts "\n✅ rbcsv gem is working correctly!"
34
+ rescue => e
35
+ puts "❌ Error: #{e.message}"
36
+ puts e.backtrace
37
+ exit 1
38
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbcsv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - fujitani sora
@@ -43,6 +43,7 @@ files:
43
43
  - CODE_OF_CONDUCT.md
44
44
  - Cargo.lock
45
45
  - Cargo.toml
46
+ - DEVELOPMENT.md
46
47
  - LICENSE.txt
47
48
  - README.md
48
49
  - Rakefile
@@ -53,10 +54,12 @@ files:
53
54
  - lib/rbcsv.rb
54
55
  - lib/rbcsv/version.rb
55
56
  - output_comparison.rb
57
+ - quick_test.rb
56
58
  - sample.csv
57
59
  - sig/r_csv.rbs
58
60
  - test.rb
59
61
  - test_fixed.rb
62
+ - test_install.rb
60
63
  homepage: https://github.com/fs0414/rbcsv
61
64
  licenses:
62
65
  - MIT
@@ -79,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
82
  - !ruby/object:Gem::Version
80
83
  version: 3.3.11
81
84
  requirements: []
82
- rubygems_version: 3.6.9
85
+ rubygems_version: 3.7.2
83
86
  specification_version: 4
84
87
  summary: High-performance CSV processing library with Rust extensions
85
88
  test_files: []