export_to 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8343ea3a496879b2f226295e264b133de7a653d20da05e0169e1feda30442fc
4
- data.tar.gz: 33659ff4d475676775019bf0449cd65e6f1bbd6defb4ed10bd29a3178d9a7f6e
3
+ metadata.gz: 48ade8d2d2cc83728bcbbebf003187ac72d515f42e65645e67a91ba4021936ce
4
+ data.tar.gz: ea3754d3619c1d7fcf73c26c7eb2205f6de0caf43e9fd9731b155a4feda6f595
5
5
  SHA512:
6
- metadata.gz: 0541dc21f8b0f10369be9555525ef169982eb5bcdcaf4431dccae593d2be1aa3458bbd816fe0c2c72bfcedeb5b9ae5fcfb6d95281d0c8edb13033f9498a8334e
7
- data.tar.gz: 9fcb5586c9384318668d2db5ad49f976906e2a8f69175f906f8f92606cba9cb887cbd5c212733f85a274690032055aa6d86d91afadeeb7bb8d140259ea15a1c4
6
+ metadata.gz: 0a63b654da5d5bcf77bb87853e3c0f8c3cd7b0d985a40204e6ef259ad12da528e96f8077523464a5d7ba202a450b08c8f483431d0ffe022901e0421399de1e72
7
+ data.tar.gz: f311a3013ffac1fd95eca922865e9df4c427db2a9dd7c0dca8f8c3bf2188b2716aca16dc869d2d9909a5a2d6afaa237edfd429912aef1accbd62b6329e93f586
@@ -31,6 +31,14 @@ jobs:
31
31
  rails: '7.1'
32
32
  - ruby: '3.3'
33
33
  rails: '7.1'
34
+ - ruby: '3.3'
35
+ rails: '8.0'
36
+ - ruby: '3.3'
37
+ rails: '8.1'
38
+ - ruby: '3.4'
39
+ rails: '8.0'
40
+ - ruby: '3.4'
41
+ rails: '8.1'
34
42
 
35
43
  env:
36
44
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/ruby_${{ matrix.ruby }}_rails_${{ matrix.rails }}.gemfile
@@ -0,0 +1,121 @@
1
+ name: Publish gem
2
+
3
+ # master 上 gemspec 版本號尚未發佈到 rubygems.org 時,自動以 Trusted Publishing (OIDC) 發佈。
4
+ # 版本已存在則跳過;不使用任何儲存的 RubyGems API key。
5
+ on:
6
+ push:
7
+ branches:
8
+ - master
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: publish-gem-${{ github.repository }}
16
+ cancel-in-progress: false
17
+
18
+ env:
19
+ # 與 ci.yml 矩陣中最新的 Ruby 3.4 / Rails 8.1 組合一致
20
+ RUBY_VERSION: '3.4.11'
21
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/ruby_3.4_rails_8.1.gemfile
22
+ GEM_NAME: export_to
23
+ GEMSPEC: export_to.gemspec
24
+
25
+ jobs:
26
+ check:
27
+ name: Check version on rubygems.org
28
+ runs-on: ubuntu-latest
29
+ outputs:
30
+ should_publish: ${{ steps.check.outputs.should_publish }}
31
+ gem_name: ${{ steps.spec.outputs.name }}
32
+ gem_version: ${{ steps.spec.outputs.version }}
33
+ steps:
34
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
37
+
38
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
39
+ with:
40
+ ruby-version: ${{ env.RUBY_VERSION }}
41
+
42
+ - name: Read gem name and version from gemspec
43
+ id: spec
44
+ run: |
45
+ ruby -e 'spec = Gem::Specification.load(ENV.fetch("GEMSPEC")) or abort("cannot load #{ENV["GEMSPEC"]}"); abort("gemspec name #{spec.name} != #{ENV["GEM_NAME"]}") unless spec.name == ENV["GEM_NAME"]; puts "name=#{spec.name}"; puts "version=#{spec.version}"' >> "$GITHUB_OUTPUT"
46
+ cat "$GITHUB_OUTPUT"
47
+
48
+ - name: Check whether this version already exists on rubygems.org
49
+ id: check
50
+ env:
51
+ GEM_NAME: ${{ steps.spec.outputs.name }}
52
+ GEM_VERSION: ${{ steps.spec.outputs.version }}
53
+ run: |
54
+ set -euo pipefail
55
+ status=$(curl -sS -o versions.json -w '%{http_code}' "https://rubygems.org/api/v1/versions/${GEM_NAME}.json")
56
+ if [ "$status" = "404" ]; then
57
+ echo "Gem ${GEM_NAME} not found on rubygems.org; will publish."
58
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
59
+ exit 0
60
+ fi
61
+ if [ "$status" != "200" ]; then
62
+ echo "::error::rubygems.org API returned HTTP ${status}"
63
+ exit 1
64
+ fi
65
+ if jq -e --arg v "$GEM_VERSION" 'any(.[]; .number == $v)' versions.json > /dev/null; then
66
+ echo "${GEM_NAME} ${GEM_VERSION} already on rubygems.org; skip publish."
67
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
68
+ else
69
+ echo "${GEM_NAME} ${GEM_VERSION} not on rubygems.org yet; will publish."
70
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
71
+ fi
72
+
73
+ test:
74
+ name: Test (Ruby 3.4 / Rails 8.1)
75
+ needs: check
76
+ if: needs.check.outputs.should_publish == 'true'
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
80
+ with:
81
+ persist-credentials: false
82
+
83
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
84
+ with:
85
+ ruby-version: ${{ env.RUBY_VERSION }}
86
+ bundler-cache: true
87
+
88
+ - run: bundle exec rake test
89
+
90
+ publish:
91
+ name: Publish ${{ needs.check.outputs.gem_name }} ${{ needs.check.outputs.gem_version }}
92
+ needs: [check, test]
93
+ if: needs.check.outputs.should_publish == 'true' && github.ref == 'refs/heads/master'
94
+ runs-on: ubuntu-latest
95
+ environment: gem-publish
96
+ permissions:
97
+ id-token: write # Trusted Publishing (OIDC)
98
+ contents: write # rake release 推送 vX.Y.Z tag
99
+ steps:
100
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
101
+ with:
102
+ persist-credentials: false
103
+
104
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
105
+ with:
106
+ ruby-version: ${{ env.RUBY_VERSION }}
107
+ bundler-cache: true
108
+
109
+ # setup-ruby 的 bundler-cache 會把 BUNDLE_PATH 等設定寫進 .bundle/config,
110
+ # 若該檔被 git 追蹤,rake release 的 guard_clean 會判定工作目錄不乾淨。
111
+ # 目前此 repo 沒有追蹤任何 .bundle/config;這一步對「有追蹤才標記」做容錯處理,
112
+ # 日後若加入 gemfiles/.bundle/config 或 .bundle/config 也不會讓發佈失敗。
113
+ - name: Ignore CI-only bundler config change
114
+ run: |
115
+ git status --porcelain
116
+ git ls-files -z '*.bundle/config' | xargs -0 -r git update-index --assume-unchanged
117
+ git status --porcelain
118
+
119
+ # 透過 rubygems/configure-rubygems-credentials 取得短效 OIDC token,
120
+ # 再執行 bundle exec rake release(build、建立並推送 vX.Y.Z tag、push gem)
121
+ - uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,113 @@
1
+ # 更新紀錄(Changelog)
2
+
3
+ 本檔記錄此專案所有值得注意的變更。
4
+
5
+ 格式參考 [Keep a Changelog](https://keepachangelog.com/zh-TW/1.1.0/),版本號遵循 [Semantic Versioning](https://semver.org/lang/zh-TW/)。
6
+
7
+ ## [Unreleased]
8
+
9
+ ### Added
10
+
11
+ - 新增 `CHANGELOG.md`。
12
+ - 完整的 `README.md`:概述、環境需求、安裝、DSL 用法、開發與發版流程。
13
+ - CLAUDE.md 規則:每個 PR 都必須同步更新 `README.md` 與 `CHANGELOG.md`;`README.md` 與 `CHANGELOG.md` 一律用繁體中文撰寫。
14
+
15
+ ### Changed
16
+
17
+ - `README.md`、`CHANGELOG.md` 改以繁體中文(zh-TW)撰寫。
18
+
19
+ ## [1.3.0] - 2026-10-27
20
+
21
+ ### Fixed
22
+
23
+ - `ExportTo::Base#each` 每次呼叫都會 raise `NoMethodError (undefined method 'rows!')`。現在會回傳所有列組成的 Array(第一列是標題列,內容與 `#to_csv` 相同);帶 block 時會逐列 yield 並回傳 exporter 本身,因此 Enumerable 方法(`map`、`to_a` 等)也能使用。`#each!` 沒有變動。
24
+ - `TESTING.md` 與 `bin/test_all` 已過時(只涵蓋 Ruby 2.7–3.1)。現在它們的說明與執行內容都與 `.github/workflows/ci.yml` 的矩陣一致。
25
+
26
+ ### Deprecated
27
+
28
+ - `ExportTo::Base#to_xls`:自 1.0.0 起它回傳的就是 **XLSX** 資料(與 `#to_xlsx` 相同),而不是舊版 `.xls` 檔。為了不讓既有呼叫端壞掉,回傳值維持不變,但現在會印出一次性的 `[DEPRECATION]` 警告(輸出到 `$stderr`,每個 process 一次)。請改用 `#to_xlsx`;若要真正的 `.xls`,請用 `ExportTo::Exporter::Xls.new(export).to_xls`。未來的 major 版本可能會讓 `#to_xls` 回傳真正的 `.xls` 檔,或將它移除。
29
+
30
+ ### Added
31
+
32
+ - 測試:`#each`、`#to_xls` 的相容性(仍回傳 XLSX)與其棄用警告、`#to_xlsx` / `ExportTo::Exporter::Xls` 的輸出格式。
33
+
34
+ ## [1.2.1] - 2026-10-27
35
+
36
+ ### Added
37
+
38
+ - 支援 Rails 8.0 / 8.1 與 Ruby 3.4(`activesupport` 現在放寬到 `< 8.2`;`csv` gem 改為明確相依;移除 `required_ruby_version` 的上限;新增 `ruby_3.3_rails_8.*` / `ruby_3.4_rails_8.*` gemfile 與對應的 CI job)。
39
+ - GitHub Actions `publish.yml`:新版本進到 `master` 時,透過 Trusted Publishing 自動發佈到 RubyGems。
40
+ - CLAUDE.md 加入發版規則。
41
+
42
+ ### Changed
43
+
44
+ - 移除每個 gemfile 各自的舊 lockfile,這些 lockfile 會讓 CI 在較舊的 Ruby 上失敗。
45
+
46
+ ## [1.2.0] - 2026-01-08
47
+
48
+ ### Added
49
+
50
+ - 新增 Ruby 3.2 與 3.3 的測試 gemfile(`ruby_3.2_rails_7.0`、`ruby_3.2_rails_7.1`、`ruby_3.3_rails_7.1`)與 CI job。
51
+
52
+ ### Changed
53
+
54
+ - gemspec 的 `required_ruby_version` 上限由 `< 3.2.0` 提高到 `< 3.4.0`。
55
+
56
+ ## [1.1.2] - 2026-01-07
57
+
58
+ ### Changed
59
+
60
+ - 更新各 gemfile 的 lockfile。
61
+
62
+ ## [1.1.1] - 2026-01-07
63
+
64
+ ### Added
65
+
66
+ - 多版本 Ruby / Rails 測試環境:每個版本各自的 gemfile、GitHub Actions 矩陣、`bin/test_all`、`bin/check_dependencies` 與 `TESTING.md`。
67
+
68
+ ### Fixed
69
+
70
+ - 測試不再依賴寫死的 macOS 路徑。
71
+
72
+ ## [1.1.0] - 2023-02-02
73
+
74
+ ### Changed
75
+
76
+ - 修正並微調 XLSX exporter(`ExportTo::Exporter::Xlsx`)。
77
+
78
+ ## [1.0.0] - 2023-02-02
79
+
80
+ ### Changed
81
+
82
+ - 內部改寫:匯出邏輯移到 `ExportTo::Exporter::Csv` / `Xls` / `Xlsx`;`ExportTo::Base` 提供 `to_csv`、`to_xlsx` 與 `to_xls`。
83
+ - `activesupport` 限制為 `>= 6, < 7`;開發相依套件放寬為 `bundler >= 2.0`、`rake >= 13`。
84
+
85
+ ## [0.1.2] - 2019-06-19
86
+
87
+ ### Changed
88
+
89
+ - CSV 輸出現在會強制幫每個欄位加上引號。
90
+
91
+ ## [0.1.1] - 2018-12-05
92
+
93
+ ### Fixed
94
+
95
+ - 修正匯出時出現 "File exists" 的 bug。
96
+
97
+ ## [0.1.0] - 2018-10-21
98
+
99
+ ### Added
100
+
101
+ - 首次發佈:`ExportTo::Base` DSL(`set`、`joins`、`presenter`、`each_with`)、帶有列計數器的 presenter、空的 join relation 處理,以及 `to_xlsx`。
102
+
103
+ [Unreleased]: https://github.com/superlanding/export_to/compare/v1.3.0...HEAD
104
+ [1.3.0]: https://github.com/superlanding/export_to/compare/v1.2.1...v1.3.0
105
+ [1.2.1]: https://github.com/superlanding/export_to/compare/v1.2.0...v1.2.1
106
+ [1.2.0]: https://github.com/superlanding/export_to/compare/v1.1.2...v1.2.0
107
+ [1.1.2]: https://github.com/superlanding/export_to/compare/v1.1.1...v1.1.2
108
+ [1.1.1]: https://github.com/superlanding/export_to/releases/tag/v1.1.1
109
+ [1.1.0]: https://github.com/superlanding/export_to/commit/48957da
110
+ [1.0.0]: https://github.com/superlanding/export_to/commit/8d7f937
111
+ [0.1.2]: https://github.com/superlanding/export_to/commit/d276482
112
+ [0.1.1]: https://github.com/superlanding/export_to/commit/12dd5c1
113
+ [0.1.0]: https://github.com/superlanding/export_to/commit/d605bf9
data/CLAUDE.md ADDED
@@ -0,0 +1,21 @@
1
+ # CLAUDE.md
2
+
3
+ ## 發版規則(Ruby gem)
4
+
5
+ - 這個 gem(`export_to`,gemspec:`export_to.gemspec`)在 master 收到「新版本號」時,由 GitHub Actions(`.github/workflows/publish.yml`)自動發佈到 rubygems.org(Trusted Publishing,不使用 API key)。
6
+ - **每一個 PR 都必須跳版號**(改 `lib/export_to/version.rb`),除非這個 PR 只改文件、CI 或 CLAUDE.md 且明確不需要發版。沒跳版號的 PR 合進 master 後不會發佈。
7
+ - 跳版規則(Semantic Versioning):修 bug 跳 patch(x.y.Z);新增相容功能跳 minor(x.Y.0);不相容變更跳 major(X.0.0)。
8
+ - 版號只改在 PR 裡。AI 帳號只能開 branch 與 PR,不得合併到 master;只有真人帳號可以合併。合併就等於批准發佈。
9
+ - 不得為了讓測試通過而改變程式原本的行為。
10
+
11
+ ## 文件與更新紀錄(README / CHANGELOG)
12
+
13
+ - **每一個 PR 都必須同步更新文件**:
14
+ - 只要行為、用法、設定、相依套件或支援的 Ruby / Rails 版本有變,就要更新 `README.md`。
15
+ - 一律要在 `CHANGELOG.md` 新增一筆條目([Keep a Changelog](https://keepachangelog.com/zh-TW/1.1.0/) 格式,分類用 Added / Changed / Deprecated / Removed / Fixed / Security)。
16
+ - **語言規則**:`README.md` 與 `CHANGELOG.md` 一律用繁體中文(zh-TW,台灣用語)撰寫;程式碼、識別字(class / 方法 / 變數名稱)、指令、檔名與路徑維持英文,不要翻譯。
17
+ - `CHANGELOG.md` 的寫法:
18
+ - 沒有跳版號的 PR:寫在最上方的 `## [Unreleased]` 底下。
19
+ - 有跳版號的 PR:把條目寫在新的版本章節 `## [x.y.z] - YYYY-MM-DD`(日期為合併日),並同步更新檔尾的版本比較連結。
20
+ - 版本號必須與 rubygems.org 上實際發佈的版本一致,不得寫還沒發佈的版本。
21
+ - 只改文件、CI 或 CLAUDE.md 的 PR 不需要跳版號,但仍要在 `CHANGELOG.md` 的 `Unreleased` 補一筆。
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- export_to (1.2.0)
5
- activesupport (>= 6.0, < 8)
4
+ export_to (1.3.0)
5
+ activesupport (>= 6.0, < 8.2)
6
+ csv
6
7
  fast_excel (~> 0.4)
7
8
  iconv (~> 1.0)
8
9
  spreadsheet (~> 1.3)
@@ -29,14 +30,15 @@ GEM
29
30
  minitest (>= 5.1)
30
31
  tzinfo (~> 2.0)
31
32
  zeitwerk (~> 2.3)
32
- bigdecimal (4.0.1)
33
+ bigdecimal (4.1.3)
33
34
  builder (3.2.4)
34
35
  concurrent-ruby (1.2.0)
35
36
  crass (1.0.6)
37
+ csv (3.3.6)
36
38
  erubi (1.12.0)
37
39
  fast_excel (0.5.0)
38
40
  ffi (> 1.9, < 2)
39
- ffi (1.17.3)
41
+ ffi (1.17.4)
40
42
  i18n (1.12.0)
41
43
  concurrent-ruby (~> 1.0)
42
44
  iconv (1.1.1)
@@ -68,7 +70,7 @@ GEM
68
70
  rake (13.0.6)
69
71
  ruby-ole (1.2.13.1)
70
72
  shoulda-context (1.2.2)
71
- spreadsheet (1.3.4)
73
+ spreadsheet (1.3.5)
72
74
  bigdecimal
73
75
  logger
74
76
  ruby-ole
data/README.md CHANGED
@@ -1,64 +1,156 @@
1
1
  # ExportTo
2
2
 
3
- 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/export_to`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Gem Version](https://badge.fury.io/rb/export_to.svg)](https://rubygems.org/gems/export_to)
4
+ [![CI](https://github.com/superlanding/export_to/actions/workflows/ci.yml/badge.svg)](https://github.com/superlanding/export_to/actions/workflows/ci.yml)
4
5
 
5
- TODO: Delete this and the text above, and describe your gem
6
+ 用一套簡單的宣告式 DSL,把一批資料(ActiveRecord relation、Struct 陣列,或任何有 `each` 的物件)匯出成 **CSV** 或 **XLSX**(透過較底層的 exporter,也能匯出舊版 **XLS**)。
6
7
 
7
- ## Installation
8
+ 你只要在 `ExportTo::Base` 的子類別裡宣告一次欄位(標題、來源屬性、寬度、格式、值的轉換),需要計算欄位時可以加上 presenter,也可以加上 `joins` relation 把一筆資料展開成多列,最後呼叫 `to_csv` / `to_xlsx` 即可。
8
9
 
9
- Add this line to your application's Gemfile:
10
+ ## 環境需求
11
+
12
+ | Ruby | CI 測試的 Rails / ActiveSupport 版本 |
13
+ |------|------------------------------------|
14
+ | 2.7 | 6.1 |
15
+ | 3.0 | 6.1, 7.0 |
16
+ | 3.1 | 6.1, 7.0, 7.1 |
17
+ | 3.2 | 7.0, 7.1 |
18
+ | 3.3 | 7.1, 8.0, 8.1 |
19
+ | 3.4 | 8.0, 8.1 |
20
+
21
+ gemspec 要求 Ruby `>= 2.7.0` 與 `activesupport >= 6.0, < 8.2`。實際支援範圍以 [`.github/workflows/ci.yml`](.github/workflows/ci.yml) 為準。
22
+
23
+ Runtime 相依套件:`fast_excel`(XLSX)、`spreadsheet`(XLS)、`csv`、`iconv`、`activesupport`。
24
+
25
+ ## 安裝
26
+
27
+ 在應用程式的 Gemfile 加入這一行:
10
28
 
11
29
  ```ruby
12
- gem 'export_to'
30
+ gem "export_to"
13
31
  ```
14
32
 
15
- And then execute:
33
+ 然後執行:
16
34
 
17
- $ bundle
35
+ ```bash
36
+ bundle install
37
+ ```
18
38
 
19
- Or install it yourself as:
39
+ 或自行執行 `gem install export_to` 安裝。
20
40
 
21
- $ gem install export_to
41
+ ## 使用方式
22
42
 
23
- ## Usage
43
+ ### 1. 定義 exporter
24
44
 
25
- TODO: Write usage instructions here
45
+ ```ruby
46
+ class UserExport < ExportTo::Base
47
+ # set <標題>, <key>, <欄位選項>, &可選的轉換 block
48
+ set "Name", :full_name, width: 30
49
+ set "Mobile", :mobile, width: 20, format: { bold: true }
50
+ set "Amount", :amount, width: 12 do |value|
51
+ value + 100 # block 會收到儲存格的值,並回傳最終要輸出的值
52
+ end
53
+ set "Source", "web" # key 為 String 時,每一列都會寫入這個固定值
54
+
55
+ excel worksheet: "Users", height: 24 # 可選的 XLSX 設定(工作表名稱、列高)
56
+
57
+ presenter do # 可選:計算欄位
58
+ def full_name
59
+ "#{first_name} #{last_name}" # 找不到的方法會委派給該筆 record
60
+ end
61
+
62
+ def amount
63
+ relation? ? relation.amount : 0
64
+ end
65
+ end
66
+
67
+ joins :wallets # 可選:record.wallets 裡每一筆各佔一列
68
+ end
69
+ ```
26
70
 
27
- ## Ruby & Rails Version Support
71
+ - **`set(title, key, options = {}, &block)`**:每次呼叫新增一個欄位,依呼叫順序排列。
72
+ - `key` 為 **Symbol** 時,會從 presenter 讀取(presenter 會委派給 record);`nil` 會變成 `""`。
73
+ - `key` 為 **String** 時,直接當作固定內容使用。
74
+ - XLSX 專用的 `options`:`width:`(欄寬)與 `format:`(`FastExcel` 的 format hash,例如 `{ bold: true }`)。
75
+ - 可選的 block 用來後處理儲存格的值。
76
+ - **`excel(worksheet: "Name", height: 20)`**:工作表名稱(預設 `"Default"`)與資料列的列高(預設 `20`)。標題列為粗體、置中並帶有背景色。
77
+ - **`presenter { ... }`**:定義 `YourExport::Presenter`(是上層 exporter 的 presenter 的子類別)。在裡面可以使用 `model`、`relation`、`relation?`、`x`(record 的索引)與 `y`(`joins` relation 內的索引)。
78
+ - **`joins(:relation_name)`**:當 `record.relation_name` 有資料時,每個元素各自成為一列,且 `relation` 就是該元素;presenter 會先找 `relation` 上的方法,找不到再找 `model`。relation 為空的 record 會產生單獨一列(此時 `relation?` 為 `false`)。
79
+ - **`each_with { |presenter| ... }`**:hook,會在讀取各欄位之前,以每一列的 presenter 呼叫一次。
80
+ - **`self.each_method = :find_in_batches`**:改用 `records.find_in_batches` 而不是 `records.each` 來迭代,適合很大的 ActiveRecord relation。
28
81
 
29
- This gem supports:
30
- - Ruby 2.7 with Rails 6.1
31
- - Ruby 3.0 with Rails 6.1, 7.0
32
- - Ruby 3.1 with Rails 6.1, 7.0, 7.1
33
- - Ruby 3.2 with Rails 7.0, 7.1
34
- - Ruby 3.3 with Rails 7.1
82
+ ### 2. 匯出
35
83
 
36
- ## Development
84
+ ```ruby
85
+ users = User.includes(:wallets).where(active: true) # 或任何可列舉的物件
86
+ export = UserExport.new(users)
37
87
 
38
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
88
+ export.to_csv # => String,每個欄位都有加引號("Name","Mobile",...)
89
+ export.to_xlsx # => .xlsx 檔案的二進位 String
90
+ export.each # => 所有列組成的 Array,第一列是標題列:[["Name", "Mobile", ...], ["Eddie Li", ...], ...]
91
+ export.each { |columns| ... } # 逐列 yield(第一列是標題列),並回傳 exporter 本身;Enumerable 方法也能用
92
+ ```
39
93
 
40
- ### Testing Multiple Ruby Versions
94
+ 兩位使用者的 CSV 輸出範例(第二位有兩個 wallet):
41
95
 
42
- To test against all supported Ruby versions at once:
96
+ ```
97
+ "Name","Mobile","Amount","Source"
98
+ "Eddie Li","+8869","100","web"
99
+ "Vegeta Lu","+8868","1100","web"
100
+ "Vegeta Lu","+8868","2100","web"
101
+ ```
102
+
103
+ 在 Rails controller 裡提供下載:
104
+
105
+ ```ruby
106
+ def export
107
+ send_data UserExport.new(User.all).to_xlsx,
108
+ filename: "users.xlsx",
109
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
110
+ end
111
+ ```
112
+
113
+ ### 已知限制
114
+
115
+ 以下是目前程式碼的實際行為:
116
+
117
+ - `ExportTo::Base#to_xls` **已棄用,而且名稱有誤導性**:自 1.0.0 起它回傳的是與 `#to_xlsx` 相同的 **XLSX** 資料(不是舊版 `.xls` 檔)。保留這個行為是為了不讓既有呼叫端壞掉;呼叫時會印出一次性的 `[DEPRECATION]` 警告。請改用 `#to_xlsx`。若需要真正的舊版 `.xls` 檔,請用 `ExportTo::Exporter::Xls.new(export).to_xls`(不會套用任何樣式選項)。
118
+ - `width:` / `format:` 與 `excel` 選項只對 XLSX 輸出有效。
119
+ - 子類別會共用上層 class 的欄位定義陣列(`class_attribute`),所以如果某個 exporter 本身也會單獨使用,就不要在它的子類別裡呼叫 `set`。
120
+
121
+ ## 開發
122
+
123
+ clone 下來之後,先執行 `bin/setup` 安裝相依套件,接著跑測試:
43
124
 
44
125
  ```bash
45
- bin/test_all
126
+ bundle exec rake test # 也可以用 rake t / rake(預設 task)
127
+ bin/console # 互動式 console
46
128
  ```
47
129
 
48
- To check dependency compatibility:
130
+ 每個 Ruby / Rails 組合在 `gemfiles/` 內都有各自的 gemfile(CI 會把 `BUNDLE_GEMFILE` 指到它):
49
131
 
50
132
  ```bash
51
- bin/check_dependencies
133
+ BUNDLE_GEMFILE=$PWD/gemfiles/ruby_3.4_rails_8.1.gemfile bundle install
134
+ BUNDLE_GEMFILE=$PWD/gemfiles/ruby_3.4_rails_8.1.gemfile bundle exec rake test
52
135
  ```
53
136
 
54
- For more detailed testing instructions, see [TESTING.md](TESTING.md).
137
+ `bin/test_all` 會跑過所有支援的 Ruby 版本(需要 rbenv 或 rvm),`bin/check_dependencies` 會檢查相依套件的相容性;詳見 [TESTING.md](TESTING.md)。
138
+
139
+ ## 發版
140
+
141
+ 發版透過 RubyGems [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/) 自動完成(repo 內不存放任何 API key):
142
+
143
+ 1. 在你的 PR 裡修改 `lib/export_to/version.rb` 的 `ExportTo::VERSION`(遵循 [Semantic Versioning](https://semver.org/lang/zh-TW/):修 bug 跳 patch、相容的新功能跳 minor、不相容的變更跳 major)。
144
+ 2. 在 `CHANGELOG.md` 新增對應的 `## [x.y.z] - YYYY-MM-DD` 區塊。
145
+ 3. 由真人 review 後合併進 `master`(AI 帳號只能開 PR)。
146
+ 4. [`.github/workflows/publish.yml`](.github/workflows/publish.yml) 會在 `master` 上執行:如果 gemspec 的版本還沒上 rubygems.org,就執行 `rake release`(打包 gem、打 `vX.Y.Z` tag、發佈)。沒有跳版號的合併不會發佈任何東西。
55
147
 
56
- 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
148
+ 只改文件 / CI 的 PR 不需要跳版號。請見 [CLAUDE.md](CLAUDE.md):每個 PR 也都必須更新 `README.md`(行為或用法有變時)並在 `CHANGELOG.md` 新增一筆條目。
57
149
 
58
- ## Contributing
150
+ ## 貢獻
59
151
 
60
- Bug reports and pull requests are welcome on GitHub at https://github.com/superlanding/export_to.
152
+ 歡迎到 GitHub 回報問題與提交 pull request:https://github.com/superlanding/export_to。
61
153
 
62
- ## License
154
+ ## 授權
63
155
 
64
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
156
+ 本 gem 以 [MIT License](https://opensource.org/licenses/MIT) 條款開源。
data/TESTING.md CHANGED
@@ -1,103 +1,45 @@
1
1
  # Testing Guide
2
2
 
3
- This gem supports multiple Ruby and Rails versions:
4
- - Ruby 2.7 with Rails 6.1
5
- - Ruby 3.0 with Rails 6.1, 7.0
6
- - Ruby 3.1 with Rails 6.1, 7.0, 7.1
3
+ Tests use Minitest + shoulda-context (`test/**/*_test.rb`) and run against several Ruby / Rails combinations.
7
4
 
8
- ## Multi-Version Testing
5
+ The supported matrix is defined in **one place**: [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Each combination has a gemfile `gemfiles/ruby_<ruby>_rails_<rails>.gemfile`. Lists of versions are intentionally not copied here so they cannot go stale; see the [README](README.md#requirements) for the current table.
9
6
 
10
- ### Local Testing
7
+ ## Run the tests for one combination
11
8
 
12
- #### Test all Ruby versions at once
13
-
14
- Run the test suite against all supported Ruby versions:
9
+ CI selects the gemfile with `BUNDLE_GEMFILE`; do the same locally (switch to the matching Ruby first, e.g. with rbenv):
15
10
 
16
11
  ```bash
17
- bin/test_all
12
+ export BUNDLE_GEMFILE=$PWD/gemfiles/ruby_3.4_rails_8.1.gemfile
13
+ bundle install
14
+ bundle exec rake test # also: rake t / rake
18
15
  ```
19
16
 
20
- This script will:
21
- - Automatically switch between Ruby versions (requires rbenv or rvm)
22
- - Install dependencies for each version
23
- - Run the test suite for each version
24
- - Display a summary of results
25
-
26
- #### Test a specific Ruby/Rails version combination
17
+ ## Run the whole CI matrix locally
27
18
 
28
19
  ```bash
29
- # Ruby 2.7 with Rails 6.1
30
- export BUNDLE_GEMFILE=gemfiles/ruby_2.7_rails_6.1.gemfile
31
- bundle install
32
- bundle exec rake test
33
-
34
- # Ruby 3.0 with Rails 6.1
35
- export BUNDLE_GEMFILE=gemfiles/ruby_3.0_rails_6.1.gemfile
36
- bundle install
37
- bundle exec rake test
38
-
39
- # Ruby 3.0 with Rails 7.0
40
- export BUNDLE_GEMFILE=gemfiles/ruby_3.0_rails_7.0.gemfile
41
- bundle install
42
- bundle exec rake test
43
-
44
- # Ruby 3.1 with Rails 7.1
45
- export BUNDLE_GEMFILE=gemfiles/ruby_3.1_rails_7.1.gemfile
46
- bundle install
47
- bundle exec rake test
20
+ bin/test_all
48
21
  ```
49
22
 
50
- ### Dependency Checking
23
+ `bin/test_all` needs rbenv or rvm. For every entry of its `TEST_MATRIX` (kept equal to the CI matrix) it switches to the newest installed patch version of that Ruby, runs `bundle install` with the matching gemfile, runs `rake test` and prints a summary. A Ruby that is not installed is reported as a failure. When you change `ci.yml`, update `TEST_MATRIX` in `bin/test_all` as well.
51
24
 
52
- Check if all dependencies are compatible with all Ruby versions:
25
+ ## Check dependencies
53
26
 
54
27
  ```bash
55
28
  bin/check_dependencies
56
29
  ```
57
30
 
58
- This script will verify that all dependencies can be installed and will report any outdated gems.
59
-
60
- ## CI/CD
61
-
62
- The gem uses GitHub Actions for continuous integration. Every push and pull request will automatically test against all supported combinations:
63
- - Ruby 2.7 / Rails 6.1
64
- - Ruby 3.0 / Rails 6.1, 7.0
65
- - Ruby 3.1 / Rails 6.1, 7.0, 7.1
66
-
67
- See `.github/workflows/ci.yml` for the full configuration.
68
-
69
- ## Gemfiles Structure
70
-
71
- The `gemfiles/` directory contains version-specific Gemfiles for each Ruby/Rails combination:
72
- - `ruby_2.7_rails_6.1.gemfile` - Ruby 2.7 with Rails 6.1
73
- - `ruby_3.0_rails_6.1.gemfile` - Ruby 3.0 with Rails 6.1
74
- - `ruby_3.0_rails_7.0.gemfile` - Ruby 3.0 with Rails 7.0
75
- - `ruby_3.1_rails_6.1.gemfile` - Ruby 3.1 with Rails 6.1
76
- - `ruby_3.1_rails_7.0.gemfile` - Ruby 3.1 with Rails 7.0
77
- - `ruby_3.1_rails_7.1.gemfile` - Ruby 3.1 with Rails 7.1
31
+ Verifies that the dependencies can be installed for the supported versions and reports outdated gems.
78
32
 
79
- Each gemfile has its own lockfile committed to the repository to ensure consistent dependency versions across environments.
33
+ ## Adding a Ruby / Rails combination
80
34
 
81
- ## Requirements
35
+ 1. Add `gemfiles/ruby_<ruby>_rails_<rails>.gemfile` (copy a neighbouring one and change the `activesupport` / `railties` versions).
36
+ 2. Add the combination to the matrix in `.github/workflows/ci.yml` and to `TEST_MATRIX` in `bin/test_all`.
37
+ 3. Update the table in the README.
82
38
 
83
- ### For Local Multi-Version Testing
39
+ ## Lockfiles
84
40
 
85
- You need either:
86
- - **rbenv** with Ruby 2.7, 3.0, and 3.1 installed
87
- - **rvm** with Ruby 2.7, 3.0, and 3.1 installed
41
+ Some gemfiles have a committed `*.gemfile.lock`, and `Gemfile.lock` is committed too. They contain the gem's own version (`export_to (x.y.z)`), so **keep them consistent: bump the version in those files whenever `lib/export_to/version.rb` changes**.
88
42
 
89
- ### Installing Ruby Versions
43
+ ## CI
90
44
 
91
- #### Using rbenv:
92
- ```bash
93
- rbenv install 2.7.8
94
- rbenv install 3.0.6
95
- rbenv install 3.1.4
96
- ```
97
-
98
- #### Using rvm:
99
- ```bash
100
- rvm install 2.7.8
101
- rvm install 3.0.6
102
- rvm install 3.1.4
103
- ```
45
+ GitHub Actions runs every matrix entry on every push to `master` and on every pull request that targets `master`.