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.
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Generates gem_help.rb by extracting documentation from gem source files
5
+ # Usage: ruby generate_gem_help.rb > lib/salad/gem_help.rb
6
+
7
+ require 'json'
8
+
9
+ def get_gem_description(gem_name)
10
+ descriptions = {
11
+ bananomia: "Wrapper for Bionomia API - access specimen and collection records",
12
+ bell_pepper: "Wrapper for BELS API - georeference locality descriptions via the Biodiversity Enhanced Location Services",
13
+ bok_choy: "Wrapper for BHLnames API - access Biodiversity Heritage Library names data",
14
+ checkerberry: "Wrapper for GNverifier API - verify and search scientific names across curated databases",
15
+ colrapi: "Wrapper for Catalogue of Life (ChecklistBank) API - access global taxonomic checklists and name data",
16
+ crawlyflower: "Wrapper for WoRMS API - access World Register of Marine Species data",
17
+ hookkaido: "Wrapper for OWL Ontology API - access ontology data",
18
+ nasturtium: "Wrapper for iNaturalist API - access species observations and biodiversity data",
19
+ plazucchini: "Wrapper for Plazi TreatmentBank API - access taxonomic treatments and species data",
20
+ syconium: "Wrapper for OpenAlex API - access research publications and academic data",
21
+ wikimelon: "Wrapper for Wikidata API - access linked open knowledge data"
22
+ }
23
+ descriptions[gem_name] || "API wrapper for biodiversity data"
24
+ end
25
+
26
+ def get_gem_api_url(gem_name)
27
+ urls = {
28
+ bananomia: "https://bionomia.net",
29
+ bell_pepper: "https://bels.geo-vocab.org/",
30
+ bok_choy: "https://www.biodiversitylibrary.org/api",
31
+ checkerberry: "https://verifier.globalnames.org",
32
+ colrapi: "https://www.checklistbank.org/api",
33
+ crawlyflower: "https://www.marinespecies.org/rest/",
34
+ hookkaido: "https://www.ontobee.org",
35
+ nasturtium: "https://api.inaturalist.org/v1",
36
+ plazucchini: "https://api.plazi.org",
37
+ syconium: "https://api.openalex.org",
38
+ wikimelon: "https://www.wikidata.org/w/api.php"
39
+ }
40
+ urls[gem_name] || ""
41
+ end
42
+
43
+ class YardDocExtractor
44
+ def initialize(lib_filepath, test_dir = nil)
45
+ @lib_filepath = lib_filepath
46
+ @test_dir = test_dir
47
+ @content = File.read(lib_filepath) if File.exist?(lib_filepath)
48
+ @test_content = load_test_files(test_dir)
49
+ end
50
+
51
+ def extract_all_methods
52
+ return {} unless @content
53
+
54
+ methods = {}
55
+ lines = @content.split("\n")
56
+ i = 0
57
+
58
+ while i < lines.size
59
+ line = lines[i]
60
+
61
+ # Find method definition
62
+ if line.match?(/^\s*def\s+self\.(\w+)/)
63
+ method_name = line.match(/def\s+self\.(\w+)/)[1]
64
+
65
+ # Look backwards for docstring
66
+ docstring_lines = []
67
+ j = i - 1
68
+ while j >= 0 && lines[j].match?(/^\s*#/)
69
+ docstring_lines.unshift(lines[j])
70
+ j -= 1
71
+ end
72
+
73
+ docstring_text = docstring_lines.join("\n")
74
+ data = parse_docstring(docstring_text)
75
+
76
+ # Extract examples from test files
77
+ data[:examples] = extract_examples_for_method(method_name)
78
+
79
+ methods[method_name.to_sym] = data
80
+ end
81
+
82
+ i += 1
83
+ end
84
+
85
+ methods
86
+ end
87
+
88
+ def load_test_files(test_dir)
89
+ return "" unless test_dir && File.directory?(test_dir)
90
+
91
+ test_content = ""
92
+ Dir.glob("#{test_dir}/**/*_test.rb").each do |file|
93
+ test_content += File.read(file) + "\n"
94
+ end
95
+ test_content
96
+ end
97
+
98
+ private
99
+
100
+ def extract_examples_for_method(method_name)
101
+ return [] unless @test_content && @test_content.length > 0
102
+
103
+ examples = []
104
+
105
+ # Look for method calls in test files
106
+ # Patterns: ModuleName.method_name(...) or Gem.method_name(...)
107
+ @test_content.scan(/([A-Z][a-z]*\.)?#{Regexp.escape(method_name)}\([^)]*\)/) do |match|
108
+ example = $&
109
+ examples << example unless examples.include?(example) || example.length > 100
110
+ end
111
+
112
+ examples[0...3] # Return up to 3 examples
113
+ end
114
+
115
+ def parse_docstring(docstring_text)
116
+ lines = docstring_text.split("\n")
117
+ clean_lines = lines.map { |line| line.sub(/^\s*#\s?/, '').rstrip }
118
+
119
+ # Extract summary
120
+ summary_idx = clean_lines.index { |l| !l.empty? && !l.start_with?('@') }
121
+ summary = summary_idx ? clean_lines[summary_idx] : ""
122
+
123
+ # Extract @param lines
124
+ params = {}
125
+ current_param = nil
126
+
127
+ clean_lines.each do |line|
128
+ if line.start_with?('@param ')
129
+ params[current_param[:name]] = current_param[:desc] if current_param
130
+
131
+ if line =~ /@param\s+(\w+)\s+\[([^\]]+)\]\s*(.*?)$/
132
+ param_name = $1.to_sym
133
+ param_desc = $3.strip
134
+ current_param = { name: param_name, desc: param_desc }
135
+ elsif line =~ /@param\s+(\w+)\s+(.*)$/
136
+ param_name = $1.to_sym
137
+ param_desc = $2.strip
138
+ current_param = { name: param_name, desc: param_desc }
139
+ end
140
+ elsif line.start_with?('@') && current_param
141
+ params[current_param[:name]] = current_param[:desc]
142
+ current_param = nil
143
+ elsif current_param && !line.empty?
144
+ current_param[:desc] += " #{line}"
145
+ end
146
+ end
147
+
148
+ params[current_param[:name]] = current_param[:desc] if current_param
149
+
150
+ { summary: summary, params: params }
151
+ end
152
+ end
153
+
154
+ # Configuration for all gems with their test paths (from source repos).
155
+ # Lib paths are resolved from installed gem specs. Test paths are looked up
156
+ # under SFG_SRC_DIR (defaults to ~/src/github.com/speciesfilegroup).
157
+ require 'rubygems'
158
+
159
+ SFG_GEMS = %i[
160
+ bananomia bell_pepper bok_choy checkerberry colrapi crawlyflower
161
+ hookkaido nasturtium plazucchini syconium wikimelon
162
+ ].freeze
163
+
164
+ SFG_SRC_DIR = ENV.fetch('SFG_SRC_DIR', File.expand_path('~/src/github.com/speciesfilegroup'))
165
+
166
+ def gem_lib_path(name)
167
+ Gem::Specification.find_by_name(name.to_s).full_gem_path + "/lib/#{name}.rb"
168
+ rescue Gem::MissingSpecError
169
+ nil
170
+ end
171
+
172
+ GEMS_CONFIG = SFG_GEMS.each_with_object({}) do |name, h|
173
+ h[name] = {
174
+ lib: gem_lib_path(name),
175
+ test: File.join(SFG_SRC_DIR, name.to_s, 'test')
176
+ }
177
+ end.freeze
178
+
179
+ # Extract documentation from all gems
180
+ gem_docs = {}
181
+ GEMS_CONFIG.each do |gem_name, paths|
182
+ extractor = YardDocExtractor.new(paths[:lib], paths[:test])
183
+ gem_docs[gem_name] = extractor.extract_all_methods
184
+ end
185
+
186
+ # Generate Ruby code
187
+ ruby_code = "# frozen_string_literal: true\n\n"
188
+ ruby_code += "module Salad\n"
189
+ ruby_code += " GEM_HELP = {\n"
190
+
191
+ gem_docs.each do |gem_name, methods|
192
+ ruby_code += " #{gem_name.inspect} => {\n"
193
+ ruby_code += " description: \"#{get_gem_description(gem_name)}\",\n"
194
+ ruby_code += " api: \"#{get_gem_api_url(gem_name)}\",\n"
195
+ ruby_code += " endpoints: {\n"
196
+
197
+ methods.each do |method_name, data|
198
+ ruby_code += " #{method_name.inspect} => {\n"
199
+ ruby_code += " description: #{data[:summary].inspect},\n"
200
+
201
+ # Add params
202
+ if data[:params].any?
203
+ ruby_code += " params: {\n"
204
+ data[:params].each do |param_name, param_desc|
205
+ clean_desc = param_desc.gsub(/\n/, ' ').strip
206
+ ruby_code += " #{param_name.inspect} => #{clean_desc.inspect},\n"
207
+ end
208
+ ruby_code += " },\n"
209
+ else
210
+ ruby_code += " params: {},\n"
211
+ end
212
+
213
+ # Add examples
214
+ examples = data[:examples] || []
215
+ if examples.any?
216
+ ruby_code += " examples: [\n"
217
+ examples.each do |example|
218
+ ruby_code += " #{example.inspect},\n"
219
+ end
220
+ ruby_code += " ],\n"
221
+ else
222
+ ruby_code += " examples: [],\n"
223
+ end
224
+
225
+ ruby_code += " },\n"
226
+ end
227
+
228
+ ruby_code += " },\n"
229
+ ruby_code += " },\n"
230
+ end
231
+
232
+ ruby_code += " }.freeze\n"
233
+ ruby_code += "end\n"
234
+
235
+ puts ruby_code
236
+
237
+ # Print summary stats
238
+ STDERR.puts "\n=== GENERATION SUMMARY ===\n\n"
239
+ gem_docs.each do |gem_name, methods|
240
+ STDERR.puts "#{gem_name.to_s.ljust(15)}: #{methods.size} endpoints"
241
+ end
242
+ STDERR.puts "\nTotal endpoints: #{gem_docs.values.map(&:size).sum}"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Salad
4
+ VERSION = '0.0.1'
5
+ end
data/lib/salad.rb ADDED
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'salad/version'
4
+ require 'salad/catalogue_of_life'
5
+
6
+ module Salad
7
+ GEMS = %w[
8
+ bananomia
9
+ bell_pepper
10
+ bok_choy
11
+ checkerberry
12
+ colrapi
13
+ crawlyflower
14
+ hookkaido
15
+ nasturtium
16
+ plazucchini
17
+ syconium
18
+ wikimelon
19
+ ].freeze
20
+
21
+ SERVICES = {
22
+ bels: 'bell_pepper',
23
+ bhlnames: 'bok_choy',
24
+ bionomia: 'bananomia',
25
+ catalogue_of_life: 'colrapi',
26
+ checklistbank: 'colrapi',
27
+ clb: 'colrapi',
28
+ col: 'colrapi',
29
+ gnverifier: 'checkerberry',
30
+ inaturalist: 'nasturtium',
31
+ owl: 'hookkaido',
32
+ openalex: 'syconium',
33
+ plazi: 'plazucchini',
34
+ wikidata: 'wikimelon',
35
+ worms: 'crawlyflower'
36
+ }.freeze
37
+
38
+ class << self
39
+ def gem_module(gem_name)
40
+ name = gem_name.split('_').map(&:capitalize).join
41
+ Object.const_get(name)
42
+ rescue NameError
43
+ raise NameError, "Cannot find top-level module for gem #{gem_name.inspect} (expected #{name})"
44
+ end
45
+
46
+ def services
47
+ SERVICES
48
+ end
49
+
50
+ # Top-level constants Salad will offer for case-insensitive tab-completion
51
+ # in the REPL.
52
+ def completable_names
53
+ @completable_names ||= (['Salad'] + GEMS.map { |g| gem_module(g).to_s }).freeze
54
+ end
55
+
56
+ # Regex matching any non-empty prefix of length >= 3 of any completable name.
57
+ # Built by enumerating prefixes and joining with `|`. Each prefix is escaped
58
+ # so weird characters in a name can't change the regex's meaning. The 3-char
59
+ # minimum keeps short ambiguous input (`s`, `c`) falling through to Pry's
60
+ # default completion.
61
+ COMPLETABLE_PREFIX_MIN = 3
62
+
63
+ def completable_prefix_regex
64
+ @completable_prefix_regex ||= build_prefix_regex(completable_names)
65
+ end
66
+
67
+ # Names offered as completions after `help `. Uses CamelCase module names
68
+ # for the bundled gems (so `help nast<TAB>` -> `help Nasturtium`) plus the
69
+ # lowercase service aliases (so `help inat<TAB>` -> `help inaturalist`).
70
+ # `Salad` itself is the literal services-listing topic.
71
+ def help_completable_names
72
+ @help_completable_names ||= (completable_names + SERVICES.keys.map(&:to_s)).uniq.freeze
73
+ end
74
+
75
+ # Reverse lookup: 'Nasturtium' -> 'nasturtium', 'BellPepper' -> 'bell_pepper'.
76
+ # Returns nil for unknown module names.
77
+ def gem_for_module(module_name)
78
+ @gem_for_module ||= GEMS.each_with_object({}) do |g, h|
79
+ h[gem_module(g).to_s] = g
80
+ end.freeze
81
+ @gem_for_module[module_name]
82
+ end
83
+
84
+ def help_completable_prefix_regex
85
+ @help_completable_prefix_regex ||= build_prefix_regex(help_completable_names, prefix: 'help\\s+')
86
+ end
87
+
88
+ def build_prefix_regex(names, prefix: '')
89
+ prefixes = names.flat_map { |n| (COMPLETABLE_PREFIX_MIN..n.length).map { |i| n[0...i] } }
90
+ Regexp.new("\\A#{prefix}(?:#{prefixes.map { |p| Regexp.escape(p) }.join('|')})\\z", Regexp::IGNORECASE)
91
+ end
92
+
93
+ def help
94
+ puts 'Salad services — access by service name or gem name:'
95
+ service_width = SERVICES.keys.map { |s| s.to_s.length }.max
96
+ gem_width = SERVICES.values.map(&:length).max
97
+ SERVICES.sort.each do |service, gem_name|
98
+ mod = begin
99
+ gem_module(gem_name).to_s
100
+ rescue NameError
101
+ '(not loaded)'
102
+ end
103
+ printf " Salad.%-#{service_width}s Salad.%-#{gem_width}s -> %s\n", service, gem_name, mod
104
+ end
105
+ nil
106
+ end
107
+ end
108
+
109
+ COL_SCOPED_SERVICES = %i[catalogue_of_life col].freeze
110
+
111
+ SERVICES.each do |service, gem_name|
112
+ if COL_SCOPED_SERVICES.include?(service)
113
+ define_singleton_method(service) do
114
+ @catalogue_of_life ||= CatalogueOfLife.new
115
+ end
116
+ else
117
+ define_singleton_method(service) { gem_module(gem_name) }
118
+ end
119
+ end
120
+
121
+ GEMS.each do |gem_name|
122
+ define_singleton_method(gem_name) { gem_module(gem_name) }
123
+ end
124
+ end
125
+
126
+ Salad::GEMS.each { |g| require g }
metadata ADDED
@@ -0,0 +1,297 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: salad
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Geoff Ower
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: pry
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.15'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.15'
26
+ - !ruby/object:Gem::Dependency
27
+ name: bond
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.5'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.5'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rb-readline
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.5'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.5'
54
+ - !ruby/object:Gem::Dependency
55
+ name: bananomia
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.1'
68
+ - !ruby/object:Gem::Dependency
69
+ name: bell_pepper
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.1'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
82
+ - !ruby/object:Gem::Dependency
83
+ name: bok_choy
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: checkerberry
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: colrapi
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.1'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.1'
124
+ - !ruby/object:Gem::Dependency
125
+ name: crawlyflower
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: hookkaido
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '0.1'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '0.1'
152
+ - !ruby/object:Gem::Dependency
153
+ name: nasturtium
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '0.1'
159
+ type: :runtime
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.1'
166
+ - !ruby/object:Gem::Dependency
167
+ name: plazucchini
168
+ requirement: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '0.0'
173
+ type: :runtime
174
+ prerelease: false
175
+ version_requirements: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - "~>"
178
+ - !ruby/object:Gem::Version
179
+ version: '0.0'
180
+ - !ruby/object:Gem::Dependency
181
+ name: syconium
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '0.0'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '0.0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: wikimelon
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '0.0'
201
+ type: :runtime
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '0.0'
208
+ - !ruby/object:Gem::Dependency
209
+ name: bundler
210
+ requirement: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '2.1'
215
+ type: :development
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '2.1'
222
+ - !ruby/object:Gem::Dependency
223
+ name: rake
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - "~>"
227
+ - !ruby/object:Gem::Version
228
+ version: '13.0'
229
+ type: :development
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - "~>"
234
+ - !ruby/object:Gem::Version
235
+ version: '13.0'
236
+ - !ruby/object:Gem::Dependency
237
+ name: test-unit
238
+ requirement: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - "~>"
241
+ - !ruby/object:Gem::Version
242
+ version: '3.6'
243
+ type: :development
244
+ prerelease: false
245
+ version_requirements: !ruby/object:Gem::Requirement
246
+ requirements:
247
+ - - "~>"
248
+ - !ruby/object:Gem::Version
249
+ version: '3.6'
250
+ description: salad bundles the SpeciesFileGroup Ruby API wrappers (bananomia, bell_pepper,
251
+ bok_choy, checkerberry, colrapi, crawlyflower, hookkaido, nasturtium, plazucchini,
252
+ syconium, wikimelon) and ships a `salad` executable that launches a Pry console
253
+ with all of them pre-loaded and available under both their service names (Salad.inaturalist)
254
+ and their gem names (Salad.nasturtium).
255
+ email:
256
+ - gdower@illinois.edu
257
+ executables:
258
+ - salad
259
+ extensions: []
260
+ extra_rdoc_files: []
261
+ files:
262
+ - CHANGELOG.md
263
+ - LICENSE.txt
264
+ - README.md
265
+ - exe/salad
266
+ - lib/salad.rb
267
+ - lib/salad/bond_completer.rb
268
+ - lib/salad/catalogue_of_life.rb
269
+ - lib/salad/console.rb
270
+ - lib/salad/gem_help.rb
271
+ - lib/salad/generate_gem_help.rb
272
+ - lib/salad/version.rb
273
+ homepage: https://github.com/SpeciesFileGroup/salad
274
+ licenses:
275
+ - MIT
276
+ metadata:
277
+ homepage_uri: https://github.com/SpeciesFileGroup/salad
278
+ source_code_uri: https://github.com/SpeciesFileGroup/salad
279
+ changelog_uri: https://github.com/SpeciesFileGroup/salad/releases/tag/v0.0.1
280
+ rdoc_options: []
281
+ require_paths:
282
+ - lib
283
+ required_ruby_version: !ruby/object:Gem::Requirement
284
+ requirements:
285
+ - - ">="
286
+ - !ruby/object:Gem::Version
287
+ version: 2.7.0
288
+ required_rubygems_version: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - ">="
291
+ - !ruby/object:Gem::Version
292
+ version: '0'
293
+ requirements: []
294
+ rubygems_version: 3.6.9
295
+ specification_version: 4
296
+ summary: Unified Pry console for SpeciesFileGroup biodiversity API wrappers.
297
+ test_files: []