darksky-ruby 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 62a900ed0c6248ca0f923c8d21d9313d91710420
4
- data.tar.gz: 87a437d159fca922f16601a92dbb947e841e93b7
2
+ SHA256:
3
+ metadata.gz: cbc838749139b38df86429dd389d8758c5202fd9c77cf4ee7c8c3dc195e79ab3
4
+ data.tar.gz: 0ef99dc1e4d4d3dccf3b206118da701b2655a89e05576fee1f6ddd3e366cf809
5
5
  SHA512:
6
- metadata.gz: 6b2b0f2b5e01e70b14512cafe490a317e8c18550aa13a996a27ef8f327a4d08eace4a8616d65d206128e55c4fe013665ffd132ff8d52b941161a28f6405a352b
7
- data.tar.gz: 91fe232ff29d2d4e3fe70e6e08c90a64aa20b0ce07ffecc4d548783616befe65652d3e036cd9b873e60638b10b69eebe0e065bee9575174b6b019e56ea6b1f45
6
+ metadata.gz: 18eb161d289183c5b59dcef356665988db7e2f34d1d6a77d32a74f951b565f1960a5c65e6e95a9480b57622452a9fea8b2d1cdabbc8b34eb5eab6ad7518915fc
7
+ data.tar.gz: 2e7f16e29f5e1f566a55cabfb2fb58652cf02f66b4960eb9858b97efe76fe860c7d4fd1d0563f4d844ec14518de3b2be99640100197dc7c09b59b8f34925fb09
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 Ken J.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # darksky-ruby
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/kajiki.svg)](http://badge.fury.io/rb/kajiki)
4
+
5
+ Pure simple Ruby based [Dark Sky API](https://darksky.net/dev/) gem
6
+
7
+ ## Install
8
+
9
+ ```
10
+ $ gem install darksky-ruby
11
+ ```
12
+
13
+ ## Use
14
+
15
+ ### [Forecast Request](https://darksky.net/dev/docs#forecast-request)
16
+
17
+ Example of querying weather forecast for [SFO](https://www.airport-sfo.com/).
18
+
19
+ ```ruby
20
+ require 'darksky-ruby'
21
+
22
+ api = DarkSkyAPI.new(key: 'Your_Dark_Sky_API_Secret_Key')
23
+ data = api.forecast(lat: 37.6211, lon: -122.383)
24
+ p data[:hourly][:summary]
25
+ # => "Partly cloudy throughout the day."
26
+ ```
27
+
28
+ ### [Time Machine Request](https://darksky.net/dev/docs#time-machine-request)
29
+
30
+ Requesting observed weather for noon of Jan. 1, 2018 at SFO.
31
+
32
+ ```ruby
33
+ data = api.timemachine(lat: 37.6211, lon: -122.383, ts: Time.new(2018,1,1,12))
34
+ p data[:currently][:temperature]
35
+ # => 57.56
36
+ ```
37
+
38
+ ### [Response Format](https://darksky.net/dev/docs#response-format)
39
+
40
+ `data` in above examples would contain a Ruby Hash of the entire API response; all keys are symbolized.
41
+
42
+ ### Options
43
+
44
+ You can limit the response data size by excluding unnecessary blocks.
45
+
46
+ ```ruby
47
+ api.blocks = {minutely: false, hourly: false} # excludes blocks marked false
48
+ api.include_only([:currently, :alerts]) # excludes everything except specified
49
+ api.blocks
50
+ # => {:currently=>true, :minutely=>false, :hourly=>false, :daily=>false,
51
+ # :alerts=>true, :flags=>false}
52
+ ```
53
+
54
+ Hint, you can use `api.blocks` first to get a default Hash to get started. After you've modified the Hash, you can save it with `api.blocks =`.
55
+
56
+ Other options can be set like so.
57
+
58
+ ```ruby
59
+ api.options = {lang: 'es', units: 'si'} # Spanish language, SI units
60
+ data = api.timemachine(lat: 37.6211, lon: -122.383, ts: Time.new(2018,1,1,12))
61
+ p data[:currently][:summary]
62
+ # => "Parcialmente Nublado"
63
+ p data[:currently][:temperature]
64
+ # => 14.2
65
+ ```
66
+
67
+ ## CLI
68
+
69
+ This gem includes an executable as an example.
70
+
71
+ ```
72
+ $ darksky -h
73
+ darksky [options] <LAT,LON>
74
+ -k, --key=<s> API secret key
75
+ -l, --loc=<s> Location (latitude,longtitude)
76
+ -o, --log=<s> Log file
77
+ -t, --time=<s> Timestamp for Time Machine request
78
+ -v, --verbose Verbose mode
79
+ -h, --help Show this message
80
+ ```
81
+
82
+ ## More
83
+
84
+ Since this is a simple gem with no external dependencies, you can directly include the `lib` contents in your project if you prefer not to use Ruby Gems, such as in [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/ruby-package.html). If you do, be sure to include my copyright and license details.
data/bin/darksky CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
- require 'darksky-ruby/trollop'
2
+ require 'darksky-ruby/optimist'
3
3
  require 'darksky-ruby'
4
4
 
5
5
 
6
- opts = Trollop::options do
6
+ opts = Optimist::options do
7
7
  banner "darksky [options] <LAT,LON>"
8
8
  opt :key, 'API secret key', type: :string
9
9
  opt :loc, 'Location (latitude,longtitude)', type: :string
@@ -28,9 +28,9 @@ log.debug("Command line arguments: #{opts}")
28
28
  loc = opts[:loc]
29
29
  loc ||= ARGV.shift
30
30
 
31
- Trollop::die :loc, "is missing" if loc.nil?
31
+ Optimist::die :loc, "is missing" if loc.nil?
32
32
 
33
- api = DarkskyAPI.new(key: opts[:key])
33
+ api = DarkSkyAPI.new(key: opts[:key])
34
34
  api.blocks = {minutely: false, hourly: false, daily: false, alerts: false, flags: false}
35
35
 
36
36
  if opts[:time_given]
data/darksky-ruby.gemspec CHANGED
@@ -1,14 +1,14 @@
1
1
  $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
2
- require 'darksky-ruby/version'
2
+ require 'darksky-ruby'
3
3
 
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'darksky-ruby'
7
- s.version = Darksky::Version
7
+ s.version = '1.0.0'
8
8
  s.authors = ['Ken J.']
9
9
  s.email = ['kenjij@gmail.com']
10
- s.summary = %q{Pure simple Ruby based Darksky REST library}
11
- s.description = %q{Darksky library written in pure Ruby without external dependency.}
10
+ s.summary = %q{Pure simple Ruby based Dark Sky API gem}
11
+ s.description = %q{Dark Sky gem written in pure Ruby without any external dependency.}
12
12
  s.homepage = 'https://github.com/kenjij/darksky-ruby'
13
13
  s.license = 'MIT'
14
14
 
@@ -1,6 +1,6 @@
1
1
  require 'json'
2
2
 
3
- class DarkskyAPI
3
+ class DarkSkyAPI
4
4
  DARKSKY_URL = 'https://api.darksky.net/'
5
5
  DARKSKY_PATH_TEMPLATE = '/forecast/%{key}/%{loc}'
6
6
  DARKSKY_BLOCK_NAMES = [:currently, :minutely, :hourly, :daily, :alerts, :flags]
@@ -1,17 +1,17 @@
1
- # lib/trollop.rb -- trollop command-line processing library
1
+ # lib/optimist.rb -- optimist command-line processing library
2
2
  # Copyright (c) 2008-2014 William Morgan.
3
3
  # Copyright (c) 2014 Red Hat, Inc.
4
- # trollop is licensed under the MIT license.
4
+ # optimist is licensed under the MIT license.
5
5
 
6
6
  require 'date'
7
7
 
8
- module Trollop
8
+ module Optimist
9
9
  # note: this is duplicated in gemspec
10
10
  # please change over there too
11
- VERSION = "2.1.2"
11
+ VERSION = "3.0.0"
12
12
 
13
13
  ## Thrown by Parser in the event of a commandline error. Not needed if
14
- ## you're using the Trollop::options entry.
14
+ ## you're using the Optimist::options entry.
15
15
  class CommandlineError < StandardError
16
16
  attr_reader :error_code
17
17
 
@@ -22,12 +22,12 @@ class CommandlineError < StandardError
22
22
  end
23
23
 
24
24
  ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
25
- ## automatically by Trollop#options.
25
+ ## automatically by Optimist#options.
26
26
  class HelpNeeded < StandardError
27
27
  end
28
28
 
29
29
  ## Thrown by Parser if the user passes in '-v' or '--version'. Handled
30
- ## automatically by Trollop#options.
30
+ ## automatically by Optimist#options.
31
31
  class VersionNeeded < StandardError
32
32
  end
33
33
 
@@ -38,15 +38,38 @@ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))([eE][-+]?[\d]+)?$/
38
38
  PARAM_RE = /^-(-|\.$|[^\d\.])/
39
39
 
40
40
  ## The commandline parser. In typical usage, the methods in this class
41
- ## will be handled internally by Trollop::options. In this case, only the
41
+ ## will be handled internally by Optimist::options. In this case, only the
42
42
  ## #opt, #banner and #version, #depends, and #conflicts methods will
43
43
  ## typically be called.
44
44
  ##
45
45
  ## If you want to instantiate this class yourself (for more complicated
46
46
  ## argument-parsing logic), call #parse to actually produce the output hash,
47
47
  ## and consider calling it from within
48
- ## Trollop::with_standard_exception_handling.
48
+ ## Optimist::with_standard_exception_handling.
49
49
  class Parser
50
+
51
+ ## The registry is a class-instance-variable map of option aliases to their subclassed Option class.
52
+ @registry = {}
53
+
54
+ ## The Option subclasses are responsible for registering themselves using this function.
55
+ def self.register(lookup, klass)
56
+ @registry[lookup.to_sym] = klass
57
+ end
58
+
59
+ ## Gets the class from the registry.
60
+ ## Can be given either a class-name, e.g. Integer, a string, e.g "integer", or a symbol, e.g :integer
61
+ def self.registry_getopttype(type)
62
+ return nil unless type
63
+ if type.respond_to?(:name)
64
+ type = type.name
65
+ lookup = type.downcase.to_sym
66
+ else
67
+ lookup = type.to_sym
68
+ end
69
+ raise ArgumentError, "Unsupported argument type '#{type}', registry lookup '#{lookup}'" unless @registry.has_key?(lookup)
70
+ return @registry[lookup].new
71
+ end
72
+
50
73
  INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
51
74
 
52
75
  ## The values from the commandline that were not interpreted by #parse.
@@ -75,7 +98,7 @@ class Parser
75
98
  @educate_on_error = false
76
99
  @synopsis = nil
77
100
  @usage = nil
78
-
101
+
79
102
  # instance_eval(&b) if b # can't take arguments
80
103
  cloaker(&b).bind(self).call(*a) if b
81
104
  end
@@ -90,7 +113,7 @@ class Parser
90
113
  ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
91
114
  ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. Use :none: to not have a short value.
92
115
  ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
93
- ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
116
+ ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Optimist::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
94
117
  ## [+:required+] If set to +true+, the argument must be provided on the commandline.
95
118
  ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
96
119
  ##
@@ -116,7 +139,7 @@ class Parser
116
139
  ## There's one ambiguous case to be aware of: when +:multi+: is true and a
117
140
  ## +:default+ is set to an array (of something), it's ambiguous whether this
118
141
  ## is a multi-value argument as well as a multi-occurrence argument.
119
- ## In thise case, Trollop assumes that it's not a multi-value argument.
142
+ ## In thise case, Optimist assumes that it's not a multi-value argument.
120
143
  ## If you want a multi-value, multi-occurrence argument with a default
121
144
  ## value, you must specify +:type+ as well.
122
145
 
@@ -164,7 +187,7 @@ class Parser
164
187
 
165
188
  ## Marks two (or more!) options as requiring each other. Only handles
166
189
  ## undirected (i.e., mutual) dependencies. Directed dependencies are
167
- ## better modeled with Trollop::die.
190
+ ## better modeled with Optimist::die.
168
191
  def depends(*syms)
169
192
  syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
170
193
  @constraints << [:depends, syms]
@@ -182,7 +205,7 @@ class Parser
182
205
  ## intact.
183
206
  ##
184
207
  ## A typical use case would be for subcommand support, where these
185
- ## would be set to the list of subcommands. A subsequent Trollop
208
+ ## would be set to the list of subcommands. A subsequent Optimist
186
209
  ## invocation would then be used to parse subcommand options, after
187
210
  ## shifting the subcommand off of ARGV.
188
211
  def stop_on(*words)
@@ -203,7 +226,7 @@ class Parser
203
226
  @educate_on_error = true
204
227
  end
205
228
 
206
- ## Parses the commandline. Typically called by Trollop::options,
229
+ ## Parses the commandline. Typically called by Optimist::options,
207
230
  ## but you can call it directly if you need more control.
208
231
  ##
209
232
  ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.
@@ -240,7 +263,7 @@ class Parser
240
263
 
241
264
  sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments
242
265
 
243
- next 0 if ignore_invalid_options && !sym
266
+ next nil if ignore_invalid_options && !sym
244
267
  raise CommandlineError, "unknown argument '#{arg}'" unless sym
245
268
 
246
269
  if given_args.include?(sym) && !@specs[sym].multi?
@@ -255,7 +278,7 @@ class Parser
255
278
  # The block returns the number of parameters taken.
256
279
  num_params_taken = 0
257
280
 
258
- unless params.nil?
281
+ unless params.empty?
259
282
  if @specs[sym].single_arg?
260
283
  given_args[sym][:params] << params[0, 1] # take the first parameter
261
284
  num_params_taken = 1
@@ -301,20 +324,7 @@ class Parser
301
324
 
302
325
  vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
303
326
 
304
- case opts.type
305
- when :flag
306
- vals[sym] = (sym.to_s =~ /^no_/ ? negative_given : !negative_given)
307
- when :int, :ints
308
- vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
309
- when :float, :floats
310
- vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
311
- when :string, :strings
312
- vals[sym] = params.map { |pg| pg.map(&:to_s) }
313
- when :io, :ios
314
- vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
315
- when :date, :dates
316
- vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
317
- end
327
+ vals[sym] = opts.parse(params, negative_given)
318
328
 
319
329
  if opts.single_arg?
320
330
  if opts.multi? # multiple options, each with a single parameter
@@ -344,41 +354,13 @@ class Parser
344
354
  vals
345
355
  end
346
356
 
347
- def parse_date_parameter(param, arg) #:nodoc:
348
- begin
349
- require 'chronic'
350
- time = Chronic.parse(param)
351
- rescue LoadError
352
- # chronic is not available
353
- end
354
- time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
355
- rescue ArgumentError
356
- raise CommandlineError, "option '#{arg}' needs a date"
357
- end
358
-
359
357
  ## Print the help message to +stream+.
360
358
  def educate(stream = $stdout)
361
359
  width # hack: calculate it now; otherwise we have to be careful not to
362
360
  # call this unless the cursor's at the beginning of a line.
361
+
363
362
  left = {}
364
- @specs.each do |name, spec|
365
- left[name] =
366
- (spec.short? ? "-#{spec.short}, " : "") + "--#{spec.long}" +
367
- case spec.type
368
- when :flag then ""
369
- when :int then "=<i>"
370
- when :ints then "=<i+>"
371
- when :string then "=<s>"
372
- when :strings then "=<s+>"
373
- when :float then "=<f>"
374
- when :floats then "=<f+>"
375
- when :io then "=<filename/uri>"
376
- when :ios then "=<filename/uri+>"
377
- when :date then "=<date>"
378
- when :dates then "=<date+>"
379
- end +
380
- (spec.flag? && spec.default ? ", --no-#{spec.long}" : "")
381
- end
363
+ @specs.each { |name, spec| left[name] = spec.educate }
382
364
 
383
365
  leftcol_width = left.values.map(&:length).max || 0
384
366
  rightcol_start = leftcol_width + 6 # spaces
@@ -400,27 +382,8 @@ class Parser
400
382
 
401
383
  spec = @specs[opt]
402
384
  stream.printf " %-#{leftcol_width}s ", left[opt]
403
- desc = spec.desc + begin
404
- default_s = case spec.default
405
- when $stdout then "<stdout>"
406
- when $stdin then "<stdin>"
407
- when $stderr then "<stderr>"
408
- when Array
409
- spec.default.join(", ")
410
- else
411
- spec.default.to_s
412
- end
385
+ desc = spec.description_with_default
413
386
 
414
- if spec.default
415
- if spec.desc =~ /\.$/
416
- " (Default: #{default_s})"
417
- else
418
- " (default: #{default_s})"
419
- end
420
- else
421
- ""
422
- end
423
- end
424
387
  stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
425
388
  end
426
389
  end
@@ -460,8 +423,9 @@ class Parser
460
423
  end
461
424
  end
462
425
 
463
- ## The per-parser version of Trollop::die (see that for documentation).
426
+ ## The per-parser version of Optimist::die (see that for documentation).
464
427
  def die(arg, msg = nil, error_code = nil)
428
+ msg, error_code = nil, msg if msg.kind_of?(Integer)
465
429
  if msg
466
430
  $stderr.puts "Error: argument --#{@specs[arg].long} #{msg}."
467
431
  else
@@ -489,47 +453,60 @@ private
489
453
  when /^--$/ # arg terminator
490
454
  return remains += args[(i + 1)..-1]
491
455
  when /^--(\S+?)=(.*)$/ # long argument with equals
492
- yield "--#{$1}", [$2]
456
+ num_params_taken = yield "--#{$1}", [$2]
457
+ if num_params_taken.nil?
458
+ remains << args[i]
459
+ if @stop_on_unknown
460
+ return remains += args[i + 1..-1]
461
+ end
462
+ end
493
463
  i += 1
494
464
  when /^--(\S+)$/ # long argument
495
465
  params = collect_argument_parameters(args, i + 1)
496
- if params.empty?
497
- yield args[i], nil
498
- i += 1
499
- else
500
- num_params_taken = yield args[i], params
501
- unless num_params_taken
502
- if @stop_on_unknown
503
- return remains += args[i + 1..-1]
504
- else
505
- remains += params
506
- end
466
+ num_params_taken = yield args[i], params
467
+
468
+ if num_params_taken.nil?
469
+ remains << args[i]
470
+ if @stop_on_unknown
471
+ return remains += args[i + 1..-1]
507
472
  end
508
- i += 1 + num_params_taken
473
+ else
474
+ i += num_params_taken
509
475
  end
476
+ i += 1
510
477
  when /^-(\S+)$/ # one or more short arguments
478
+ short_remaining = ""
511
479
  shortargs = $1.split(//)
512
480
  shortargs.each_with_index do |a, j|
513
481
  if j == (shortargs.length - 1)
514
482
  params = collect_argument_parameters(args, i + 1)
515
- if params.empty?
516
- yield "-#{a}", nil
517
- i += 1
518
- else
519
- num_params_taken = yield "-#{a}", params
520
- unless num_params_taken
521
- if @stop_on_unknown
522
- return remains += args[i + 1..-1]
523
- else
524
- remains += params
525
- end
483
+
484
+ num_params_taken = yield "-#{a}", params
485
+ unless num_params_taken
486
+ short_remaining << a
487
+ if @stop_on_unknown
488
+ remains << "-#{short_remaining}"
489
+ return remains += args[i + 1..-1]
526
490
  end
527
- i += 1 + num_params_taken
491
+ else
492
+ i += num_params_taken
528
493
  end
529
494
  else
530
- yield "-#{a}", nil
495
+ unless yield "-#{a}", []
496
+ short_remaining << a
497
+ if @stop_on_unknown
498
+ short_remaining += shortargs[j + 1..-1].join
499
+ remains << "-#{short_remaining}"
500
+ return remains += args[i + 1..-1]
501
+ end
502
+ end
531
503
  end
532
504
  end
505
+
506
+ unless short_remaining.empty?
507
+ remains << "-#{short_remaining}"
508
+ end
509
+ i += 1
533
510
  else
534
511
  if @stop_on_unknown
535
512
  return remains += args[i..-1]
@@ -543,29 +520,6 @@ private
543
520
  remains
544
521
  end
545
522
 
546
- def parse_integer_parameter(param, arg)
547
- raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^-?[\d_]+$/
548
- param.to_i
549
- end
550
-
551
- def parse_float_parameter(param, arg)
552
- raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
553
- param.to_f
554
- end
555
-
556
- def parse_io_parameter(param, arg)
557
- if param =~ /^(stdin|-)$/i
558
- $stdin
559
- else
560
- require 'open-uri'
561
- begin
562
- open param
563
- rescue SystemCallError => e
564
- raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}"
565
- end
566
- end
567
- end
568
-
569
523
  def collect_argument_parameters(args, start_at)
570
524
  params = []
571
525
  pos = start_at
@@ -621,173 +575,318 @@ private
621
575
  end
622
576
  end
623
577
 
624
- ## The option for each flag
625
578
  class Option
626
- ## The set of values that indicate a flag option when passed as the
627
- ## +:type+ parameter of #opt.
628
- FLAG_TYPES = [:flag, :bool, :boolean]
629
579
 
630
- ## The set of values that indicate a single-parameter (normal) option when
631
- ## passed as the +:type+ parameter of #opt.
632
- ##
633
- ## A value of +io+ corresponds to a readable IO resource, including
634
- ## a filename, URI, or the strings 'stdin' or '-'.
635
- SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io, :date]
636
-
637
- ## The set of values that indicate a multiple-parameter option (i.e., that
638
- ## takes multiple space-separated values on the commandline) when passed as
639
- ## the +:type+ parameter of #opt.
640
- MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios, :dates]
641
-
642
- ## The complete set of legal values for the +:type+ parameter of #opt.
643
- TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
644
-
645
- attr_accessor :name, :opts
646
-
647
- def initialize(name, desc="", opts={}, &b)
648
- ## fill in :type
649
- opts[:type] = # normalize
650
- case opts[:type]
651
- when :boolean, :bool then :flag
652
- when :integer then :int
653
- when :integers then :ints
654
- when :double then :float
655
- when :doubles then :floats
656
- when Class
657
- case opts[:type].name
658
- when 'TrueClass',
659
- 'FalseClass' then :flag
660
- when 'String' then :string
661
- when 'Integer' then :int
662
- when 'Float' then :float
663
- when 'IO' then :io
664
- when 'Date' then :date
665
- else
666
- raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
667
- end
668
- when nil then nil
669
- else
670
- raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
671
- opts[:type]
672
- end
580
+ attr_accessor :name, :short, :long, :default
581
+ attr_writer :multi_given
673
582
 
674
- ## for options with :multi => true, an array default doesn't imply
675
- ## a multi-valued argument. for that you have to specify a :type
676
- ## as well. (this is how we disambiguate an ambiguous situation;
677
- ## see the docs for Parser#opt for details.)
678
- disambiguated_default = if opts[:multi] && opts[:default].kind_of?(Array) && !opts[:type]
679
- opts[:default].first
680
- else
681
- opts[:default]
583
+ def initialize
584
+ @long = nil
585
+ @short = nil
586
+ @name = nil
587
+ @multi_given = false
588
+ @hidden = false
589
+ @default = nil
590
+ @optshash = Hash.new()
591
+ end
592
+
593
+ def opts(key)
594
+ @optshash[key]
595
+ end
596
+
597
+ def opts=(o)
598
+ @optshash = o
599
+ end
600
+
601
+ ## Indicates a flag option, which is an option without an argument
602
+ def flag? ; false ; end
603
+ def single_arg?
604
+ !self.multi_arg? && !self.flag?
605
+ end
606
+
607
+ def multi ; @multi_given ; end
608
+ alias multi? multi
609
+
610
+ ## Indicates that this is a multivalued (Array type) argument
611
+ def multi_arg? ; false ; end
612
+ ## note: Option-Types with both multi_arg? and flag? false are single-parameter (normal) options.
613
+
614
+ def array_default? ; self.default.kind_of?(Array) ; end
615
+
616
+ def short? ; short && short != :none ; end
617
+
618
+ def callback ; opts(:callback) ; end
619
+ def desc ; opts(:desc) ; end
620
+
621
+ def required? ; opts(:required) ; end
622
+
623
+ def parse(_paramlist, _neg_given)
624
+ raise NotImplementedError, "parse must be overridden for newly registered type"
625
+ end
626
+
627
+ # provide type-format string. default to empty, but user should probably override it
628
+ def type_format ; "" ; end
629
+
630
+ def educate
631
+ (short? ? "-#{short}, " : "") + "--#{long}" + type_format + (flag? && default ? ", --no-#{long}" : "")
632
+ end
633
+
634
+ ## Format the educate-line description including the default-value(s)
635
+ def description_with_default
636
+ return desc unless default
637
+ default_s = case default
638
+ when $stdout then '<stdout>'
639
+ when $stdin then '<stdin>'
640
+ when $stderr then '<stderr>'
641
+ when Array
642
+ default.join(', ')
643
+ else
644
+ default.to_s
645
+ end
646
+ defword = desc.end_with?('.') ? 'Default' : 'default'
647
+ return "#{desc} (#{defword}: #{default_s})"
648
+ end
649
+
650
+ ## Provide a way to register symbol aliases to the Parser
651
+ def self.register_alias(*alias_keys)
652
+ alias_keys.each do |alias_key|
653
+ # pass in the alias-key and the class
654
+ Parser.register(alias_key, self)
682
655
  end
656
+ end
683
657
 
684
- type_from_default =
685
- case disambiguated_default
686
- when Integer then :int
687
- when Numeric then :float
688
- when TrueClass,
689
- FalseClass then :flag
690
- when String then :string
691
- when IO then :io
692
- when Date then :date
693
- when Array
694
- if opts[:default].empty?
695
- if opts[:type]
696
- raise ArgumentError, "multiple argument type must be plural" unless MULTI_ARG_TYPES.include?(opts[:type])
697
- nil
698
- else
699
- raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
700
- end
701
- else
702
- case opts[:default][0] # the first element determines the types
703
- when Integer then :ints
704
- when Numeric then :floats
705
- when String then :strings
706
- when IO then :ios
707
- when Date then :dates
708
- else
709
- raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
710
- end
711
- end
712
- when nil then nil
713
- else
714
- raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
715
- end
658
+ ## Factory class methods ...
659
+
660
+ # Determines which type of object to create based on arguments passed
661
+ # to +Optimist::opt+. This is trickier in Optimist, than other cmdline
662
+ # parsers (e.g. Slop) because we allow the +default:+ to be able to
663
+ # set the option's type.
664
+ def self.create(name, desc="", opts={}, settings={})
665
+
666
+ opttype = Optimist::Parser.registry_getopttype(opts[:type])
667
+ opttype_from_default = get_klass_from_default(opts, opttype)
716
668
 
717
- raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default
669
+ raise ArgumentError, ":type specification and default type don't match (default type is #{opttype_from_default.class})" if opttype && opttype_from_default && (opttype.class != opttype_from_default.class)
718
670
 
719
- opts[:type] = opts[:type] || type_from_default || :flag
671
+ opt_inst = (opttype || opttype_from_default || Optimist::BooleanOption.new)
720
672
 
721
673
  ## fill in :long
722
- opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
723
- opts[:long] = case opts[:long]
724
- when /^--([^-].*)$/ then $1
725
- when /^[^-]/ then opts[:long]
726
- else raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
727
- end
674
+ opt_inst.long = handle_long_opt(opts[:long], name)
728
675
 
729
676
  ## fill in :short
730
- opts[:short] = opts[:short].to_s if opts[:short] && opts[:short] != :none
731
- opts[:short] = case opts[:short]
732
- when /^-(.)$/ then $1
733
- when nil, :none, /^.$/ then opts[:short]
734
- else raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
735
- end
677
+ opt_inst.short = handle_short_opt(opts[:short])
736
678
 
737
- if opts[:short]
738
- raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ ::Trollop::Parser::INVALID_SHORT_ARG_REGEX
739
- end
679
+ ## fill in :multi
680
+ multi_given = opts[:multi] || false
681
+ opt_inst.multi_given = multi_given
740
682
 
741
683
  ## fill in :default for flags
742
- opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
684
+ defvalue = opts[:default] || opt_inst.default
743
685
 
744
686
  ## autobox :default for :multi (multi-occurrence) arguments
745
- opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].kind_of?(Array)
687
+ defvalue = [defvalue] if defvalue && multi_given && !defvalue.kind_of?(Array)
688
+ opt_inst.default = defvalue
689
+ opt_inst.name = name
690
+ opt_inst.opts = opts
691
+ opt_inst
692
+ end
746
693
 
747
- ## fill in :multi
748
- opts[:multi] ||= false
694
+ private
749
695
 
750
- self.name = name
751
- self.opts = opts
696
+ def self.get_type_from_disdef(optdef, opttype, disambiguated_default)
697
+ if disambiguated_default.is_a? Array
698
+ return(optdef.first.class.name.downcase + "s") if !optdef.empty?
699
+ if opttype
700
+ raise ArgumentError, "multiple argument type must be plural" unless opttype.multi_arg?
701
+ return nil
702
+ else
703
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array"
704
+ end
705
+ end
706
+ return disambiguated_default.class.name.downcase
752
707
  end
753
708
 
754
- def key?(name)
755
- opts.key?(name)
709
+ def self.get_klass_from_default(opts, opttype)
710
+ ## for options with :multi => true, an array default doesn't imply
711
+ ## a multi-valued argument. for that you have to specify a :type
712
+ ## as well. (this is how we disambiguate an ambiguous situation;
713
+ ## see the docs for Parser#opt for details.)
714
+
715
+ disambiguated_default = if opts[:multi] && opts[:default].is_a?(Array) && opttype.nil?
716
+ opts[:default].first
717
+ else
718
+ opts[:default]
719
+ end
720
+
721
+ return nil if disambiguated_default.nil?
722
+ type_from_default = get_type_from_disdef(opts[:default], opttype, disambiguated_default)
723
+ return Optimist::Parser.registry_getopttype(type_from_default)
756
724
  end
757
725
 
758
- def type ; opts[:type] ; end
759
- def flag? ; type == :flag ; end
760
- def single_arg?
761
- SINGLE_ARG_TYPES.include?(type)
726
+ def self.handle_long_opt(lopt, name)
727
+ lopt = lopt ? lopt.to_s : name.to_s.gsub("_", "-")
728
+ lopt = case lopt
729
+ when /^--([^-].*)$/ then $1
730
+ when /^[^-]/ then lopt
731
+ else raise ArgumentError, "invalid long option name #{lopt.inspect}"
732
+ end
762
733
  end
763
734
 
764
- def multi ; opts[:multi] ; end
765
- alias multi? multi
735
+ def self.handle_short_opt(sopt)
736
+ sopt = sopt.to_s if sopt && sopt != :none
737
+ sopt = case sopt
738
+ when /^-(.)$/ then $1
739
+ when nil, :none, /^.$/ then sopt
740
+ else raise ArgumentError, "invalid short option name '#{sopt.inspect}'"
741
+ end
766
742
 
767
- def multi_arg?
768
- MULTI_ARG_TYPES.include?(type)
743
+ if sopt
744
+ raise ArgumentError, "a short option name can't be a number or a dash" if sopt =~ ::Optimist::Parser::INVALID_SHORT_ARG_REGEX
745
+ end
746
+ return sopt
769
747
  end
770
748
 
771
- def default ; opts[:default] ; end
772
- #? def multi_default ; opts.default || opts.multi && [] ; end
773
- def array_default? ; opts[:default].kind_of?(Array) ; end
749
+ end
774
750
 
775
- def short ; opts[:short] ; end
776
- def short? ; short && short != :none ; end
777
- # not thrilled about this
778
- def short=(val) ; opts[:short] = val ; end
779
- def long ; opts[:long] ; end
780
- def callback ; opts[:callback] ; end
781
- def desc ; opts[:desc] ; end
751
+ # Flag option. Has no arguments. Can be negated with "no-".
752
+ class BooleanOption < Option
753
+ register_alias :flag, :bool, :boolean, :trueclass, :falseclass
754
+ def initialize
755
+ super()
756
+ @default = false
757
+ end
758
+ def flag? ; true ; end
759
+ def parse(_paramlist, neg_given)
760
+ return(self.name.to_s =~ /^no_/ ? neg_given : !neg_given)
761
+ end
762
+ end
763
+
764
+ # Floating point number option class.
765
+ class FloatOption < Option
766
+ register_alias :float, :double
767
+ def type_format ; "=<f>" ; end
768
+ def parse(paramlist, _neg_given)
769
+ paramlist.map do |pg|
770
+ pg.map do |param|
771
+ raise CommandlineError, "option '#{self.name}' needs a floating-point number" unless param.is_a?(Numeric) || param =~ FLOAT_RE
772
+ param.to_f
773
+ end
774
+ end
775
+ end
776
+ end
777
+
778
+ # Integer number option class.
779
+ class IntegerOption < Option
780
+ register_alias :int, :integer, :fixnum
781
+ def type_format ; "=<i>" ; end
782
+ def parse(paramlist, _neg_given)
783
+ paramlist.map do |pg|
784
+ pg.map do |param|
785
+ raise CommandlineError, "option '#{self.name}' needs an integer" unless param.is_a?(Numeric) || param =~ /^-?[\d_]+$/
786
+ param.to_i
787
+ end
788
+ end
789
+ end
790
+ end
791
+
792
+ # Option class for handling IO objects and URLs.
793
+ # Note that this will return the file-handle, not the file-name
794
+ # in the case of file-paths given to it.
795
+ class IOOption < Option
796
+ register_alias :io
797
+ def type_format ; "=<filename/uri>" ; end
798
+ def parse(paramlist, _neg_given)
799
+ paramlist.map do |pg|
800
+ pg.map do |param|
801
+ if param =~ /^(stdin|-)$/i
802
+ $stdin
803
+ else
804
+ require 'open-uri'
805
+ begin
806
+ open param
807
+ rescue SystemCallError => e
808
+ raise CommandlineError, "file or url for option '#{self.name}' cannot be opened: #{e.message}"
809
+ end
810
+ end
811
+ end
812
+ end
813
+ end
814
+ end
782
815
 
783
- def required? ; opts[:required] ; end
816
+ # Option class for handling Strings.
817
+ class StringOption < Option
818
+ register_alias :string
819
+ def type_format ; "=<s>" ; end
820
+ def parse(paramlist, _neg_given)
821
+ paramlist.map { |pg| pg.map(&:to_s) }
822
+ end
823
+ end
784
824
 
785
- def self.create(name, desc="", opts={})
786
- new(name, desc, opts)
825
+ # Option for dates. Uses Chronic if it exists.
826
+ class DateOption < Option
827
+ register_alias :date
828
+ def type_format ; "=<date>" ; end
829
+ def parse(paramlist, _neg_given)
830
+ paramlist.map do |pg|
831
+ pg.map do |param|
832
+ next param if param.is_a?(Date)
833
+ begin
834
+ begin
835
+ require 'chronic'
836
+ time = Chronic.parse(param)
837
+ rescue LoadError
838
+ # chronic is not available
839
+ end
840
+ time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
841
+ rescue ArgumentError
842
+ raise CommandlineError, "option '#{self.name}' needs a date"
843
+ end
844
+ end
845
+ end
787
846
  end
788
847
  end
789
848
 
790
- ## The easy, syntactic-sugary entry method into Trollop. Creates a Parser,
849
+ ### MULTI_OPT_TYPES :
850
+ ## The set of values that indicate a multiple-parameter option (i.e., that
851
+ ## takes multiple space-separated values on the commandline) when passed as
852
+ ## the +:type+ parameter of #opt.
853
+
854
+ # Option class for handling multiple Integers
855
+ class IntegerArrayOption < IntegerOption
856
+ register_alias :fixnums, :ints, :integers
857
+ def type_format ; "=<i+>" ; end
858
+ def multi_arg? ; true ; end
859
+ end
860
+
861
+ # Option class for handling multiple Floats
862
+ class FloatArrayOption < FloatOption
863
+ register_alias :doubles, :floats
864
+ def type_format ; "=<f+>" ; end
865
+ def multi_arg? ; true ; end
866
+ end
867
+
868
+ # Option class for handling multiple Strings
869
+ class StringArrayOption < StringOption
870
+ register_alias :strings
871
+ def type_format ; "=<s+>" ; end
872
+ def multi_arg? ; true ; end
873
+ end
874
+
875
+ # Option class for handling multiple dates
876
+ class DateArrayOption < DateOption
877
+ register_alias :dates
878
+ def type_format ; "=<date+>" ; end
879
+ def multi_arg? ; true ; end
880
+ end
881
+
882
+ # Option class for handling Files/URLs via 'open'
883
+ class IOArrayOption < IOOption
884
+ register_alias :ios
885
+ def type_format ; "=<filename/uri+>" ; end
886
+ def multi_arg? ; true ; end
887
+ end
888
+
889
+ ## The easy, syntactic-sugary entry method into Optimist. Creates a Parser,
791
890
  ## passes the block to it, then parses +args+ with it, handling any errors or
792
891
  ## requests for help or version information appropriately (and then exiting).
793
892
  ## Modifies +args+ in place. Returns a hash of option values.
@@ -804,8 +903,8 @@ end
804
903
  ##
805
904
  ## Example:
806
905
  ##
807
- ## require 'trollop'
808
- ## opts = Trollop::options do
906
+ ## require 'optimist'
907
+ ## opts = Optimist::options do
809
908
  ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
810
909
  ## opt :name, "Monkey name", :type => :string # a string --name <s>, defaulting to nil
811
910
  ## opt :num_limbs, "Number of limbs", :default => 4 # an integer --num-limbs <i>, defaulting to 4
@@ -817,13 +916,13 @@ end
817
916
  ## ## if called with --monkey
818
917
  ## p opts # => {:monkey=>true, :name=>nil, :num_limbs=>4, :help=>false, :monkey_given=>true}
819
918
  ##
820
- ## See more examples at http://trollop.rubyforge.org.
919
+ ## See more examples at http://optimist.rubyforge.org.
821
920
  def options(args = ARGV, *a, &b)
822
921
  @last_parser = Parser.new(*a, &b)
823
922
  with_standard_exception_handling(@last_parser) { @last_parser.parse args }
824
923
  end
825
924
 
826
- ## If Trollop::options doesn't do quite what you want, you can create a Parser
925
+ ## If Optimist::options doesn't do quite what you want, you can create a Parser
827
926
  ## object and call Parser#parse on it. That method will throw CommandlineError,
828
927
  ## HelpNeeded and VersionNeeded exceptions when necessary; if you want to
829
928
  ## have these handled for you in the standard manner (e.g. show the help
@@ -834,15 +933,15 @@ end
834
933
  ##
835
934
  ## Usage example:
836
935
  ##
837
- ## require 'trollop'
838
- ## p = Trollop::Parser.new do
936
+ ## require 'optimist'
937
+ ## p = Optimist::Parser.new do
839
938
  ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
840
939
  ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
841
940
  ## end
842
941
  ##
843
- ## opts = Trollop::with_standard_exception_handling p do
942
+ ## opts = Optimist::with_standard_exception_handling p do
844
943
  ## o = p.parse ARGV
845
- ## raise Trollop::HelpNeeded if ARGV.empty? # show help screen
944
+ ## raise Optimist::HelpNeeded if ARGV.empty? # show help screen
846
945
  ## o
847
946
  ## end
848
947
  ##
@@ -877,12 +976,16 @@ end
877
976
  ## opt :whatever # ...
878
977
  ## end
879
978
  ##
880
- ## Trollop::die "need at least one filename" if ARGV.empty?
979
+ ## Optimist::die "need at least one filename" if ARGV.empty?
980
+ ##
981
+ ## An exit code can be provide if needed
982
+ ##
983
+ ## Optimist::die "need at least one filename", -2 if ARGV.empty?
881
984
  def die(arg, msg = nil, error_code = nil)
882
985
  if @last_parser
883
986
  @last_parser.die arg, msg, error_code
884
987
  else
885
- raise ArgumentError, "Trollop::die can only be called after Trollop::options"
988
+ raise ArgumentError, "Optimist::die can only be called after Optimist::options"
886
989
  end
887
990
  end
888
991
 
@@ -897,15 +1000,15 @@ end
897
1000
  ## EOS
898
1001
  ## end
899
1002
  ##
900
- ## Trollop::educate if ARGV.empty?
1003
+ ## Optimist::educate if ARGV.empty?
901
1004
  def educate
902
1005
  if @last_parser
903
1006
  @last_parser.educate
904
1007
  exit
905
1008
  else
906
- raise ArgumentError, "Trollop::educate can only be called after Trollop::options"
1009
+ raise ArgumentError, "Optimist::educate can only be called after Optimist::options"
907
1010
  end
908
1011
  end
909
1012
 
910
1013
  module_function :options, :die, :educate, :with_standard_exception_handling
911
- end # module
1014
+ end # module
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darksky-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ken J.
@@ -10,7 +10,7 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2019-09-09 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Darksky library written in pure Ruby without external dependency.
13
+ description: Dark Sky gem written in pure Ruby without any external dependency.
14
14
  email:
15
15
  - kenjij@gmail.com
16
16
  executables:
@@ -18,13 +18,14 @@ executables:
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - LICENSE
22
+ - README.md
21
23
  - bin/darksky
22
24
  - darksky-ruby.gemspec
23
25
  - lib/darksky-ruby.rb
24
26
  - lib/darksky-ruby/api.rb
25
27
  - lib/darksky-ruby/http.rb
26
- - lib/darksky-ruby/trollop.rb
27
- - lib/darksky-ruby/version.rb
28
+ - lib/darksky-ruby/optimist.rb
28
29
  homepage: https://github.com/kenjij/darksky-ruby
29
30
  licenses:
30
31
  - MIT
@@ -44,9 +45,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
44
45
  - !ruby/object:Gem::Version
45
46
  version: '0'
46
47
  requirements: []
47
- rubyforge_project:
48
- rubygems_version: 2.6.14
48
+ rubygems_version: 3.0.6
49
49
  signing_key:
50
50
  specification_version: 4
51
- summary: Pure simple Ruby based Darksky REST library
51
+ summary: Pure simple Ruby based Dark Sky API gem
52
52
  test_files: []
@@ -1,5 +0,0 @@
1
- module Darksky
2
-
3
- Version = '0.0.2'
4
-
5
- end