telescope-term 1.5 → 2.0.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/telescope +65 -44
  3. metadata +7 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86323c41ff9b3285feda98f66c9a2ff5af15af38101a2b7fdedde5d2238fc4d2
4
- data.tar.gz: 2cc345bbe5d0b06f05a29e6020b7684cb5655edd4daf6ed16edeb106d8167fa0
3
+ metadata.gz: c74c6c6a0d418a1418cf442f5a67763a20308eee58c24bbbd98377c363ec4373
4
+ data.tar.gz: 59a75595a9ea9db796e7d492e184fd46c37d5114cef29947a2f16d39088be193
5
5
  SHA512:
6
- metadata.gz: 63dcd8986a007b965ef4a4dca822774cc4a714b3ce9d71bc703806984077f4e56b6327b5baad3805dd1bef10cb9e13b4b453abc02f8de1c07e781cddc5a9ed69
7
- data.tar.gz: d848b5ce07f6e5dabec1e0934c3c321a495783131b2c1e30e7027d2493c49635f73ac997605c11bbd46858447f613745de1410a39a65a169e1104f4a4ef02164
6
+ metadata.gz: 1fd26d33a924c73dbb95798ea53e02fb82579bee93a12caba77e02d17bfd1151fc35b7d8ca47c2687e2374f4439a8bea64b975797dd66215975ec46f98378d8b
7
+ data.tar.gz: a95af60950cce3a01c047dd5597f25703c395b40977e39931adebdc4911c37d0dfb82ecd1e7ca0719e3b7d8ea7d6816818ab1becd25bbe5f09ab8be6c28ed4b9
data/bin/telescope CHANGED
@@ -37,7 +37,7 @@ require 'json'
37
37
  require 'csv'
38
38
 
39
39
  # Version
40
- VERSION = "1.5"
40
+ VERSION = "2.0.1"
41
41
 
42
42
  # Persistence paths
43
43
  SAVE = File.join(Dir.home, '.telescope')
@@ -159,8 +159,6 @@ def create_backup
159
159
  end
160
160
 
161
161
  # Load saved data if present
162
- SAVE = File.join(Dir.home, '.telescope') # Persistence path
163
-
164
162
  if File.exist?(SAVE)
165
163
  create_backup
166
164
  load SAVE # expects plaintext: @ts = [...] and @ep = [...]
@@ -195,47 +193,49 @@ module Rcurses
195
193
  end
196
194
 
197
195
  # OPTICAL FORMULAS {{{1
198
- def tfr(app, tfl); (tfl.to_f / app); end
199
- def mlim(app); (5 * Math::log(app / 10, 10) + 7.5); end
200
- def xeye(app); (app.to_f ** 2 / 49); end
201
- def minx(app, tfl); (tfl / (7 * tfr(app, tfl))); end
202
- def mine(app, tfl); (7 * tfr(app, tfl)); end
203
- def maxx(app); (2 * app.to_f); end
204
- def maxe(app, tfl); (tfl / maxx(app)); end
205
- def sepr(app); (3600.0 * Math::asin(671E-6 / app).rad); end
206
- def sepd(app); (115.824 / app); end
207
- def e_st(app, tfl); (6.4 * tfl / app); end
208
- def e_gx(app, tfl); (3.6 * tfl / app); end
209
- def e_pl(app, tfl); (2.1 * tfl / app); end
210
- def e_2s(app, tfl); (1.3 * tfl / app); end
211
- def e_t2(app, tfl); (0.7 * tfl / app); end
212
- def moon(tfl); (384E6*Math::tan((115.824.deg / tfl)/360)); end
213
- def sun(tfl); (moon(tfl) / 2.5668); end
214
- def tfov(tfl, epfl, afov); (afov.to_f / magx(tfl, epfl)); end
215
- def pupl(app, tfl, epfl); (app.to_f / magx(tfl, epfl)); end
216
- def magx(tfl, epfl); (tfl.to_f / epfl); end
196
+ def tfr(app, tfl); return 0.0 if app.to_f == 0; (tfl.to_f / app); end
197
+ def mlim(app); return 0.0 if app.to_f <= 0; (5 * Math::log(app / 10, 10) + 7.5); end
198
+ def xeye(app); (app.to_f ** 2 / 49); end
199
+ def minx(app, tfl); r = tfr(app, tfl); return 0.0 if r == 0; (tfl / (7 * r)); end
200
+ def mine(app, tfl); (7 * tfr(app, tfl)); end
201
+ def maxx(app); (2 * app.to_f); end
202
+ def maxe(app, tfl); m = maxx(app); return 0.0 if m == 0; (tfl / m); end
203
+ def sepr(app); return 0.0 if app.to_f == 0; (3600.0 * Math::asin(671E-6 / app).rad); end
204
+ def sepd(app); return 0.0 if app.to_f == 0; (115.824 / app); end
205
+ def e_st(app, tfl); return 0.0 if app.to_f == 0; (6.4 * tfl / app); end
206
+ def e_gx(app, tfl); return 0.0 if app.to_f == 0; (3.6 * tfl / app); end
207
+ def e_pl(app, tfl); return 0.0 if app.to_f == 0; (2.1 * tfl / app); end
208
+ def e_2s(app, tfl); return 0.0 if app.to_f == 0; (1.3 * tfl / app); end
209
+ def e_t2(app, tfl); return 0.0 if app.to_f == 0; (0.7 * tfl / app); end
210
+ def moon(tfl); return 0.0 if tfl.to_f == 0; (384E6*Math::tan((115.824.deg / tfl)/360)); end
211
+ def sun(tfl); (moon(tfl) / 2.5668); end
212
+ def magx(tfl, epfl); return 0.0 if epfl.to_f == 0; (tfl.to_f / epfl); end
213
+ def tfov(tfl, epfl, afov); m = magx(tfl, epfl); return 0.0 if m == 0; (afov.to_f / m); end
214
+ def pupl(app, tfl, epfl); m = magx(tfl, epfl); return 0.0 if m == 0; (app.to_f / m); end
217
215
 
218
216
  # VALIDATION FUNCTIONS {{{1
217
+ # Returns nil on success, or an error message string on failure.
219
218
  def validate_telescope_input(input_array)
220
- return false unless input_array.size >= 3
221
-
219
+ return "Need at least: name, aperture, focal length" unless input_array.size >= 3
220
+
222
221
  name, app, fl = input_array[0..2]
223
- return false if name.nil? || name.strip.empty?
224
- return false unless app.to_f > 0
225
- return false unless fl.to_f > 0
226
-
227
- true
222
+ return "Name cannot be empty" if name.nil? || name.strip.empty?
223
+ return "Aperture must be a positive number" unless app.to_s.strip =~ /\A\d+(\.\d+)?\z/ && app.to_f > 0
224
+ return "Focal length must be a positive number" unless fl.to_s.strip =~ /\A\d+(\.\d+)?\z/ && fl.to_f > 0
225
+
226
+ nil
228
227
  end
229
228
 
229
+ # Returns nil on success, or an error message string on failure.
230
230
  def validate_eyepiece_input(input_array)
231
- return false unless input_array.size >= 3
232
-
231
+ return "Need at least: name, focal length, AFOV" unless input_array.size >= 3
232
+
233
233
  name, fl, afov = input_array[0..2]
234
- return false if name.nil? || name.strip.empty?
235
- return false unless fl.to_f > 0
236
- return false unless afov.to_f > 0 && afov.to_f <= 180
237
-
238
- true
234
+ return "Name cannot be empty" if name.nil? || name.strip.empty?
235
+ return "Focal length must be a positive number" unless fl.to_s.strip =~ /\A\d+(\.\d+)?\z/ && fl.to_f > 0
236
+ return "AFOV must be between 0 and 180 degrees" unless afov.to_s.strip =~ /\A\d+(\.\d+)?\z/ && afov.to_f > 0 && afov.to_f <= 180
237
+
238
+ nil
239
239
  end
240
240
 
241
241
  def safe_file_write(file, content)
@@ -306,6 +306,7 @@ def render_ts #{{{2
306
306
  end
307
307
 
308
308
  def ep_nice(app, tfl, e) #{{{2
309
+ return '' if app.to_f == 0
309
310
  r = (tfl / app)
310
311
  out = ''
311
312
  # Enhanced color coding with background highlights for optimal ranges - aligned with header
@@ -356,6 +357,13 @@ def ep_nice(app, tfl, e) #{{{2
356
357
  end
357
358
 
358
359
  def render_ep #{{{2
360
+ if @ts.empty?
361
+ @pEP.text = "\n Add a telescope first (press 't')"
362
+ @pEP.refresh
363
+ @pEPh.full_refresh
364
+ return
365
+ end
366
+ @pTS.index = [[@pTS.index, 0].max, @ts.size - 1].min
359
367
  app = @ts[@pTS.index][1].to_f
360
368
  tfl = @ts[@pTS.index][2].to_f
361
369
  @pEP.text = "\n"
@@ -597,6 +605,8 @@ Signal.trap('WINCH') do
597
605
  end
598
606
 
599
607
  # MAIN LOOP {{{1
608
+ # Initialize rcurses (required for rcurses 6.0.0+)
609
+ Rcurses.init!
600
610
  refresh_all
601
611
  loop do
602
612
  render_ts
@@ -621,8 +631,9 @@ loop do
621
631
  refresh_all
622
632
  when 't'
623
633
  inp=@pTSa.ask('name, app, fl [, notes]: ','').split(', ')
624
- unless validate_telescope_input(inp)
625
- @pST.say(" Invalid input - Press any key")
634
+ err = validate_telescope_input(inp)
635
+ if err
636
+ @pST.say(" #{err} - Press any key")
626
637
  getchr
627
638
  next
628
639
  end
@@ -633,8 +644,9 @@ loop do
633
644
  @current = @ts
634
645
  when 'e'
635
646
  inp=@pEPa.ask('name, fl, afov [, notes]: ','').split(', ')
636
- unless validate_eyepiece_input(inp)
637
- @pST.say(" Invalid input - Press any key")
647
+ err = validate_eyepiece_input(inp)
648
+ if err
649
+ @pST.say(" #{err} - Press any key")
638
650
  getchr
639
651
  next
640
652
  end
@@ -644,25 +656,29 @@ loop do
644
656
  @ep_unsorted << inp # keep master list updated
645
657
  @current = @ep
646
658
  when 'ENTER'
659
+ next if @current.empty?
647
660
  val = @current[@focus.index].join(', ')
648
661
  arr=@pST.ask('Edit: ', val).split(', ')
649
- is_valid = @current.equal?(@ts) ? validate_telescope_input(arr) : validate_eyepiece_input(arr)
650
- unless is_valid
651
- @pST.say(" Invalid input - Press any key")
662
+ err = @current.equal?(@ts) ? validate_telescope_input(arr) : validate_eyepiece_input(arr)
663
+ if err
664
+ @pST.say(" #{err} - Press any key")
652
665
  getchr
653
666
  next
654
667
  end
655
668
  arr << '' if arr.size == 3 # Add empty notes if not provided
656
669
  @current[@focus.index] = arr
657
670
  when 'D'
671
+ next if @current.empty?
658
672
  if @current.equal?(@ts)
659
673
  removed = @ts.delete_at(@focus.index)
660
674
  @ts_unsorted.delete(removed)
661
675
  @tstag.delete_at(@focus.index)
676
+ @pTS.index = [[@pTS.index, 0].max, @ts.size - 1].min
662
677
  else
663
678
  removed = @ep.delete_at(@focus.index)
664
679
  @ep_unsorted.delete(removed)
665
680
  @eptag.delete_at(@focus.index)
681
+ @pEP.index = [[@pEP.index, 0].max, @ep.size - 1].min
666
682
  end
667
683
  when 'TAB'
668
684
  @focus = (@focus == @pTS ? @pEP : @pTS)
@@ -673,20 +689,24 @@ loop do
673
689
  @pTS.border_refresh
674
690
  @pEP.border_refresh
675
691
  when 'UP'
692
+ next if @current.empty?
676
693
  @focus.index -= 1
677
694
  @focus.index = @current.length - 1 if @focus.index < 0
678
695
  when 'DOWN'
696
+ next if @current.empty?
679
697
  @focus.index += 1
680
698
  @focus.index = 0 if @focus.index > @current.length - 1
681
- @focus == :ts ? @cursor_ts = 0 : @cursor_ep = 0
682
699
  when 'HOME'
683
700
  @focus.index = 0
684
701
  when 'END'
702
+ next if @current.empty?
685
703
  @focus.index = @current.length - 1
686
704
  when 'S-UP'
705
+ next if @current.empty?
687
706
  @current.insert([@focus.index - 1, 0].max, @current.delete_at(@focus.index))
688
707
  @focus.index -= 1 if @focus.index != 0
689
708
  when 'S-DOWN'
709
+ next if @current.empty?
690
710
  @current.insert([@focus.index + 1, @current.size - 1].min, @current.delete_at(@focus.index))
691
711
  @focus.index += 1 if @focus.index != @current.size - 1
692
712
  when 'o' # Toggle-sort the currently focused list
@@ -710,6 +730,7 @@ loop do
710
730
  @pEP.index = [@pEP.index, @ep.size - 1].min
711
731
  end
712
732
  when ' ' # SPACE: tag/untag current entry
733
+ next if @current.empty?
713
734
  if @current.equal?(@ts)
714
735
  idx = @pTS.index
715
736
  @tstag[idx] = !@tstag[idx]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telescope-term
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.5'
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-06 00:00:00.000000000 Z
11
+ date: 2026-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses
@@ -16,21 +16,19 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.5'
19
+ version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.5'
26
+ version: '6.0'
27
27
  description: 'With this program you can list your telescopes and eyepieces and get
28
28
  a set of calculations done for each scope and for the combination of scope and eyepiece.
29
- Easy interface. Run the program, then hit ''?'' to show the help file. Version 1.0:
30
- A full rewrite using the rcurses library (https://github.com/isene/rcurses) - lots
31
- of improvements. 1.1: Removed tty startup/exit codes as rcurses now handles that.
32
- 1.5: Major feature update with notes, export, configuration, validation, backups,
33
- and enhanced UI.'
29
+ Easy interface. Run the program, then hit ''?'' to show the help file. Version 2.0.1:
30
+ Improved input validation with descriptive errors, division-by-zero guards, empty
31
+ list safety.'
34
32
  email: g@isene.com
35
33
  executables:
36
34
  - telescope