salad 0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +9 -0
- data/README.md +123 -0
- data/exe/salad +5 -0
- data/lib/salad/bond_completer.rb +104 -0
- data/lib/salad/catalogue_of_life.rb +43 -0
- data/lib/salad/console.rb +503 -0
- data/lib/salad/gem_help.rb +2163 -0
- data/lib/salad/generate_gem_help.rb +242 -0
- data/lib/salad/version.rb +5 -0
- data/lib/salad.rb +126 -0
- metadata +297 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'salad'
|
|
4
|
+
require_relative 'gem_help'
|
|
5
|
+
require_relative 'bond_completer'
|
|
6
|
+
|
|
7
|
+
module Salad
|
|
8
|
+
HINTS = [
|
|
9
|
+
"TAB completes service and gem names: `Salad.nas<TAB>` → Salad.nasturtium",
|
|
10
|
+
"TAB inside parens shows endpoint parameters: `Salad.inaturalist.observations(<TAB>`",
|
|
11
|
+
"Results render in `less`, press `q` to exit paged API results",
|
|
12
|
+
"Results render in `less`, press `/` to search within paged API results (n/N for next/previous match)",
|
|
13
|
+
"Results render in `less`, use vim-style navigation (j/k, gg/G, h/l) — `man less` is worth a read",
|
|
14
|
+
"See full endpoint docs: `help Salad.nasturtium.observations`",
|
|
15
|
+
"List all services: `help`",
|
|
16
|
+
"Service and gem names are interchangeable: `Salad.inaturalist` == `Salad.nasturtium`",
|
|
17
|
+
"`Salad.catalogue_of_life.*` and `Salad.col.*` auto-scope to the latest COL release (3LR)",
|
|
18
|
+
"Override the COL dataset: `Salad.col.nameusage_search(q: 'Aedes', dataset_id: 'COL25')`",
|
|
19
|
+
"`Salad.clb` and `Salad.checklistbank` search across ALL ChecklistBank datasets, not just COL",
|
|
20
|
+
"Type `hints` any time to see all tips"
|
|
21
|
+
].freeze
|
|
22
|
+
|
|
23
|
+
def self.console
|
|
24
|
+
# Disable Readline's automatic space appending to completions
|
|
25
|
+
# Salad completions should be chainable without spaces
|
|
26
|
+
require 'readline'
|
|
27
|
+
Readline.completion_append_character = ""
|
|
28
|
+
|
|
29
|
+
Pry.config.prompt = Pry::Prompt.new(
|
|
30
|
+
'salad',
|
|
31
|
+
'Salad REPL prompt',
|
|
32
|
+
[->(*) { 'salad> ' }, ->(*) { 'salad* ' }]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Case-insensitive prefix completion for top-level constants:
|
|
36
|
+
# `sala<TAB>` -> `Salad.`, `nast<TAB>` -> `Nasturtium.`, etc.
|
|
37
|
+
# The regex is tight (only matches strings that are actual prefixes of a
|
|
38
|
+
# known name) so unrelated lowercase input like `put<TAB>` falls through
|
|
39
|
+
# to Pry's default object/method completion.
|
|
40
|
+
Bond::M.complete(:on => Salad.completable_prefix_regex, :search => false, :action => lambda { |input|
|
|
41
|
+
prefix = input.line.downcase
|
|
42
|
+
Salad.completable_names
|
|
43
|
+
.select { |n| n.downcase.start_with?(prefix) }
|
|
44
|
+
.map { |n| "#{n}." }
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
# Help-prefixed variant: `help sal<TAB>` -> `help Salad`,
|
|
48
|
+
# `help nast<TAB>` -> `help nasturtium`. Completes to topics the help
|
|
49
|
+
# command actually recognizes (gem/service names and the literal 'Salad'),
|
|
50
|
+
# with no trailing dot so pressing Enter shows the help.
|
|
51
|
+
Bond::M.complete(:on => Salad.help_completable_prefix_regex, :search => false, :action => lambda { |input|
|
|
52
|
+
prefix = input.line.sub(/\Ahelp\s+/i, '').downcase
|
|
53
|
+
Salad.help_completable_names.select { |n| n.downcase.start_with?(prefix) }
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
# Configure Bond completion for Salad services and gems
|
|
57
|
+
# Don't match if there's already a dot (that's handled by endpoint completion)
|
|
58
|
+
salad_gem_pattern = /^(help\s+)?Salad\.(\w*)$/
|
|
59
|
+
Bond::M.complete(:on => salad_gem_pattern, :action => lambda { |input|
|
|
60
|
+
match = input.line.match(salad_gem_pattern)
|
|
61
|
+
if match
|
|
62
|
+
prefix = match[2]
|
|
63
|
+
completions = (Salad::SERVICES.keys.map(&:to_s) + Salad::GEMS)
|
|
64
|
+
.select { |g| g.start_with?(prefix) }
|
|
65
|
+
|
|
66
|
+
if input.line.start_with?('help ')
|
|
67
|
+
# No trailing dot: `help Salad.nasturtium` matches the help command's
|
|
68
|
+
# regex; `help Salad.nasturtium.` would fall through to "no help".
|
|
69
|
+
completions.map { |g| "Salad.#{g}" }
|
|
70
|
+
else
|
|
71
|
+
# Trailing dot for chaining: after `Salad.nast<TAB>`, the user almost
|
|
72
|
+
# always wants to type a method next, so put the cursor past the dot.
|
|
73
|
+
completions.map { |g| "Salad.#{g}." }
|
|
74
|
+
end
|
|
75
|
+
else
|
|
76
|
+
[]
|
|
77
|
+
end
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
# Completion for endpoint/method names: "Salad.gem.method" or "Salad.gem .method" (with optional space)
|
|
81
|
+
# Handles cases where Pry adds a space after the gem name completion or around the dot
|
|
82
|
+
salad_endpoint_pattern = /^(help\s+)?Salad\.(\w+)\s*\.\s*(\w*)$/
|
|
83
|
+
Bond::M.complete(:on => salad_endpoint_pattern, :action => lambda { |input|
|
|
84
|
+
match = input.line.match(salad_endpoint_pattern)
|
|
85
|
+
if match
|
|
86
|
+
is_help = !match[1].nil?
|
|
87
|
+
gem_name = match[2]
|
|
88
|
+
method_prefix = match[3]
|
|
89
|
+
|
|
90
|
+
actual_gem_name = if Salad::SERVICES.key?(gem_name.to_sym)
|
|
91
|
+
Salad::SERVICES[gem_name.to_sym]
|
|
92
|
+
elsif Salad::GEMS.include?(gem_name)
|
|
93
|
+
gem_name
|
|
94
|
+
else
|
|
95
|
+
nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if actual_gem_name
|
|
99
|
+
begin
|
|
100
|
+
mod = Salad.gem_module(actual_gem_name)
|
|
101
|
+
gem_sym = actual_gem_name.to_sym
|
|
102
|
+
|
|
103
|
+
if is_help
|
|
104
|
+
help_data = Salad::GEM_HELP[gem_sym]
|
|
105
|
+
if help_data&.[](:endpoints)
|
|
106
|
+
help_data[:endpoints].keys
|
|
107
|
+
.select { |e| e.to_s.start_with?(method_prefix) }
|
|
108
|
+
.map { |e| "Salad.#{gem_name}.#{e}" }
|
|
109
|
+
else
|
|
110
|
+
[]
|
|
111
|
+
end
|
|
112
|
+
else
|
|
113
|
+
help_data = Salad::GEM_HELP[gem_sym]
|
|
114
|
+
endpoints = help_data&.[](:endpoints)&.keys&.map(&:to_s) || []
|
|
115
|
+
|
|
116
|
+
begin
|
|
117
|
+
public_methods = mod.methods(false).map(&:to_s)
|
|
118
|
+
rescue
|
|
119
|
+
public_methods = []
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
all_methods = (endpoints + public_methods).uniq
|
|
123
|
+
.select { |m| m.start_with?(method_prefix) }
|
|
124
|
+
.sort
|
|
125
|
+
|
|
126
|
+
all_methods.map { |m| "Salad.#{gem_name}.#{m}" }
|
|
127
|
+
end
|
|
128
|
+
rescue NameError
|
|
129
|
+
[]
|
|
130
|
+
end
|
|
131
|
+
else
|
|
132
|
+
[]
|
|
133
|
+
end
|
|
134
|
+
else
|
|
135
|
+
[]
|
|
136
|
+
end
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
# Completion for raw module access: "Nasturtium.method" or "help Nasturtium.method".
|
|
140
|
+
# Pry's BondCompleter override disables default object-introspection completion,
|
|
141
|
+
# so we explicitly handle the SFG wrapper modules here.
|
|
142
|
+
module_to_gem = Salad::GEMS.each_with_object({}) do |g, h|
|
|
143
|
+
h[g.split('_').map(&:capitalize).join] = g
|
|
144
|
+
end
|
|
145
|
+
module_endpoint_pattern = /^(help\s+)?([A-Z]\w*)\s*\.\s*(\w*)$/
|
|
146
|
+
Bond::M.complete(:on => module_endpoint_pattern, :action => lambda { |input|
|
|
147
|
+
match = input.line.match(module_endpoint_pattern)
|
|
148
|
+
if match
|
|
149
|
+
is_help = !match[1].nil?
|
|
150
|
+
module_name = match[2]
|
|
151
|
+
method_prefix = match[3]
|
|
152
|
+
gem_name = module_to_gem[module_name]
|
|
153
|
+
|
|
154
|
+
if gem_name
|
|
155
|
+
help_data = Salad::GEM_HELP[gem_name.to_sym]
|
|
156
|
+
endpoints = help_data&.[](:endpoints)&.keys&.map(&:to_s) || []
|
|
157
|
+
|
|
158
|
+
if is_help
|
|
159
|
+
endpoints
|
|
160
|
+
.select { |e| e.start_with?(method_prefix) }
|
|
161
|
+
.sort
|
|
162
|
+
.map { |e| "#{module_name}.#{e}" }
|
|
163
|
+
else
|
|
164
|
+
public_methods = begin
|
|
165
|
+
Object.const_get(module_name).methods(false).map(&:to_s)
|
|
166
|
+
rescue NameError
|
|
167
|
+
[]
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
(endpoints + public_methods).uniq
|
|
171
|
+
.select { |m| m.start_with?(method_prefix) }
|
|
172
|
+
.sort
|
|
173
|
+
.map { |m| "#{module_name}.#{m}" }
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
[]
|
|
177
|
+
end
|
|
178
|
+
else
|
|
179
|
+
[]
|
|
180
|
+
end
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
# Completion for keyword arguments inside a method call:
|
|
184
|
+
# "Salad.gem.method(" or "Salad.gem.method(prior: 1, partial"
|
|
185
|
+
# Shows only the params documented for that specific endpoint in GEM_HELP,
|
|
186
|
+
# and hides params already supplied in the current call.
|
|
187
|
+
# :search => false disables Bond's normal_search filter, which would otherwise
|
|
188
|
+
# require candidates to start with the full line (e.g. "Salad.inaturalist.observations(").
|
|
189
|
+
# We return short candidates ("taxon_id: ") so Readline replaces only the word
|
|
190
|
+
# after the "(" break-char instead of doubling the whole prefix.
|
|
191
|
+
salad_kwarg_pattern = /^Salad\.(\w+)\s*\.\s*(\w+)\s*\(([^)]*)$/
|
|
192
|
+
Bond::M.complete(:on => salad_kwarg_pattern, :search => false, :action => lambda { |input|
|
|
193
|
+
match = input.line.match(salad_kwarg_pattern)
|
|
194
|
+
if match
|
|
195
|
+
gem_name = match[1]
|
|
196
|
+
method_name = match[2]
|
|
197
|
+
inside_parens = match[3]
|
|
198
|
+
|
|
199
|
+
partial = inside_parens[/(\w*)\z/] || ''
|
|
200
|
+
prefix_in_parens = inside_parens[0...(inside_parens.length - partial.length)]
|
|
201
|
+
|
|
202
|
+
actual_gem_name = if Salad::SERVICES.key?(gem_name.to_sym)
|
|
203
|
+
Salad::SERVICES[gem_name.to_sym]
|
|
204
|
+
elsif Salad::GEMS.include?(gem_name)
|
|
205
|
+
gem_name
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
if actual_gem_name
|
|
209
|
+
help_data = Salad::GEM_HELP[actual_gem_name.to_sym]
|
|
210
|
+
endpoint = help_data && help_data[:endpoints] && help_data[:endpoints][method_name.to_sym]
|
|
211
|
+
|
|
212
|
+
if endpoint && endpoint[:params]
|
|
213
|
+
params = endpoint[:params].keys.map(&:to_s)
|
|
214
|
+
used = prefix_in_parens.scan(/(\w+)\s*:/).flatten
|
|
215
|
+
# Return only the param + ": " — Readline treats "(" as a word break,
|
|
216
|
+
# so the "word" being completed starts after the last "(" (or space),
|
|
217
|
+
# not at the beginning of the line. Returning a full-line candidate
|
|
218
|
+
# would cause Readline to insert it after the "(", doubling the prefix.
|
|
219
|
+
(params - used)
|
|
220
|
+
.select { |p| p.start_with?(partial) }
|
|
221
|
+
.sort
|
|
222
|
+
.map { |p| "#{p}: " }
|
|
223
|
+
else
|
|
224
|
+
[]
|
|
225
|
+
end
|
|
226
|
+
else
|
|
227
|
+
[]
|
|
228
|
+
end
|
|
229
|
+
else
|
|
230
|
+
[]
|
|
231
|
+
end
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
# Same keyword-arg completion for raw module form: "Nasturtium.observations(...".
|
|
235
|
+
# Only documented params for the specific endpoint are returned, hiding ones
|
|
236
|
+
# already supplied in the current call.
|
|
237
|
+
module_kwarg_pattern = /^([A-Z]\w*)\s*\.\s*(\w+)\s*\(([^)]*)$/
|
|
238
|
+
Bond::M.complete(:on => module_kwarg_pattern, :search => false, :action => lambda { |input|
|
|
239
|
+
match = input.line.match(module_kwarg_pattern)
|
|
240
|
+
if match
|
|
241
|
+
module_name = match[1]
|
|
242
|
+
method_name = match[2]
|
|
243
|
+
inside_parens = match[3]
|
|
244
|
+
gem_name = module_to_gem[module_name]
|
|
245
|
+
|
|
246
|
+
if gem_name
|
|
247
|
+
partial = inside_parens[/(\w*)\z/] || ''
|
|
248
|
+
prefix_in_parens = inside_parens[0...(inside_parens.length - partial.length)]
|
|
249
|
+
|
|
250
|
+
help_data = Salad::GEM_HELP[gem_name.to_sym]
|
|
251
|
+
endpoint = help_data && help_data[:endpoints] && help_data[:endpoints][method_name.to_sym]
|
|
252
|
+
|
|
253
|
+
if endpoint && endpoint[:params]
|
|
254
|
+
params = endpoint[:params].keys.map(&:to_s)
|
|
255
|
+
used = prefix_in_parens.scan(/(\w+)\s*:/).flatten
|
|
256
|
+
(params - used)
|
|
257
|
+
.select { |p| p.start_with?(partial) }
|
|
258
|
+
.sort
|
|
259
|
+
.map { |p| "#{p}: " }
|
|
260
|
+
else
|
|
261
|
+
[]
|
|
262
|
+
end
|
|
263
|
+
else
|
|
264
|
+
[]
|
|
265
|
+
end
|
|
266
|
+
else
|
|
267
|
+
[]
|
|
268
|
+
end
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
Pry.config.commands.create_command('help', 'Show Salad services and gem documentation') do
|
|
272
|
+
def process(*args)
|
|
273
|
+
topic = args.join(' ')
|
|
274
|
+
if args.empty? || topic == 'Salad'
|
|
275
|
+
show_salad_services_help
|
|
276
|
+
elsif !show_service_help(topic)
|
|
277
|
+
puts "\nNo Salad help available for `#{topic}`."
|
|
278
|
+
puts "Try `help` (no args) to list services, or Pry's `show-doc #{topic}` / `ls #{topic}` for object docs."
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
private
|
|
283
|
+
|
|
284
|
+
def show_salad_services_help
|
|
285
|
+
puts "\n=== Salad Services ==="
|
|
286
|
+
puts "Access bundled biodiversity informatics API wrappers by service name or gem name:\n"
|
|
287
|
+
service_width = Salad::SERVICES.keys.map { |s| s.to_s.length }.max
|
|
288
|
+
gem_width = Salad::SERVICES.values.map(&:length).max
|
|
289
|
+
Salad::SERVICES.sort.each do |service, gem_name|
|
|
290
|
+
mod = begin
|
|
291
|
+
Salad.gem_module(gem_name).to_s
|
|
292
|
+
rescue NameError
|
|
293
|
+
'(not loaded)'
|
|
294
|
+
end
|
|
295
|
+
printf " Salad.%-#{service_width}s Salad.%-#{gem_width}s -> %s\n", service, gem_name, mod
|
|
296
|
+
end
|
|
297
|
+
puts "\nUsage Examples:"
|
|
298
|
+
puts " help Salad.checkerberry # Show all endpoints (try: help Salad.check<TAB>)"
|
|
299
|
+
puts " help Salad.checkerberry.verify # Show specific endpoint (try: help Salad.checkerberry.v<TAB>)"
|
|
300
|
+
puts " help gnverifier # Also works with service or gem name"
|
|
301
|
+
puts "\nAPI Examples:"
|
|
302
|
+
puts " Salad.worms.record(127160)"
|
|
303
|
+
puts " Crawlyflower.record(127160) # Same as above"
|
|
304
|
+
puts " Salad.inaturalist.observations(id: 1234)"
|
|
305
|
+
puts "\nTab-completion is powered by Bond and shows only Salad endpoints.\n\n"
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def show_service_help(topic)
|
|
309
|
+
begin
|
|
310
|
+
# Try to match endpoint pattern: "Salad.gem_name.endpoint_name"
|
|
311
|
+
if topic =~ /^Salad\.([\w_]+)\.([\w_]+)$/
|
|
312
|
+
gem_name = Regexp.last_match(1)
|
|
313
|
+
endpoint_name = Regexp.last_match(2)
|
|
314
|
+
return show_endpoint_help(gem_name, endpoint_name)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# Try to match Salad service pattern: "Salad.service_name" or "Salad.gem_name"
|
|
318
|
+
if topic =~ /^Salad\.([\w_]+)$/
|
|
319
|
+
name = Regexp.last_match(1)
|
|
320
|
+
# Check if it's a service name, and convert to gem name
|
|
321
|
+
gem_name = Salad::SERVICES[name.to_sym] || name
|
|
322
|
+
show_gem_methods(gem_name)
|
|
323
|
+
return true
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Try matching just service or gem name: "inaturalist" or "nasturtium"
|
|
327
|
+
if Salad::SERVICES.key?(topic.to_sym)
|
|
328
|
+
gem_name = Salad::SERVICES[topic.to_sym]
|
|
329
|
+
show_gem_methods(gem_name)
|
|
330
|
+
return true
|
|
331
|
+
elsif Salad::GEMS.include?(topic)
|
|
332
|
+
show_gem_methods(topic)
|
|
333
|
+
return true
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# CamelCase module form: "Nasturtium" or "Nasturtium.endpoint"
|
|
337
|
+
if topic =~ /^([A-Z]\w*)(?:\.([\w_]+))?$/
|
|
338
|
+
module_name = Regexp.last_match(1)
|
|
339
|
+
endpoint_name = Regexp.last_match(2)
|
|
340
|
+
gem_name = Salad.gem_for_module(module_name)
|
|
341
|
+
if gem_name
|
|
342
|
+
return show_endpoint_help(gem_name, endpoint_name) if endpoint_name
|
|
343
|
+
show_gem_methods(gem_name)
|
|
344
|
+
return true
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
false
|
|
349
|
+
rescue => e
|
|
350
|
+
puts "\nError loading help: #{e.class}: #{e.message}"
|
|
351
|
+
false
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def show_gem_methods(service_or_gem)
|
|
356
|
+
mod = begin
|
|
357
|
+
Salad.gem_module(service_or_gem)
|
|
358
|
+
rescue NameError
|
|
359
|
+
puts "Service/gem '#{service_or_gem}' not found."
|
|
360
|
+
return
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
puts "\n=== #{mod.to_s} ==="
|
|
364
|
+
|
|
365
|
+
# Check for detailed help documentation
|
|
366
|
+
gem_sym = service_or_gem.to_sym
|
|
367
|
+
if Salad::GEM_HELP[gem_sym]
|
|
368
|
+
show_detailed_gem_help(gem_sym, mod, service_or_gem)
|
|
369
|
+
else
|
|
370
|
+
show_generic_gem_help(mod, service_or_gem)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def show_endpoint_help(gem_name, endpoint_name)
|
|
375
|
+
# Convert service name to gem name if needed
|
|
376
|
+
actual_gem_name = if Salad::SERVICES.key?(gem_name.to_sym)
|
|
377
|
+
Salad::SERVICES[gem_name.to_sym]
|
|
378
|
+
else
|
|
379
|
+
gem_name
|
|
380
|
+
end
|
|
381
|
+
gem_sym = actual_gem_name.to_sym
|
|
382
|
+
|
|
383
|
+
# Check if gem has documented help
|
|
384
|
+
unless Salad::GEM_HELP[gem_sym]
|
|
385
|
+
puts "\nNo detailed documentation available for #{gem_name}.#{endpoint_name}"
|
|
386
|
+
return true
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
help = Salad::GEM_HELP[gem_sym]
|
|
390
|
+
endpoints = help[:endpoints]
|
|
391
|
+
|
|
392
|
+
unless endpoints
|
|
393
|
+
puts "\nNo endpoints documented for #{gem_name}"
|
|
394
|
+
return true
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
endpoint_sym = endpoint_name.to_sym
|
|
398
|
+
endpoint_info = endpoints[endpoint_sym]
|
|
399
|
+
|
|
400
|
+
unless endpoint_info
|
|
401
|
+
puts "\nEndpoint '#{endpoint_name}' not found in #{gem_name}."
|
|
402
|
+
puts "\nAvailable endpoints: #{endpoints.keys.join(', ')}"
|
|
403
|
+
return true
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
mod = begin
|
|
407
|
+
Salad.gem_module(actual_gem_name)
|
|
408
|
+
rescue NameError
|
|
409
|
+
nil
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
module_name = mod ? mod.to_s : actual_gem_name.camelize
|
|
413
|
+
puts "\n=== #{module_name}.#{endpoint_name} ==="
|
|
414
|
+
puts "\n#{endpoint_info[:description]}"
|
|
415
|
+
|
|
416
|
+
if endpoint_info[:params]
|
|
417
|
+
puts "\nParameters:"
|
|
418
|
+
endpoint_info[:params].each do |param_name, param_desc|
|
|
419
|
+
puts " #{param_name.to_s.ljust(20)} — #{param_desc}"
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
if endpoint_info[:examples]
|
|
424
|
+
puts "\nExamples:"
|
|
425
|
+
endpoint_info[:examples].each { |ex| puts " #{ex}" }
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
puts "\n"
|
|
429
|
+
true
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def show_detailed_gem_help(gem_sym, mod, gem_name)
|
|
433
|
+
help = Salad::GEM_HELP[gem_sym]
|
|
434
|
+
|
|
435
|
+
puts "\n#{help[:description]}"
|
|
436
|
+
puts "API: #{help[:api]}"
|
|
437
|
+
|
|
438
|
+
if help[:endpoints]
|
|
439
|
+
puts "\n--- Available Endpoints ---"
|
|
440
|
+
help[:endpoints].each do |endpoint_name, endpoint_info|
|
|
441
|
+
begin
|
|
442
|
+
puts "\n#{endpoint_name}"
|
|
443
|
+
puts " #{endpoint_info[:description]}"
|
|
444
|
+
|
|
445
|
+
if endpoint_info[:examples]
|
|
446
|
+
puts " Example: #{endpoint_info[:examples].first}"
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
puts " ↳ help Salad.#{gem_name}.#{endpoint_name} # Full documentation"
|
|
450
|
+
rescue => e
|
|
451
|
+
puts "\n#{endpoint_name}"
|
|
452
|
+
puts " (Documentation temporarily unavailable)"
|
|
453
|
+
end
|
|
454
|
+
end
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
puts "\n"
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def show_generic_gem_help(mod, service_or_gem)
|
|
461
|
+
# Show module methods
|
|
462
|
+
public_methods = mod.methods(false).sort
|
|
463
|
+
if public_methods.any?
|
|
464
|
+
puts "\nPublic methods:"
|
|
465
|
+
public_methods.each { |m| puts " #{m}" }
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# Show constants (often contain useful classes)
|
|
469
|
+
constants = mod.constants(false).sort
|
|
470
|
+
if constants.any?
|
|
471
|
+
puts "\nAvailable classes/constants:"
|
|
472
|
+
module_name = mod.to_s
|
|
473
|
+
constants.each { |c| puts " #{module_name}::#{c}" }
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
puts "\nUsage examples:"
|
|
477
|
+
gem_name = service_or_gem.include?('_') ? service_or_gem :
|
|
478
|
+
(Salad::SERVICES.find { |_, v| v == service_or_gem }&.last || service_or_gem)
|
|
479
|
+
module_name = mod.to_s
|
|
480
|
+
if mod.respond_to?(:help)
|
|
481
|
+
puts " #{module_name}.help # Gem documentation"
|
|
482
|
+
end
|
|
483
|
+
puts " Salad.#{gem_name}.observations(...) # Call available methods"
|
|
484
|
+
puts " #{module_name}.observations(...) # Or use the gem name directly"
|
|
485
|
+
puts "\n\n"
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
Pry.config.commands.create_command('hints', 'Show tips for using the Salad console') do
|
|
490
|
+
def process
|
|
491
|
+
puts "\n=== Salad Tips ==="
|
|
492
|
+
Salad::HINTS.each_with_index do |hint, i|
|
|
493
|
+
puts " #{(i + 1).to_s.rjust(2)}. #{hint}"
|
|
494
|
+
end
|
|
495
|
+
puts "\n"
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
puts "salad v#{Salad::VERSION} — type `help` to list services, `hints` for tips."
|
|
500
|
+
puts "Tip: #{Salad::HINTS.sample}"
|
|
501
|
+
Pry.start
|
|
502
|
+
end
|
|
503
|
+
end
|