fizzy-cli 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f3929ca84a2457054103a3f63addd79d594e2a1b10c35335a0de3ec0255144b
4
- data.tar.gz: ed75704d968deb083d4535ce85d6f68827877e65bc735b36c23ac22f0b7f01dc
3
+ metadata.gz: 8dff080f62b6b259b77127650d8be4044a35ecd86959efb4aeef2145c1446f54
4
+ data.tar.gz: c91fc7730a30946f973afed8f3e88599b4f1e6133f4985c012b26c323112f3aa
5
5
  SHA512:
6
- metadata.gz: 5dd289c5a61f833fb5f90f6efa6a2bbf9d7532f5a6650157debaa918347e94bc9336490dd352a3d9985fa7a875b2730a74a3ee291444b09035220a48365d489a
7
- data.tar.gz: 3bf5e77238da1f4f1d49dfb4d74874ab7305be00bf6478ad3e0b2090145d2259922ca928548d58af9fa5325db2295255f2dac3e073eb4590a19c624cba0ecc1c
6
+ metadata.gz: 920efebccdd3501bf2337d2ba494fc7d8a83ec975494069a973dffa20537f8cf6fd3e9aff8f67f509f4cd011e24b1787ff8370fb909591fbf3b820d55c03c096
7
+ data.tar.gz: 05d674ae7f2cd41978a6e4ef18a108812e54314fb6a8a0f0a1c5c1dacb096119aebf6b69561b376ee4f6559f9b1b9051121a5d7ba9067aeb383a501ab31e69da
data/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/).
7
7
 
8
+ ## [0.2.0] - 2026-02-21
9
+
10
+ ### Changed
11
+ - Token storage migrated from JSON (`tokens.json`) to YAML (`tokens.yml`)
12
+ - Auth file parsing uses `YAML.safe_load_file` instead of `JSON.parse`
13
+
14
+ ### Fixed
15
+ - Formatter table column alignment with CJK characters, emoji, and other wide Unicode
16
+ - Formatter handles nil cell values without error
17
+ - ValidationError now shows human-readable output (`- field: message`) instead of raw JSON
18
+ - `identity` command documented as `fizzy auth identity` in README
19
+ - HTTP connection auto-closes on process exit via `at_exit` hook
20
+
21
+ ### Added
22
+ - `--account SLUG` usage example in README
23
+ - CLI integration tests for boards, cards, auth, and error handling (10 tests)
24
+
25
+ ### Removed
26
+ - Unused `patch` HTTP method from Client
27
+
8
28
  ## [0.1.0] - 2025-02-21
9
29
 
10
30
  ### Added
data/README.md CHANGED
@@ -31,7 +31,7 @@ gem install fizzy
31
31
  fizzy auth login --token YOUR_TOKEN
32
32
  ```
33
33
 
34
- Tokens are stored at `~/.config/fizzy-cli/tokens.json`. You can also set `FIZZY_TOKEN` as an environment variable with `--account` to skip the file.
34
+ Tokens are stored at `~/.config/fizzy-cli/tokens.yml`. You can also set `FIZZY_TOKEN` as an environment variable with `--account` to skip the file.
35
35
 
36
36
  ```sh
37
37
  fizzy auth status # Show current auth
@@ -43,6 +43,11 @@ fizzy auth switch SLUG # Change default account
43
43
 
44
44
  All commands support `--json` for JSON output and `--account SLUG` to override the default account.
45
45
 
46
+ ```sh
47
+ # Use a different account
48
+ fizzy cards list --board BOARD_ID --account my-other-team
49
+ ```
50
+
46
51
  ### Boards
47
52
 
48
53
  ```sh
@@ -142,7 +147,7 @@ fizzy pins unpin 42
142
147
  ### Other
143
148
 
144
149
  ```sh
145
- fizzy identity # Show accounts and user info
150
+ fizzy auth identity # Show current user info
146
151
  fizzy version # Print version
147
152
  fizzy help # List all commands
148
153
  ```
@@ -173,7 +178,7 @@ end
173
178
 
174
179
  | Source | Purpose |
175
180
  |--------|---------|
176
- | `~/.config/fizzy-cli/tokens.json` | Stored auth tokens and default account |
181
+ | `~/.config/fizzy-cli/tokens.yml` | Stored auth tokens and default account |
177
182
  | `FIZZY_TOKEN` env var | Token override (requires `--account`) |
178
183
 
179
184
  ## Development
data/lib/fizzy/auth.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Fizzy
4
4
  class Auth
5
5
  CONFIG_DIR = File.expand_path("~/.config/fizzy-cli")
6
- TOKEN_FILE = File.join(CONFIG_DIR, "tokens.json")
6
+ TOKEN_FILE = File.join(CONFIG_DIR, "tokens.yml")
7
7
 
8
8
  def self.resolve(account_slug = nil)
9
9
  if ENV["FIZZY_TOKEN"]
@@ -25,7 +25,7 @@ module Fizzy
25
25
  "No tokens file at #{TOKEN_FILE}. Run: fizzy auth login --token TOKEN"
26
26
  end
27
27
 
28
- data = JSON.parse(File.read(TOKEN_FILE))
28
+ data = YAML.safe_load_file(TOKEN_FILE, permitted_classes: [Time])
29
29
  slug = normalize_slug(account_slug || data["default_account"])
30
30
  account = data["accounts"]&.find { |a| normalize_slug(a["account_slug"]) == slug }
31
31
  raise AuthError, "No account found for #{slug}" unless account
@@ -57,7 +57,7 @@ module Fizzy
57
57
  }
58
58
 
59
59
  FileUtils.mkdir_p(Auth::CONFIG_DIR)
60
- File.write(Auth::TOKEN_FILE, JSON.pretty_generate(data))
60
+ File.write(Auth::TOKEN_FILE, YAML.dump(data))
61
61
  File.chmod(0o600, Auth::TOKEN_FILE)
62
62
 
63
63
  puts "Authenticated as #{accounts.first.dig("user", "name")}"
@@ -85,7 +85,7 @@ module Fizzy
85
85
 
86
86
  desc "accounts", "List available accounts"
87
87
  def accounts
88
- data = JSON.parse(File.read(Auth::TOKEN_FILE))
88
+ data = YAML.safe_load_file(Auth::TOKEN_FILE, permitted_classes: [Time])
89
89
  data["accounts"].each do |a|
90
90
  marker = a["account_slug"] == data["default_account"] ? " *" : ""
91
91
  puts "#{a["account_name"]} (#{a["account_slug"]})#{marker}"
@@ -95,13 +95,13 @@ module Fizzy
95
95
 
96
96
  desc "switch ACCOUNT_SLUG", "Set default account"
97
97
  def switch(account_slug)
98
- data = JSON.parse(File.read(Auth::TOKEN_FILE))
98
+ data = YAML.safe_load_file(Auth::TOKEN_FILE, permitted_classes: [Time])
99
99
  normalized = Auth.normalize_slug(account_slug)
100
100
  account = data["accounts"].find { |a| Auth.normalize_slug(a["account_slug"]) == normalized }
101
101
  raise AuthError, "No account found for #{account_slug}" unless account
102
102
 
103
103
  data["default_account"] = normalized
104
- File.write(Auth::TOKEN_FILE, JSON.pretty_generate(data))
104
+ File.write(Auth::TOKEN_FILE, YAML.dump(data))
105
105
 
106
106
  puts "Switched to #{account["account_name"]} (#{normalized})"
107
107
  end
data/lib/fizzy/client.rb CHANGED
@@ -25,10 +25,6 @@ module Fizzy
25
25
  request(:put, path, body: body)
26
26
  end
27
27
 
28
- def patch(path, body: nil)
29
- request(:patch, path, body: body)
30
- end
31
-
32
28
  def delete(path)
33
29
  request(:delete, path)
34
30
  end
@@ -43,6 +39,11 @@ module Fizzy
43
39
  http.open_timeout = 5
44
40
  http.read_timeout = 30
45
41
  http.start
42
+ at_exit do
43
+ http.finish
44
+ rescue StandardError
45
+ nil
46
+ end
46
47
  http
47
48
  end
48
49
  end
@@ -71,7 +72,6 @@ module Fizzy
71
72
  when :get then Net::HTTP::Get.new(uri)
72
73
  when :post then Net::HTTP::Post.new(uri)
73
74
  when :put then Net::HTTP::Put.new(uri)
74
- when :patch then Net::HTTP::Patch.new(uri)
75
75
  when :delete then Net::HTTP::Delete.new(uri)
76
76
  end
77
77
  end
@@ -116,7 +116,15 @@ module Fizzy
116
116
 
117
117
  def parse_error(response)
118
118
  data = JSON.parse(response.body)
119
- data["error"] || data["errors"]&.join(", ") || response.body
119
+
120
+ if data["errors"].is_a?(Array) && data["errors"].any?
121
+ items = data["errors"].map do |e|
122
+ e.is_a?(Hash) ? "#{e["field"]}: #{e["message"]}" : e.to_s
123
+ end
124
+ "Validation failed\n - #{items.join("\n - ")}"
125
+ else
126
+ data["error"] || response.body
127
+ end
120
128
  rescue JSON::ParserError
121
129
  response.body
122
130
  end
@@ -7,13 +7,13 @@ module Fizzy
7
7
 
8
8
  all_rows = [headers] + rows
9
9
  widths = headers.each_index.map do |i|
10
- all_rows.map { |r| r[i].to_s.length }.max
10
+ all_rows.map { |r| display_width(r[i].to_s) }.max
11
11
  end
12
12
 
13
- io.puts headers.each_with_index.map { |h, i| h.to_s.ljust(widths[i]) }.join(" ")
13
+ io.puts headers.each_with_index.map { |h, i| display_ljust(h.to_s, widths[i]) }.join(" ")
14
14
  io.puts widths.map { |w| "-" * w }.join(" ")
15
15
  rows.each do |row|
16
- io.puts row.each_with_index.map { |c, i| c.to_s.ljust(widths[i]) }.join(" ")
16
+ io.puts row.each_with_index.map { |c, i| display_ljust(c.to_s, widths[i]) }.join(" ")
17
17
  end
18
18
  end
19
19
 
@@ -33,5 +33,27 @@ module Fizzy
33
33
 
34
34
  str.length > max ? "#{str[0...(max - 1)]}…" : str
35
35
  end
36
+
37
+ def self.display_width(str)
38
+ str = str.to_s
39
+ str.each_char.sum { |c| wide_char?(c) ? 2 : 1 }
40
+ end
41
+
42
+ def self.display_ljust(str, width)
43
+ str = str.to_s
44
+ padding = width - display_width(str)
45
+ padding.positive? ? "#{str}#{" " * padding}" : str
46
+ end
47
+
48
+ WIDE_RANGES = [
49
+ 0x1100..0x115F, 0x2E80..0xA4CF, 0xAC00..0xD7AF, 0xF900..0xFAFF,
50
+ 0xFE10..0xFE6F, 0xFF00..0xFF60, 0x1F000..0x1FFFF, 0x20000..0x2FA1F
51
+ ].freeze
52
+
53
+ def self.wide_char?(char)
54
+ WIDE_RANGES.any? { |r| r.cover?(char.ord) }
55
+ end
56
+
57
+ private_class_method :display_width, :display_ljust, :wide_char?
36
58
  end
37
59
  end
data/lib/fizzy/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fizzy
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/fizzy.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "json"
4
4
  require "net/http"
5
5
  require "uri"
6
+ require "yaml"
6
7
 
7
8
  require_relative "fizzy/version"
8
9
  require_relative "fizzy/errors"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fizzy-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Paluy
@@ -82,7 +82,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  requirements: []
85
- rubygems_version: 4.0.3
85
+ rubygems_version: 3.6.9
86
86
  specification_version: 4
87
87
  summary: CLI for Fizzy project management
88
88
  test_files: []