freshbooks-cli 0.3.0 → 0.3.1

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: 7d8d73d99463668eb02e32550fe31e0c0ee32f616bd8fbfd9839df01f5a22052
4
- data.tar.gz: ce0758e59c4e5631b8d84c87d8c56bdb63e87386021e27de32922e162d0dad55
3
+ metadata.gz: 1a9fbab3436ac48b76db93a82b5b6ee909ab84e8928bb21ce0941c3547c1174e
4
+ data.tar.gz: 187a0da5b2fb4eb34f2df616573bc22fa819fa49da38a33b1df5bfab2fa5dba8
5
5
  SHA512:
6
- metadata.gz: d21f0dd4f0d9704e736aa41b2f574c9d616e097d05a61d5e7eb23a6d39730cc9cb291178381397563e34e71fdbae7716ba25af2d8c34b4fe1da3b409f2ebc56f
7
- data.tar.gz: ab9dcae6c63603ddc32aeb4519c6e4022eaa406e53a02293e1d5f7cf7c7546a1f1f3017194b3498bbcfd8de3da03be48b20ac039d8c587b6c15ff4aa7eff11a3
6
+ metadata.gz: 28163e4059f5d8b5ac7bb6aed4c49b7abdf1e3d3ebae078a4feccead18e6d6989bc5985b9794e9401570c99735bedadce33aa440696ca7e459cc5144a4d167ac
7
+ data.tar.gz: 5fc2c9c1b53be6557cd30edbfc9ce898f86fa9fbd5fdc19403ab379f5daf18e4e8ed1cb0ac9ffd3aa10bd11a9326ab01942707717cc619adf3d3b6f05b9505cb
data/lib/fb/cli.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "thor"
4
4
  require "json"
5
5
  require "date"
6
+ require "io/console"
6
7
 
7
8
  module FB
8
9
  class Cli < Thor
@@ -11,8 +12,16 @@ module FB
11
12
  end
12
13
 
13
14
  class_option :no_interactive, type: :boolean, default: false, desc: "Disable interactive prompts (auto-detected when not a TTY)"
15
+ class_option :interactive, type: :boolean, default: false, desc: "Force interactive mode even when not a TTY"
14
16
  class_option :format, type: :string, desc: "Output format: table (default) or json"
15
17
 
18
+ no_commands do
19
+ def invoke_command(command, *args)
20
+ Spinner.interactive = interactive?
21
+ super
22
+ end
23
+ end
24
+
16
25
  # --- version ---
17
26
 
18
27
  desc "version", "Print the current version"
@@ -294,12 +303,12 @@ module FB
294
303
  client = maps[:clients][e["client_id"].to_s] || e["client_id"].to_s
295
304
  project = maps[:projects][e["project_id"].to_s] || "-"
296
305
  service = maps[:services][e["service_id"].to_s] || "-"
297
- note = (e["note"] || "").slice(0, 50)
306
+ note = e["note"] || ""
298
307
  hours = (e["duration"].to_i / 3600.0).round(2)
299
308
  [e["id"].to_s, date, client, project, service, note, "#{hours}h"]
300
309
  end
301
310
 
302
- print_table(["ID", "Date", "Client", "Project", "Service", "Note", "Duration"], rows)
311
+ print_table(["ID", "Date", "Client", "Project", "Service", "Note", "Duration"], rows, wrap_col: 5)
303
312
 
304
313
  total = entries.sum { |e| e["duration"].to_i } / 3600.0
305
314
  puts "\nTotal: #{total.round(2)}h"
@@ -586,6 +595,7 @@ module FB
586
595
 
587
596
  def interactive?
588
597
  return false if options[:no_interactive]
598
+ return true if options[:interactive]
589
599
  $stdin.tty?
590
600
  end
591
601
 
@@ -747,15 +757,56 @@ module FB
747
757
  input
748
758
  end
749
759
 
750
- def print_table(headers, rows)
760
+ def print_table(headers, rows, wrap_col: nil)
751
761
  widths = headers.each_with_index.map do |h, i|
752
762
  [h.length, *rows.map { |r| r[i].to_s.length }].max
753
763
  end
754
764
 
765
+ # Word-wrap a specific column if it would exceed terminal width
766
+ if wrap_col
767
+ term_width = IO.console&.winsize&.last || ENV["COLUMNS"]&.to_i || 120
768
+ fixed_width = widths.each_with_index.sum { |w, i| i == wrap_col ? 0 : w } + (widths.length - 1) * 2
769
+ max_wrap = term_width - fixed_width
770
+ max_wrap = [max_wrap, 20].max
771
+ widths[wrap_col] = [widths[wrap_col], max_wrap].min
772
+ end
773
+
755
774
  fmt = widths.map { |w| "%-#{w}s" }.join(" ")
756
775
  puts fmt % headers
757
776
  puts widths.map { |w| "-" * w }.join(" ")
758
- rows.each { |r| puts fmt % r }
777
+
778
+ rows.each do |r|
779
+ if wrap_col && r[wrap_col].to_s.length > widths[wrap_col]
780
+ lines = word_wrap(r[wrap_col].to_s, widths[wrap_col])
781
+ padded = widths.each_with_index.map { |w, i| i == wrap_col ? "" : " " * w }
782
+ pad_fmt = padded.each_with_index.map { |p, i| i == wrap_col ? "%s" : "%-#{widths[i]}s" }.join(" ")
783
+ lines.each_with_index do |line, li|
784
+ if li == 0
785
+ row = r.dup
786
+ row[wrap_col] = line
787
+ puts fmt % row
788
+ else
789
+ blank = padded.dup
790
+ blank[wrap_col] = line
791
+ puts pad_fmt % blank
792
+ end
793
+ end
794
+ else
795
+ puts fmt % r
796
+ end
797
+ end
798
+ end
799
+
800
+ def word_wrap(text, width)
801
+ lines = []
802
+ remaining = text
803
+ while remaining.length > width
804
+ break_at = remaining.rindex(" ", width) || width
805
+ lines << remaining[0...break_at]
806
+ remaining = remaining[break_at..].lstrip
807
+ end
808
+ lines << remaining unless remaining.empty?
809
+ lines
759
810
  end
760
811
 
761
812
  def print_status_section(title, entries, maps)
data/lib/fb/spinner.rb CHANGED
@@ -4,15 +4,31 @@ module FB
4
4
  module Spinner
5
5
  FRAMES = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏].freeze
6
6
 
7
+ @interactive = nil
8
+
9
+ def self.interactive=(value)
10
+ @interactive = value
11
+ end
12
+
13
+ def self.interactive?
14
+ return @interactive unless @interactive.nil?
15
+ $stderr.tty?
16
+ end
17
+
7
18
  def self.spin(message)
8
- done = false
9
19
  result = nil
10
20
 
21
+ unless interactive?
22
+ result = yield
23
+ return result
24
+ end
25
+
26
+ done = false
11
27
  thread = Thread.new do
12
28
  i = 0
13
29
  while !done
14
- print "\r#{FRAMES[i % FRAMES.length]} #{message}"
15
- $stdout.flush
30
+ $stderr.print "\r#{FRAMES[i % FRAMES.length]} #{message}"
31
+ $stderr.flush
16
32
  i += 1
17
33
  sleep 0.08
18
34
  end
@@ -23,7 +39,7 @@ module FB
23
39
  ensure
24
40
  done = true
25
41
  thread.join
26
- print "\r✓ #{message}\n"
42
+ $stderr.print "\r✓ #{message}\n"
27
43
  end
28
44
 
29
45
  result
data/lib/fb/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FB
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freshbooks-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - parasquid