bundler 1.17.0.pre.1 → 1.17.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bundler might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -1
- data/README.md +1 -1
- data/bundler.gemspec +1 -1
- data/lib/bundler.rb +28 -10
- data/lib/bundler/build_metadata.rb +2 -2
- data/lib/bundler/cli.rb +33 -5
- data/lib/bundler/cli/binstubs.rb +8 -2
- data/lib/bundler/cli/remove.rb +18 -0
- data/lib/bundler/definition.rb +18 -13
- data/lib/bundler/dependency.rb +2 -2
- data/lib/bundler/dsl.rb +18 -2
- data/lib/bundler/endpoint_specification.rb +1 -1
- data/lib/bundler/env.rb +8 -6
- data/lib/bundler/fetcher.rb +1 -1
- data/lib/bundler/gem_helper.rb +1 -1
- data/lib/bundler/injector.rb +156 -8
- data/lib/bundler/installer/gem_installer.rb +1 -1
- data/lib/bundler/installer/parallel_installer.rb +1 -1
- data/lib/bundler/mirror.rb +2 -2
- data/lib/bundler/plugin.rb +5 -5
- data/lib/bundler/plugin/index.rb +1 -1
- data/lib/bundler/resolver/spec_group.rb +0 -4
- data/lib/bundler/runtime.rb +1 -1
- data/lib/bundler/settings.rb +1 -1
- data/lib/bundler/shared_helpers.rb +6 -0
- data/lib/bundler/source/metadata.rb +1 -1
- data/lib/bundler/templates/newgem/newgem.gemspec.tt +4 -0
- data/lib/bundler/version.rb +1 -1
- data/man/bundle-add.1 +1 -1
- data/man/bundle-add.1.txt +1 -1
- data/man/bundle-binstubs.1 +1 -1
- data/man/bundle-binstubs.1.txt +1 -1
- data/man/bundle-check.1 +1 -1
- data/man/bundle-check.1.txt +1 -1
- data/man/bundle-clean.1 +1 -1
- data/man/bundle-clean.1.txt +1 -1
- data/man/bundle-config.1 +1 -1
- data/man/bundle-config.1.txt +1 -1
- data/man/bundle-doctor.1 +1 -1
- data/man/bundle-doctor.1.txt +1 -1
- data/man/bundle-exec.1 +1 -1
- data/man/bundle-exec.1.txt +1 -1
- data/man/bundle-gem.1 +1 -1
- data/man/bundle-gem.1.txt +1 -1
- data/man/bundle-info.1 +1 -1
- data/man/bundle-info.1.txt +1 -1
- data/man/bundle-init.1 +1 -1
- data/man/bundle-init.1.txt +1 -1
- data/man/bundle-inject.1 +1 -1
- data/man/bundle-inject.1.txt +1 -1
- data/man/bundle-install.1 +6 -3
- data/man/bundle-install.1.txt +118 -107
- data/man/bundle-install.ronn +12 -3
- data/man/bundle-list.1 +1 -1
- data/man/bundle-list.1.txt +1 -1
- data/man/bundle-lock.1 +1 -1
- data/man/bundle-lock.1.txt +1 -1
- data/man/bundle-open.1 +1 -1
- data/man/bundle-open.1.txt +1 -1
- data/man/bundle-outdated.1 +1 -1
- data/man/bundle-outdated.1.txt +1 -1
- data/man/bundle-package.1 +1 -1
- data/man/bundle-package.1.txt +1 -1
- data/man/bundle-platform.1 +1 -1
- data/man/bundle-platform.1.txt +1 -1
- data/man/bundle-pristine.1 +1 -1
- data/man/bundle-pristine.1.txt +1 -1
- data/man/bundle-remove.1 +31 -0
- data/man/bundle-remove.1.txt +34 -0
- data/man/bundle-remove.ronn +23 -0
- data/man/bundle-show.1 +1 -1
- data/man/bundle-show.1.txt +1 -1
- data/man/bundle-update.1 +11 -7
- data/man/bundle-update.1.txt +71 -68
- data/man/bundle-update.ronn +11 -7
- data/man/bundle-viz.1 +1 -1
- data/man/bundle-viz.1.txt +1 -1
- data/man/bundle.1 +1 -1
- data/man/bundle.1.txt +1 -1
- data/man/gemfile.5 +1 -1
- data/man/gemfile.5.txt +1 -1
- data/man/index.txt +1 -0
- metadata +7 -2
data/lib/bundler/fetcher.rb
CHANGED
data/lib/bundler/gem_helper.rb
CHANGED
@@ -153,7 +153,7 @@ module Bundler
|
|
153
153
|
sh "git tag -m \"Version #{version}\" #{version_tag}"
|
154
154
|
Bundler.ui.confirm "Tagged #{version_tag}."
|
155
155
|
yield if block_given?
|
156
|
-
rescue
|
156
|
+
rescue RuntimeError
|
157
157
|
Bundler.ui.error "Untagging #{version_tag} due to error."
|
158
158
|
sh_with_code "git tag -d #{version_tag}"
|
159
159
|
raise
|
data/lib/bundler/injector.rb
CHANGED
@@ -2,13 +2,20 @@
|
|
2
2
|
|
3
3
|
module Bundler
|
4
4
|
class Injector
|
5
|
+
INJECTED_GEMS = "injected gems".freeze
|
6
|
+
|
5
7
|
def self.inject(new_deps, options = {})
|
6
8
|
injector = new(new_deps, options)
|
7
9
|
injector.inject(Bundler.default_gemfile, Bundler.default_lockfile)
|
8
10
|
end
|
9
11
|
|
10
|
-
def
|
11
|
-
|
12
|
+
def self.remove(gems, options = {})
|
13
|
+
injector = new(gems, options)
|
14
|
+
injector.remove(Bundler.default_gemfile, Bundler.default_lockfile)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(deps, options = {})
|
18
|
+
@deps = deps
|
12
19
|
@options = options
|
13
20
|
end
|
14
21
|
|
@@ -28,19 +35,19 @@ module Bundler
|
|
28
35
|
builder.eval_gemfile(gemfile_path)
|
29
36
|
|
30
37
|
# don't inject any gems that are already in the Gemfile
|
31
|
-
@
|
38
|
+
@deps -= builder.dependencies
|
32
39
|
|
33
40
|
# add new deps to the end of the in-memory Gemfile
|
34
41
|
# Set conservative versioning to false because
|
35
42
|
# we want to let the resolver resolve the version first
|
36
|
-
builder.eval_gemfile(
|
43
|
+
builder.eval_gemfile(INJECTED_GEMS, build_gem_lines(false)) if @deps.any?
|
37
44
|
|
38
45
|
# resolve to see if the new deps broke anything
|
39
46
|
@definition = builder.to_definition(lockfile_path, {})
|
40
47
|
@definition.resolve_remotely!
|
41
48
|
|
42
49
|
# since nothing broke, we can add those gems to the gemfile
|
43
|
-
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @
|
50
|
+
append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @deps.any?
|
44
51
|
|
45
52
|
# since we resolved successfully, write out the lockfile
|
46
53
|
@definition.lock(Bundler.default_lockfile)
|
@@ -49,7 +56,21 @@ module Bundler
|
|
49
56
|
Bundler.reset_paths!
|
50
57
|
|
51
58
|
# return an array of the deps that we added
|
52
|
-
@
|
59
|
+
@deps
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
|
64
|
+
# @param [Pathname] lockfile_path The lockfile from which to remove dependencies.
|
65
|
+
# @return [Array]
|
66
|
+
def remove(gemfile_path, lockfile_path)
|
67
|
+
# remove gems from each gemfiles we have
|
68
|
+
Bundler.definition.gemfiles.each do |path|
|
69
|
+
deps = remove_deps(path)
|
70
|
+
|
71
|
+
show_warning("No gems were removed from the gemfile.") if deps.empty?
|
72
|
+
|
73
|
+
deps.each {|dep| Bundler.ui.confirm "#{SharedHelpers.pretty_dependency(dep, false)} was removed." }
|
53
74
|
end
|
54
75
|
end
|
55
76
|
|
@@ -76,7 +97,7 @@ module Bundler
|
|
76
97
|
end
|
77
98
|
|
78
99
|
def build_gem_lines(conservative_versioning)
|
79
|
-
@
|
100
|
+
@deps.map do |d|
|
80
101
|
name = d.name.dump
|
81
102
|
|
82
103
|
requirement = if conservative_versioning
|
@@ -86,7 +107,7 @@ module Bundler
|
|
86
107
|
end
|
87
108
|
|
88
109
|
if d.groups != Array(:default)
|
89
|
-
group = d.groups.size == 1 ? ", :group => #{d.groups.inspect}" : ", :groups => #{d.groups.inspect}"
|
110
|
+
group = d.groups.size == 1 ? ", :group => #{d.groups.first.inspect}" : ", :groups => #{d.groups.inspect}"
|
90
111
|
end
|
91
112
|
|
92
113
|
source = ", :source => \"#{d.source}\"" unless d.source.nil?
|
@@ -101,5 +122,132 @@ module Bundler
|
|
101
122
|
f.puts new_gem_lines
|
102
123
|
end
|
103
124
|
end
|
125
|
+
|
126
|
+
# evalutes a gemfile to remove the specified gem
|
127
|
+
# from it.
|
128
|
+
def remove_deps(gemfile_path)
|
129
|
+
initial_gemfile = IO.readlines(gemfile_path)
|
130
|
+
|
131
|
+
Bundler.ui.info "Removing gems from #{gemfile_path}"
|
132
|
+
|
133
|
+
# evaluate the Gemfile we have
|
134
|
+
builder = Dsl.new
|
135
|
+
builder.eval_gemfile(gemfile_path)
|
136
|
+
|
137
|
+
removed_deps = remove_gems_from_dependencies(builder, @deps, gemfile_path)
|
138
|
+
|
139
|
+
# abort the opertion if no gems were removed
|
140
|
+
# no need to operate on gemfile furthur
|
141
|
+
return [] if removed_deps.empty?
|
142
|
+
|
143
|
+
cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path)
|
144
|
+
|
145
|
+
SharedHelpers.write_to_gemfile(gemfile_path, cleaned_gemfile)
|
146
|
+
|
147
|
+
# check for errors
|
148
|
+
# including extra gems being removed
|
149
|
+
# or some gems not being removed
|
150
|
+
# and return the actual removed deps
|
151
|
+
cross_check_for_errors(gemfile_path, builder.dependencies, removed_deps, initial_gemfile)
|
152
|
+
end
|
153
|
+
|
154
|
+
# @param [Dsl] builder Dsl object of current Gemfile.
|
155
|
+
# @param [Array] gems Array of names of gems to be removed.
|
156
|
+
# @param [Pathname] path of the Gemfile
|
157
|
+
# @return [Array] removed_deps Array of removed dependencies.
|
158
|
+
def remove_gems_from_dependencies(builder, gems, gemfile_path)
|
159
|
+
removed_deps = []
|
160
|
+
|
161
|
+
gems.each do |gem_name|
|
162
|
+
deleted_dep = builder.dependencies.find {|d| d.name == gem_name }
|
163
|
+
|
164
|
+
if deleted_dep.nil?
|
165
|
+
raise GemfileError, "`#{gem_name}` is not specified in #{gemfile_path} so it could not be removed."
|
166
|
+
end
|
167
|
+
|
168
|
+
builder.dependencies.delete(deleted_dep)
|
169
|
+
|
170
|
+
removed_deps << deleted_dep
|
171
|
+
end
|
172
|
+
|
173
|
+
removed_deps
|
174
|
+
end
|
175
|
+
|
176
|
+
# @param [Array] gems Array of names of gems to be removed.
|
177
|
+
# @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
|
178
|
+
def remove_gems_from_gemfile(gems, gemfile_path)
|
179
|
+
patterns = /gem\s+(['"])#{Regexp.union(gems)}\1|gem\s*\((['"])#{Regexp.union(gems)}\2\)/
|
180
|
+
|
181
|
+
# remove lines which match the regex
|
182
|
+
new_gemfile = IO.readlines(gemfile_path).reject {|line| line.match(patterns) }
|
183
|
+
|
184
|
+
# remove lone \n and append them with other strings
|
185
|
+
new_gemfile.each_with_index do |_line, index|
|
186
|
+
if new_gemfile[index + 1] == "\n"
|
187
|
+
new_gemfile[index] += new_gemfile[index + 1]
|
188
|
+
new_gemfile.delete_at(index + 1)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
%w[group source env install_if].each {|block| remove_nested_blocks(new_gemfile, block) }
|
193
|
+
|
194
|
+
new_gemfile.join.chomp
|
195
|
+
end
|
196
|
+
|
197
|
+
# @param [Array] gemfile Array of gemfile contents.
|
198
|
+
# @param [String] block_name Name of block name to look for.
|
199
|
+
def remove_nested_blocks(gemfile, block_name)
|
200
|
+
nested_blocks = 0
|
201
|
+
|
202
|
+
# count number of nested blocks
|
203
|
+
gemfile.each_with_index {|line, index| nested_blocks += 1 if !gemfile[index + 1].nil? && gemfile[index + 1].include?(block_name) && line.include?(block_name) }
|
204
|
+
|
205
|
+
while nested_blocks >= 0
|
206
|
+
nested_blocks -= 1
|
207
|
+
|
208
|
+
gemfile.each_with_index do |line, index|
|
209
|
+
next unless !line.nil? && line.include?(block_name)
|
210
|
+
if gemfile[index + 1] =~ /^\s*end\s*$/
|
211
|
+
gemfile[index] = nil
|
212
|
+
gemfile[index + 1] = nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
gemfile.compact!
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# @param [Pathname] gemfile_path The Gemfile from which to remove dependencies.
|
221
|
+
# @param [Array] original_deps Array of original dependencies.
|
222
|
+
# @param [Array] removed_deps Array of removed dependencies.
|
223
|
+
# @param [Array] initial_gemfile Contents of original Gemfile before any operation.
|
224
|
+
def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_gemfile)
|
225
|
+
# evalute the new gemfile to look for any failure cases
|
226
|
+
builder = Dsl.new
|
227
|
+
builder.eval_gemfile(gemfile_path)
|
228
|
+
|
229
|
+
# record gems which were removed but not requested
|
230
|
+
extra_removed_gems = original_deps - builder.dependencies
|
231
|
+
|
232
|
+
# if some extra gems were removed then raise error
|
233
|
+
# and revert Gemfile to original
|
234
|
+
unless extra_removed_gems.empty?
|
235
|
+
SharedHelpers.write_to_gemfile(gemfile_path, initial_gemfile.join)
|
236
|
+
|
237
|
+
raise InvalidOption, "Gems could not be removed. #{extra_removed_gems.join(", ")} would also have been removed. Bundler cannot continue."
|
238
|
+
end
|
239
|
+
|
240
|
+
# record gems which could not be removed due to some reasons
|
241
|
+
errored_deps = builder.dependencies.select {|d| d.gemfile == gemfile_path } & removed_deps.select {|d| d.gemfile == gemfile_path }
|
242
|
+
|
243
|
+
show_warning "#{errored_deps.map(&:name).join(", ")} could not be removed." unless errored_deps.empty?
|
244
|
+
|
245
|
+
# return actual removed dependencies
|
246
|
+
removed_deps - errored_deps
|
247
|
+
end
|
248
|
+
|
249
|
+
def show_warning(message)
|
250
|
+
Bundler.ui.info Bundler.ui.add_color(message, :yellow)
|
251
|
+
end
|
104
252
|
end
|
105
253
|
end
|
data/lib/bundler/mirror.rb
CHANGED
@@ -152,7 +152,7 @@ module Bundler
|
|
152
152
|
socket.connect_nonblock(address)
|
153
153
|
rescue Errno::EINPROGRESS
|
154
154
|
wait_for_writtable_socket(socket, address, timeout)
|
155
|
-
rescue # Connection failed somehow, again
|
155
|
+
rescue RuntimeError # Connection failed somehow, again
|
156
156
|
false
|
157
157
|
end
|
158
158
|
end
|
@@ -172,7 +172,7 @@ module Bundler
|
|
172
172
|
socket.connect_nonblock(address)
|
173
173
|
rescue Errno::EISCONN
|
174
174
|
true
|
175
|
-
rescue # Connection failed
|
175
|
+
rescue StandardError # Connection failed
|
176
176
|
false
|
177
177
|
end
|
178
178
|
end
|
data/lib/bundler/plugin.rb
CHANGED
@@ -67,7 +67,7 @@ module Bundler
|
|
67
67
|
installed_specs = Installer.new.install_definition(definition)
|
68
68
|
|
69
69
|
save_plugins plugins, installed_specs, builder.inferred_plugins
|
70
|
-
rescue => e
|
70
|
+
rescue RuntimeError => e
|
71
71
|
unless e.is_a?(GemfileError)
|
72
72
|
Bundler.ui.error "Failed to install plugin: #{e.message}\n #{e.backtrace[0]}"
|
73
73
|
end
|
@@ -81,8 +81,8 @@ module Bundler
|
|
81
81
|
|
82
82
|
# The directory root for all plugin related data
|
83
83
|
#
|
84
|
-
#
|
85
|
-
# in user_bundle_path
|
84
|
+
# If run in an app, points to local root, in app_config_path
|
85
|
+
# Otherwise, points to global root, in Bundler.user_bundle_path("plugin")
|
86
86
|
def root
|
87
87
|
@root ||= if SharedHelpers.in_bundle?
|
88
88
|
local_root
|
@@ -97,7 +97,7 @@ module Bundler
|
|
97
97
|
|
98
98
|
# The global directory root for all plugin related data
|
99
99
|
def global_root
|
100
|
-
Bundler.user_bundle_path
|
100
|
+
Bundler.user_bundle_path("plugin")
|
101
101
|
end
|
102
102
|
|
103
103
|
# The cache directory for plugin stuffs
|
@@ -271,7 +271,7 @@ module Bundler
|
|
271
271
|
load path.join(PLUGIN_FILE_NAME)
|
272
272
|
|
273
273
|
@loaded_plugin_names << name
|
274
|
-
rescue => e
|
274
|
+
rescue RuntimeError => e
|
275
275
|
Bundler.ui.error "Failed loading plugin #{name}: #{e.message}"
|
276
276
|
raise
|
277
277
|
end
|
data/lib/bundler/plugin/index.rb
CHANGED
@@ -54,10 +54,6 @@ module Bundler
|
|
54
54
|
dependencies.concat(metadata_dependencies).flatten
|
55
55
|
end
|
56
56
|
|
57
|
-
def platforms_for_dependency_named(dependency)
|
58
|
-
__dependencies.select {|_, deps| deps.map(&:name).include? dependency }.keys
|
59
|
-
end
|
60
|
-
|
61
57
|
def ==(other)
|
62
58
|
return unless other.is_a?(SpecGroup)
|
63
59
|
name == other.name &&
|
data/lib/bundler/runtime.rb
CHANGED
@@ -79,7 +79,7 @@ module Bundler
|
|
79
79
|
required_file = file
|
80
80
|
begin
|
81
81
|
Kernel.require file
|
82
|
-
rescue => e
|
82
|
+
rescue RuntimeError => e
|
83
83
|
raise e if e.is_a?(LoadError) # we handle this a little later
|
84
84
|
raise Bundler::GemRequireError.new e,
|
85
85
|
"There was an error while trying to load the gem '#{file}'."
|
data/lib/bundler/settings.rb
CHANGED
@@ -197,10 +197,12 @@ module Bundler
|
|
197
197
|
def pretty_dependency(dep, print_source = false)
|
198
198
|
msg = String.new(dep.name)
|
199
199
|
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
|
200
|
+
|
200
201
|
if dep.is_a?(Bundler::Dependency)
|
201
202
|
platform_string = dep.platforms.join(", ")
|
202
203
|
msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY
|
203
204
|
end
|
205
|
+
|
204
206
|
msg << " from the `#{dep.source}` source" if print_source && dep.source
|
205
207
|
msg
|
206
208
|
end
|
@@ -223,6 +225,10 @@ module Bundler
|
|
223
225
|
Digest(name)
|
224
226
|
end
|
225
227
|
|
228
|
+
def write_to_gemfile(gemfile_path, contents)
|
229
|
+
filesystem_access(gemfile_path) {|g| File.open(g, "w") {|file| file.puts contents } }
|
230
|
+
end
|
231
|
+
|
226
232
|
private
|
227
233
|
|
228
234
|
def validate_bundle_path
|
@@ -19,7 +19,7 @@ module Bundler
|
|
19
19
|
# can't point to the actual gemspec or else the require paths will be wrong
|
20
20
|
s.loaded_from = File.expand_path("..", __FILE__)
|
21
21
|
end
|
22
|
-
if loaded_spec =
|
22
|
+
if loaded_spec = Bundler.rubygems.loaded_specs("bundler")
|
23
23
|
idx << loaded_spec # this has to come after the fake gemspec, to override it
|
24
24
|
elsif local_spec = Bundler.rubygems.find_name("bundler").find {|s| s.version.to_s == VERSION }
|
25
25
|
idx << local_spec
|
@@ -23,6 +23,10 @@ Gem::Specification.new do |spec|
|
|
23
23
|
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
24
24
|
if spec.respond_to?(:metadata)
|
25
25
|
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
26
|
+
|
27
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
28
|
+
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
|
29
|
+
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
|
26
30
|
else
|
27
31
|
raise "RubyGems 2.0 or newer is required to protect against " \
|
28
32
|
"public gem pushes."
|
data/lib/bundler/version.rb
CHANGED
@@ -7,7 +7,7 @@ module Bundler
|
|
7
7
|
# We're doing this because we might write tests that deal
|
8
8
|
# with other versions of bundler and we are unsure how to
|
9
9
|
# handle this better.
|
10
|
-
VERSION = "1.17.0.pre.
|
10
|
+
VERSION = "1.17.0.pre.2" unless defined?(::Bundler::VERSION)
|
11
11
|
|
12
12
|
def self.overwrite_loaded_gem_version
|
13
13
|
begin
|
data/man/bundle-add.1
CHANGED
data/man/bundle-add.1.txt
CHANGED
data/man/bundle-binstubs.1
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
3
|
.
|
4
|
-
.TH "BUNDLE\-BINSTUBS" "1" "
|
4
|
+
.TH "BUNDLE\-BINSTUBS" "1" "October 2018" "" ""
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBbundle\-binstubs\fR \- Install the binstubs of the listed gems
|
data/man/bundle-binstubs.1.txt
CHANGED
data/man/bundle-check.1
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
.\" generated with Ronn/v0.7.3
|
2
2
|
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
3
3
|
.
|
4
|
-
.TH "BUNDLE\-CHECK" "1" "
|
4
|
+
.TH "BUNDLE\-CHECK" "1" "October 2018" "" ""
|
5
5
|
.
|
6
6
|
.SH "NAME"
|
7
7
|
\fBbundle\-check\fR \- Verifies if dependencies are satisfied by installed gems
|