mongo 2.0.5 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/auth/cr/conversation.rb +0 -1
  5. data/lib/mongo/client.rb +19 -5
  6. data/lib/mongo/cluster.rb +84 -16
  7. data/lib/mongo/cluster/topology.rb +3 -4
  8. data/lib/mongo/cluster/topology/replica_set.rb +75 -0
  9. data/lib/mongo/cluster/topology/sharded.rb +60 -0
  10. data/lib/mongo/cluster/topology/single.rb +51 -0
  11. data/lib/mongo/cluster/topology/unknown.rb +62 -0
  12. data/lib/mongo/error/invalid_bulk_operation.rb +2 -1
  13. data/lib/mongo/event.rb +9 -9
  14. data/lib/mongo/event/{server_added.rb → description_changed.rb} +6 -5
  15. data/lib/mongo/event/{server_removed.rb → standalone_discovered.rb} +15 -15
  16. data/lib/mongo/operation/aggregate.rb +6 -5
  17. data/lib/mongo/operation/command.rb +5 -6
  18. data/lib/mongo/operation/kill_cursors.rb +3 -2
  19. data/lib/mongo/operation/map_reduce.rb +6 -5
  20. data/lib/mongo/operation/read/collections_info.rb +5 -4
  21. data/lib/mongo/operation/read/get_more.rb +8 -7
  22. data/lib/mongo/operation/read/indexes.rb +4 -3
  23. data/lib/mongo/operation/read/list_collections.rb +5 -4
  24. data/lib/mongo/operation/read/list_indexes.rb +7 -6
  25. data/lib/mongo/operation/read/query.rb +8 -7
  26. data/lib/mongo/operation/read_preferrable.rb +6 -2
  27. data/lib/mongo/operation/write/bulk/bulk_delete.rb +13 -12
  28. data/lib/mongo/operation/write/bulk/bulk_insert.rb +10 -9
  29. data/lib/mongo/operation/write/bulk/bulk_insert/result.rb +2 -2
  30. data/lib/mongo/operation/write/bulk/bulk_update.rb +12 -11
  31. data/lib/mongo/operation/write/create_index.rb +8 -7
  32. data/lib/mongo/operation/write/create_user.rb +4 -3
  33. data/lib/mongo/operation/write/delete.rb +13 -12
  34. data/lib/mongo/operation/write/drop_index.rb +6 -5
  35. data/lib/mongo/operation/write/insert.rb +8 -7
  36. data/lib/mongo/operation/write/insert/result.rb +1 -1
  37. data/lib/mongo/operation/write/remove_user.rb +4 -3
  38. data/lib/mongo/operation/write/update.rb +10 -9
  39. data/lib/mongo/operation/write/update_user.rb +4 -3
  40. data/lib/mongo/server.rb +8 -15
  41. data/lib/mongo/server/context.rb +2 -2
  42. data/lib/mongo/server/description.rb +65 -3
  43. data/lib/mongo/server/description/features.rb +2 -1
  44. data/lib/mongo/server/description/inspector.rb +5 -5
  45. data/lib/mongo/server/description/inspector/{server_added.rb → description_changed.rb} +3 -5
  46. data/lib/mongo/server/description/inspector/{server_removed.rb → standalone_discovered.rb} +12 -15
  47. data/lib/mongo/version.rb +1 -1
  48. data/spec/mongo/auth/cr_spec.rb +1 -1
  49. data/spec/mongo/auth/ldap_spec.rb +1 -1
  50. data/spec/mongo/auth/scram_spec.rb +1 -1
  51. data/spec/mongo/auth/x509_spec.rb +1 -1
  52. data/spec/mongo/client_spec.rb +4 -0
  53. data/spec/mongo/cluster/topology/replica_set_spec.rb +254 -4
  54. data/spec/mongo/cluster/topology/sharded_spec.rb +70 -19
  55. data/spec/mongo/cluster/topology/single_spec.rb +25 -4
  56. data/spec/mongo/cluster/topology/unknown_spec.rb +167 -0
  57. data/spec/mongo/cluster/topology_spec.rb +6 -6
  58. data/spec/mongo/cluster_spec.rb +179 -1
  59. data/spec/mongo/operation/read_preferrable_spec.rb +81 -28
  60. data/spec/mongo/operation/write/bulk/bulk_delete_spec.rb +1 -0
  61. data/spec/mongo/server/connection_pool_spec.rb +10 -10
  62. data/spec/mongo/server/connection_spec.rb +1 -1
  63. data/spec/mongo/server/description/features_spec.rb +23 -3
  64. data/spec/mongo/server/description/inspector/description_changed_spec.rb +78 -0
  65. data/spec/mongo/server/description_spec.rb +238 -0
  66. data/spec/mongo/server/monitor_spec.rb +1 -1
  67. data/spec/mongo/server_discovery_and_monitoring_spec.rb +20 -4
  68. data/spec/mongo/server_selection_spec.rb +2 -2
  69. data/spec/mongo/server_spec.rb +12 -8
  70. data/spec/support/authorization.rb +8 -8
  71. data/spec/support/sdam/rs/discover_passives.yml +36 -0
  72. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +2 -2
  73. data/spec/support/sdam/rs/primary_becomes_standalone.yml +1 -1
  74. data/spec/support/sdam/rs/primary_changes_set_name.yml +2 -2
  75. data/spec/support/sdam/rs/primary_disconnect.yml +1 -1
  76. data/spec/support/sdam/rs/primary_wrong_set_name.yml +1 -1
  77. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +1 -1
  78. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +2 -2
  79. data/spec/support/sdam/sharded/single_mongos.yml +33 -0
  80. data/spec/support/shared/operation.rb +12 -4
  81. data/spec/support/shared/server_selector.rb +1 -1
  82. metadata +12 -10
  83. metadata.gz.sig +0 -0
  84. data/spec/mongo/server/description/inspector/server_added_spec.rb +0 -92
  85. data/spec/mongo/server/description/inspector/server_removed_spec.rb +0 -95
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 009aca6194505e854c2e8ce72b50826c76b81942
4
- data.tar.gz: ec9fa507bafe9c3f79ce430bee77bee9fa6452c3
3
+ metadata.gz: 391a49fc68da96ed3daf59dae9e7006edd6eb7c4
4
+ data.tar.gz: c9b0c470d42785c446fa2c2ae21273bd45f487ea
5
5
  SHA512:
6
- metadata.gz: ac666b6d3593018bf29f22a212bcf36a1b0dff88e9aee3e439c3f599bff14c90b8e2507355b10cc40e4426a7135313fa161d78fdc853ac4c094acfa9122d2c2c
7
- data.tar.gz: 0ff89ae3bc0661a5a39e067a0c96d7a044b291333f314dd6dbd89e8bb7c6d2f4f95ba894785d784acc8126f66896b0505900d56b39c77c5e1a4ac66f8f43f0ca
6
+ metadata.gz: b77002840a210f7bf8c98114529edd2d0a8891dc6518fcd3c3d1337b2648cf2b31b9d3564aab8ab404521232713a452986cfd6202bdaa69c06fde024305befbe
7
+ data.tar.gz: 146937702a43742e5e678845431b61b071c6b3573084dac4f7cb196f3ec601c97d78a248c39c1066a5fd7860f6195a72837210f6b8bdd84b7b6ed03328063f24
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -102,7 +102,6 @@ module Mongo
102
102
  # Conversation.new(user, "admin")
103
103
  #
104
104
  # @param [ Auth::User ] user The user to converse about.
105
- # @param [ String ] database The database to authenticate against.
106
105
  #
107
106
  # @since 2.0.0
108
107
  def initialize(user)
@@ -21,6 +21,12 @@ module Mongo
21
21
  class Client
22
22
  extend Forwardable
23
23
 
24
+ # The options that do not affect the behaviour of a cluster and its
25
+ # subcomponents.
26
+ #
27
+ # @since 2.1.0
28
+ CRUD_OPTIONS = [ :database, :read, :write ].freeze
29
+
24
30
  # @return [ Mongo::Cluster ] cluster The cluster of servers for the client.
25
31
  attr_reader :cluster
26
32
 
@@ -203,8 +209,9 @@ module Mongo
203
209
  clone.tap do |client|
204
210
  client.options.update(new_options)
205
211
  Database.create(client)
206
- # We can't use the same cluster if authentication details have changed.
207
- if new_options[:user] || new_options[:password]
212
+ # We can't use the same cluster if some options that would affect it
213
+ # have changed.
214
+ if cluster_modifying?(new_options)
208
215
  Cluster.create(client)
209
216
  end
210
217
  end
@@ -244,9 +251,7 @@ module Mongo
244
251
  #
245
252
  # @since 2.0.5
246
253
  def list_databases
247
- use(Database::ADMIN).command(
248
- listDatabases: 1
249
- ).first['databases']
254
+ use(Database::ADMIN).command(listDatabases: 1).first['databases']
250
255
  end
251
256
 
252
257
  private
@@ -270,5 +275,14 @@ module Mongo
270
275
  @read_preference = nil
271
276
  @write_concern = nil
272
277
  end
278
+
279
+ def cluster_modifying?(new_options)
280
+ cluster_options = new_options.reject do |name|
281
+ CRUD_OPTIONS.include?(name)
282
+ end
283
+ cluster_options.any? do |name, value|
284
+ options[name] != value
285
+ end
286
+ end
273
287
  end
274
288
  end
@@ -25,9 +25,6 @@ module Mongo
25
25
  include Event::Subscriber
26
26
  include Loggable
27
27
 
28
- # @return [ Array<String> ] The provided seed addresses.
29
- attr_reader :addresses
30
-
31
28
  # @return [ Hash ] The options hash.
32
29
  attr_reader :options
33
30
 
@@ -69,10 +66,9 @@ module Mongo
69
66
  if !addresses.include?(address)
70
67
  if addition_allowed?(address)
71
68
  log_debug([ "Adding #{address.to_s} to the cluster." ])
72
- addresses.push(address)
73
- server = Server.new(address, event_listeners,
74
- options.merge(slave_ok: @slave_ok))
75
- @servers.push(server)
69
+ @update_lock.synchronize { @addresses.push(address) }
70
+ server = Server.new(address, self, event_listeners, options)
71
+ @update_lock.synchronize { @servers.push(server) }
76
72
  server
77
73
  end
78
74
  end
@@ -93,10 +89,10 @@ module Mongo
93
89
  @event_listeners = Event::Listeners.new
94
90
  @options = options.freeze
95
91
  @topology = Topology.initial(seeds, options)
96
- @slave_ok = @topology.single? unless options[:read]
92
+ @update_lock = Mutex.new
97
93
 
98
- subscribe_to(Event::SERVER_ADDED, Event::ServerAdded.new(self))
99
- subscribe_to(Event::SERVER_REMOVED, Event::ServerRemoved.new(self))
94
+ subscribe_to(Event::STANDALONE_DISCOVERED, Event::StandaloneDiscovered.new(self))
95
+ subscribe_to(Event::DESCRIPTION_CHANGED, Event::DescriptionChanged.new(self))
100
96
  subscribe_to(Event::PRIMARY_ELECTED, Event::PrimaryElected.new(self))
101
97
 
102
98
  seeds.each{ |seed| add(seed) }
@@ -138,10 +134,23 @@ module Mongo
138
134
  #
139
135
  # @since 2.0.0
140
136
  def elect_primary!(description)
141
- @topology = topology.elect_primary(description, @servers)
137
+ @topology = topology.elect_primary(description, servers_list)
142
138
  end
143
139
 
144
- # Removed the server from the cluster for the provided address, if it
140
+ # Notify the cluster that a standalone server was discovered so that the
141
+ # topology can be updated accordingly.
142
+ #
143
+ # @example Notify the cluster that a standalone server was discovered.
144
+ # cluster.standalone_discovered
145
+ #
146
+ # @return [ Topology ] The cluster topology.
147
+ #
148
+ # @since 2.0.6
149
+ def standalone_discovered
150
+ @topology = topology.standalone_discovered
151
+ end
152
+
153
+ # Remove the server from the cluster for the provided address, if it
145
154
  # exists.
146
155
  #
147
156
  # @example Remove the server from the cluster.
@@ -153,9 +162,10 @@ module Mongo
153
162
  def remove(host)
154
163
  log_debug([ "#{host} being removed from the cluster." ])
155
164
  address = Address.new(host)
156
- removed_servers = @servers.reject!{ |server| server.address == address }
165
+ removed_servers = @servers.select { |s| s.address == address }
166
+ @update_lock.synchronize { @servers = @servers - removed_servers }
157
167
  removed_servers.each{ |server| server.disconnect! } if removed_servers
158
- addresses.reject!{ |addr| addr == address }
168
+ @update_lock.synchronize { @addresses.reject! { |addr| addr == address } }
159
169
  end
160
170
 
161
171
  # Force a scan of all known servers in the cluster.
@@ -170,7 +180,7 @@ module Mongo
170
180
  #
171
181
  # @since 2.0.0
172
182
  def scan!
173
- @servers.each{ |server| server.scan! } and true
183
+ servers_list.each{ |server| server.scan! } and true
174
184
  end
175
185
 
176
186
  # Get a list of server candidates from the cluster that can have operations
@@ -183,7 +193,37 @@ module Mongo
183
193
  #
184
194
  # @since 2.0.0
185
195
  def servers
186
- topology.servers(@servers.compact).compact
196
+ topology.servers(servers_list.compact).compact
197
+ end
198
+
199
+ # Add hosts in a description to the cluster.
200
+ #
201
+ # @example Add hosts in a description to the cluster.
202
+ # cluster.add_hosts(description)
203
+ #
204
+ # @param [ Mongo::Server::Description ] description The description.
205
+ #
206
+ # @since 2.0.6
207
+ def add_hosts(description)
208
+ if topology.add_hosts?(description, servers_list)
209
+ description.servers.each { |s| add(s) }
210
+ end
211
+ end
212
+
213
+ # Remove hosts in a description from the cluster.
214
+ #
215
+ # @example Remove hosts in a description from the cluster.
216
+ # cluster.remove_hosts(description)
217
+ #
218
+ # @param [ Mongo::Server::Description ] description The description.
219
+ #
220
+ # @since 2.0.6
221
+ def remove_hosts(description)
222
+ if topology.remove_hosts?(description)
223
+ servers_list.each do |s|
224
+ remove(s.address.to_s) if topology.remove_server?(description, s)
225
+ end
226
+ end
187
227
  end
188
228
 
189
229
  # Create a cluster for the provided client, for use when we don't want the
@@ -204,6 +244,18 @@ module Mongo
204
244
  client.instance_variable_set(:@cluster, cluster)
205
245
  end
206
246
 
247
+ # The addresses in the cluster.
248
+ #
249
+ # @example Get the addresses in the cluster.
250
+ # cluster.addresses
251
+ #
252
+ # @return [ Array<Mongo::Address> ] The addresses.
253
+ #
254
+ # @since 2.0.6
255
+ def addresses
256
+ addresses_list
257
+ end
258
+
207
259
  private
208
260
 
209
261
  def direct_connection?(address)
@@ -213,5 +265,21 @@ module Mongo
213
265
  def addition_allowed?(address)
214
266
  !@topology.single? || direct_connection?(address)
215
267
  end
268
+
269
+ def servers_list
270
+ @update_lock.synchronize do
271
+ @servers.reduce([]) do |servers, server|
272
+ servers << server
273
+ end
274
+ end
275
+ end
276
+
277
+ def addresses_list
278
+ @update_lock.synchronize do
279
+ @addresses.reduce([]) do |addresses, address|
280
+ addresses << address
281
+ end
282
+ end
283
+ end
216
284
  end
217
285
  end
@@ -48,12 +48,11 @@ module Mongo
48
48
  # @since 2.0.0
49
49
  def initial(seeds, options)
50
50
  if options.has_key?(:connect)
51
- return OPTIONS.fetch(options[:connect]).new(options, seeds)
52
- end
53
- if options.has_key?(:replica_set)
51
+ OPTIONS.fetch(options[:connect]).new(options, seeds)
52
+ elsif options.has_key?(:replica_set)
54
53
  ReplicaSet.new(options, seeds)
55
54
  else
56
- seeds.size > 1 ? Unknown.new(options, seeds) : Single.new(options, seeds)
55
+ Unknown.new(options, seeds)
57
56
  end
58
57
  end
59
58
  end
@@ -125,6 +125,54 @@ module Mongo
125
125
  end
126
126
  end
127
127
 
128
+ # Whether a server description's hosts may be added to the cluster.
129
+ #
130
+ # @example Check if a description's hosts may be added to the cluster.
131
+ # topology.add_hosts?(description, servers)
132
+ #
133
+ # @param [ Mongo::Server::Description ] description The description.
134
+ # @param [ Array<Mongo::Server> ] servers The cluster servers.
135
+ #
136
+ # @return [ true, false ] Whether a description's hosts may be added.
137
+ #
138
+ # @since 2.0.6
139
+ def add_hosts?(description, servers)
140
+ !!(member_of_this_set?(description) && !has_primary?(servers))
141
+ end
142
+
143
+ # Whether a description can be used to remove hosts from the cluster.
144
+ #
145
+ # @example Check if a description can be used to remove hosts from the cluster.
146
+ # topology.remove_hosts?(description)
147
+ #
148
+ # @param [ Mongo::Server::Description ] description The description.
149
+ #
150
+ # @return [ true, false ] Whether hosts may be removed from the cluster.
151
+ #
152
+ # @since 2.0.6
153
+ def remove_hosts?(description)
154
+ !description.config.empty? &&
155
+ (description.primary? ||
156
+ description.hosts.empty? ||
157
+ !member_of_this_set?(description))
158
+ end
159
+
160
+ # Whether a specific server in the cluster can be removed, given a description.
161
+ #
162
+ # @example Check if a specific server can be removed from the cluster.
163
+ # topology.remove_server?(description, server)
164
+ #
165
+ # @param [ Mongo::Server::Description ] description The description.
166
+ # @param [ Mongo::Serve ] server The server in question.
167
+ #
168
+ # @return [ true, false ] Whether the server can be removed from the cluster.
169
+ #
170
+ # @since 2.0.6
171
+ def remove_server?(description, server)
172
+ remove_self?(description, server) ||
173
+ (member_of_this_set?(description) && !description.lists_server?(server))
174
+ end
175
+
128
176
  # A replica set topology is not sharded.
129
177
  #
130
178
  # @example Is the topology sharded?
@@ -154,6 +202,33 @@ module Mongo
154
202
  #
155
203
  # @since 2.0.0
156
204
  def unknown?; false; end
205
+
206
+ # Notify the topology that a standalone was discovered.
207
+ #
208
+ # @example Notify the topology that a standalone was discovered.
209
+ # topology.standalone_discovered
210
+ #
211
+ # @return [ Topology::ReplicaSet ] Always returns self.
212
+ #
213
+ # @since 2.0.6
214
+ def standalone_discovered; self; end
215
+
216
+ private
217
+
218
+ def has_primary?(servers)
219
+ servers.find { |s| s.primary? }
220
+ end
221
+
222
+ def member_of_this_set?(description)
223
+ description.replica_set_member? &&
224
+ description.replica_set_name == replica_set_name
225
+ end
226
+
227
+ def remove_self?(description, server)
228
+ !member_of_this_set?(description) &&
229
+ description.is_server?(server) &&
230
+ !description.ghost?
231
+ end
157
232
  end
158
233
  end
159
234
  end
@@ -97,6 +97,50 @@ module Mongo
97
97
  servers.select{ |server| server.mongos? }
98
98
  end
99
99
 
100
+ # Whether a server description's hosts may be added to the cluster.
101
+ #
102
+ # @example Check if a description's hosts may be added to the cluster.
103
+ # topology.add_hosts?(description, servers)
104
+ #
105
+ # @param [ Mongo::Server::Description ] description The description.
106
+ # @param [ Array<Mongo::Server> ] servers The cluster servers.
107
+ #
108
+ # @return [ false ] A description's hosts are never added to a
109
+ # sharded cluster.
110
+ #
111
+ # @since 2.0.6
112
+ def add_hosts?(description, servers); false; end
113
+
114
+ # Whether a description can be used to remove hosts from the cluster.
115
+ #
116
+ # @example Check if a description can be used to remove hosts from
117
+ # the cluster.
118
+ # topology.remove_hosts?(description)
119
+ #
120
+ # @param [ Mongo::Server::Description ] description The description.
121
+ #
122
+ # @return [ true ] A description can always be used to remove hosts
123
+ # from a sharded cluster.
124
+ #
125
+ # @since 2.0.6
126
+ def remove_hosts?(description); true; end
127
+
128
+ # Whether a specific server in the cluster can be removed, given a description.
129
+ #
130
+ # @example Check if a specific server can be removed from the cluster.
131
+ # topology.remove_server?(description, server)
132
+ #
133
+ # @param [ Mongo::Server::Description ] description The description.
134
+ # @param [ Mongo::Serve ] server The server in question.
135
+ #
136
+ # @return [ true, false ] Whether the server can be removed from the cluster.
137
+ #
138
+ # @since 2.0.6
139
+ def remove_server?(description, server)
140
+ remove_self?(description, server) ||
141
+ !(server.mongos? || server.unknown?)
142
+ end
143
+
100
144
  # A sharded topology is sharded.
101
145
  #
102
146
  # @example Is the topology sharded?
@@ -126,6 +170,22 @@ module Mongo
126
170
  #
127
171
  # @since 2.0.0
128
172
  def unknown?; false; end
173
+
174
+ # Notify the topology that a standalone was discovered.
175
+ #
176
+ # @example Notify the topology that a standalone was discovered.
177
+ # topology.standalone_discovered
178
+ #
179
+ # @return [ Topology::Sharded ] Always returns self.
180
+ #
181
+ # @since 2.0.6
182
+ def standalone_discovered; self; end
183
+
184
+ private
185
+
186
+ def remove_self?(description, server)
187
+ description.is_server?(server) && !description.mongos?
188
+ end
129
189
  end
130
190
  end
131
191
  end
@@ -101,6 +101,47 @@ module Mongo
101
101
  [ servers.detect { |server| !server.unknown? } ]
102
102
  end
103
103
 
104
+ # Whether a server description's hosts may be added to the cluster.
105
+ #
106
+ # @example Check if a description's hosts may be added to the cluster.
107
+ # topology.add_hosts?(description, servers)
108
+ #
109
+ # @param [ Mongo::Server::Description ] description The description.
110
+ # @param [ Array<Mongo::Server> ] servers The cluster servers.
111
+ #
112
+ # @return [ false ] A description's hosts are never added to a
113
+ # cluster of Single topology.
114
+ #
115
+ # @since 2.0.6
116
+ def add_hosts?(description, servers); false; end
117
+
118
+ # Whether a description can be used to remove hosts from the cluster.
119
+ #
120
+ # @example Check if a description can be used to remove hosts from
121
+ # the cluster.
122
+ # topology.remove_hosts?(description)
123
+ #
124
+ # @param [ Mongo::Server::Description ] description The description.
125
+ #
126
+ # @return [ true ] A description can never be used to remove hosts
127
+ # from a cluster of Single topology.
128
+ #
129
+ # @since 2.0.6
130
+ def remove_hosts?(description); false; end
131
+
132
+ # Whether a specific server in the cluster can be removed, given a description.
133
+ #
134
+ # @example Check if a specific server can be removed from the cluster.
135
+ # topology.remove_server?(description, server)
136
+ #
137
+ # @param [ Mongo::Server::Description ] description The description.
138
+ # @param [ Mongo::Serve ] server The server in question.
139
+ #
140
+ # @return [ false ] A server is never removed from a cluster of Single topology.
141
+ #
142
+ # @since 2.0.6
143
+ def remove_server?(description, server); false; end
144
+
104
145
  # A single topology is not sharded.
105
146
  #
106
147
  # @example Is the topology sharded?
@@ -130,6 +171,16 @@ module Mongo
130
171
  #
131
172
  # @since 2.0.0
132
173
  def unknown?; false; end
174
+
175
+ # Notify the topology that a standalone was discovered.
176
+ #
177
+ # @example Notify the topology that a standalone was discovered.
178
+ # topology.standalone_discovered
179
+ #
180
+ # @return [ Topology::Single ] Always returns self.
181
+ #
182
+ # @since 2.0.6
183
+ def standalone_discovered; self; end
133
184
  end
134
185
  end
135
186
  end