telescope-term 2.0 → 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 +63 -44
  3. metadata +5 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ea52b6c70e7459e1d2883070863c43c61ea83254c894d14205b75f664b10419a
4
- data.tar.gz: 262471d0faf1eaee00418bf48969cfa0675cc5bfb0167ad4f3427d64b342c240
3
+ metadata.gz: c74c6c6a0d418a1418cf442f5a67763a20308eee58c24bbbd98377c363ec4373
4
+ data.tar.gz: 59a75595a9ea9db796e7d492e184fd46c37d5114cef29947a2f16d39088be193
5
5
  SHA512:
6
- metadata.gz: e5d1d868063f0eb0d1b0f6028e43851e9c320c699bd32718c18df9050ff6dde8b85cf14246df84f43f10b129066698a1548c0f3d97f38374b6ccfc5495fb3bbe
7
- data.tar.gz: 59da347391bd03414dfcbb92a0651b189d5df5825135e437e8c9e1d9ee2f608ee9e5a5d86d8a3c58dfb7417c3de373250843463d6bbc21533136ad3fdc5f8180
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 = "2.0"
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"
@@ -623,8 +631,9 @@ loop do
623
631
  refresh_all
624
632
  when 't'
625
633
  inp=@pTSa.ask('name, app, fl [, notes]: ','').split(', ')
626
- unless validate_telescope_input(inp)
627
- @pST.say(" Invalid input - Press any key")
634
+ err = validate_telescope_input(inp)
635
+ if err
636
+ @pST.say(" #{err} - Press any key")
628
637
  getchr
629
638
  next
630
639
  end
@@ -635,8 +644,9 @@ loop do
635
644
  @current = @ts
636
645
  when 'e'
637
646
  inp=@pEPa.ask('name, fl, afov [, notes]: ','').split(', ')
638
- unless validate_eyepiece_input(inp)
639
- @pST.say(" Invalid input - Press any key")
647
+ err = validate_eyepiece_input(inp)
648
+ if err
649
+ @pST.say(" #{err} - Press any key")
640
650
  getchr
641
651
  next
642
652
  end
@@ -646,25 +656,29 @@ loop do
646
656
  @ep_unsorted << inp # keep master list updated
647
657
  @current = @ep
648
658
  when 'ENTER'
659
+ next if @current.empty?
649
660
  val = @current[@focus.index].join(', ')
650
661
  arr=@pST.ask('Edit: ', val).split(', ')
651
- is_valid = @current.equal?(@ts) ? validate_telescope_input(arr) : validate_eyepiece_input(arr)
652
- unless is_valid
653
- @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")
654
665
  getchr
655
666
  next
656
667
  end
657
668
  arr << '' if arr.size == 3 # Add empty notes if not provided
658
669
  @current[@focus.index] = arr
659
670
  when 'D'
671
+ next if @current.empty?
660
672
  if @current.equal?(@ts)
661
673
  removed = @ts.delete_at(@focus.index)
662
674
  @ts_unsorted.delete(removed)
663
675
  @tstag.delete_at(@focus.index)
676
+ @pTS.index = [[@pTS.index, 0].max, @ts.size - 1].min
664
677
  else
665
678
  removed = @ep.delete_at(@focus.index)
666
679
  @ep_unsorted.delete(removed)
667
680
  @eptag.delete_at(@focus.index)
681
+ @pEP.index = [[@pEP.index, 0].max, @ep.size - 1].min
668
682
  end
669
683
  when 'TAB'
670
684
  @focus = (@focus == @pTS ? @pEP : @pTS)
@@ -675,20 +689,24 @@ loop do
675
689
  @pTS.border_refresh
676
690
  @pEP.border_refresh
677
691
  when 'UP'
692
+ next if @current.empty?
678
693
  @focus.index -= 1
679
694
  @focus.index = @current.length - 1 if @focus.index < 0
680
695
  when 'DOWN'
696
+ next if @current.empty?
681
697
  @focus.index += 1
682
698
  @focus.index = 0 if @focus.index > @current.length - 1
683
- @focus == :ts ? @cursor_ts = 0 : @cursor_ep = 0
684
699
  when 'HOME'
685
700
  @focus.index = 0
686
701
  when 'END'
702
+ next if @current.empty?
687
703
  @focus.index = @current.length - 1
688
704
  when 'S-UP'
705
+ next if @current.empty?
689
706
  @current.insert([@focus.index - 1, 0].max, @current.delete_at(@focus.index))
690
707
  @focus.index -= 1 if @focus.index != 0
691
708
  when 'S-DOWN'
709
+ next if @current.empty?
692
710
  @current.insert([@focus.index + 1, @current.size - 1].min, @current.delete_at(@focus.index))
693
711
  @focus.index += 1 if @focus.index != @current.size - 1
694
712
  when 'o' # Toggle-sort the currently focused list
@@ -712,6 +730,7 @@ loop do
712
730
  @pEP.index = [@pEP.index, @ep.size - 1].min
713
731
  end
714
732
  when ' ' # SPACE: tag/untag current entry
733
+ next if @current.empty?
715
734
  if @current.equal?(@ts)
716
735
  idx = @pTS.index
717
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: '2.0'
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-08-15 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
@@ -26,9 +26,9 @@ dependencies:
26
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 2.0:
30
- Breaking change - requires rcurses 6.0.0+ with explicit initialization for Ruby
31
- 3.4+ compatibility.'
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.'
32
32
  email: g@isene.com
33
33
  executables:
34
34
  - telescope