mail_plugger 1.6.1 → 1.7.1

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
2
  SHA256:
3
- metadata.gz: 9c14f1e3d3349963e366c96c190b4c5f4a5952acab1d3edea57c1362a2f2cbbe
4
- data.tar.gz: d5c5c499539d4acfd55314cc740a6a59c5dc6a464f4203e09668e48d4e4b422f
3
+ metadata.gz: 2ecc2b0b6ceb6e5a70fea8fad50d688d0366bb671f39a0d414e688c193691271
4
+ data.tar.gz: 772b7c107ddf434d6511ff5218bd271603288ddfe724d1f0a9c81ce6c315ad33
5
5
  SHA512:
6
- metadata.gz: aaf64a78ca834024faf8c7c68e4b8170ae9f1b40a92828beebc8f4fa465bc9cd741ebd3aff470865512bb4103e2c5ba68c47d1d530dec85d096332edd4541ef8
7
- data.tar.gz: ad58904037919d40948361eb9a6e3b39e5e6cbda1bee4c331bd6ad597f808457b6d9b05f7700799ff967b2378d4bccd8f3d58ff37d5a2e343123329d10cff4cf
6
+ metadata.gz: f916dab916d7b03d8993c79522ba85d109f506ad5139082d9fb2bbf8d7ad8603723a011965b14c2b93413bf63f1b93ccb586de8aef3b352674672a250fb86613
7
+ data.tar.gz: 80586fbf27d6be09aadb4d64adb889e2efe52baef42aa7a1ed6fbfdea6b3eb722705e281262b180ed19f326eaabce26f08155848bc7d3bb8cfad15eadf8051d5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Change log
2
2
 
3
+ ## 1.7.1 (2023-01-25)
4
+
5
+ ### Changes
6
+
7
+ * Update MailGrabber to version 1.3.1 because of security issues in the rack gem.
8
+ * Update bundler and gems.
9
+
10
+
11
+ ## 1.7.0 (2022-12-27)
12
+
13
+ ### New features
14
+
15
+ * Add `configure` method to MailPlugger.
16
+
17
+ ### Changes
18
+
19
+ * Add mail 2.8 gem support.
20
+ * Update the documentations.
21
+ * Change RuboCop rules.
22
+ * Update gem description.
23
+ * Refactor RSpec tests.
24
+ * Update bundler and gems.
25
+
26
+ ### Bug fixes
27
+
28
+ * Fix the Ruby version problem in the GitHub Actions workflow file.
29
+
30
+
3
31
  ## 1.6.1 (2022-05-27)
4
32
 
5
33
  ### Changes
data/README.md CHANGED
@@ -7,7 +7,12 @@
7
7
  [![Maintainability](https://api.codeclimate.com/v1/badges/137881380fc475b4a836/maintainability)](https://codeclimate.com/github/MailToolbox/mail_plugger/maintainability)
8
8
  [![Test Coverage](https://api.codeclimate.com/v1/badges/137881380fc475b4a836/test_coverage)](https://codeclimate.com/github/MailToolbox/mail_plugger/test_coverage)
9
9
 
10
- **MailPlugger** helps you to use different mail providers' **SMTP** and **API**. You can use any SMTPs and APIs which one would like to use. It allows you to send different emails with different SMTPs and APIs. Also, it can help to move between providers, load balancing or cost management.
10
+ **MailPlugger** helps you to use one or more mail providers. You can send emails via SMTP and API as well.
11
+
12
+ Also, it can help:
13
+ - move between mail providers
14
+ - load balancing
15
+ - cost management
11
16
 
12
17
  ## Installation
13
18
 
@@ -27,10 +32,12 @@ Or install it yourself as:
27
32
 
28
33
  ## Usage
29
34
 
35
+ - [How to use MailPlugger.configure method](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_configure_method.md)
30
36
  - [How to use MailPlugger.plug_in method](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_plug_in_method.md)
31
37
  - [How to use MailPlugger::DeliveryMethod class](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_delivery_method.md)
32
38
  - [How to use MailPlugger in a Ruby script or IRB console](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_in_script_or_console.md)
33
39
  - [How to use MailPlugger in Ruby on Rails](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_in_ruby_on_rails.md)
40
+ - [How to use MailPlugger.configure method in Ruby on Rails](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_configure_method_in_ruby_on_rails.md)
34
41
  - [How to use more SMTP delivery systems in Ruby on Rails](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_more_smtp_delivery_systems_in_ruby_on_rails.md)
35
42
  - [How to combine SMTP and API delivery systems in Ruby on Rails](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_smtp_and_api_delivery_systems_in_ruby_on_rails.md)
36
43
  - [How to add API specific options to the mailer method in Ruby on Rails](https://github.com/MailToolbox/mail_plugger/blob/main/docs/usage_of_api_specific_options_in_ruby_on_rails.md)
@@ -20,7 +20,7 @@ module FakePlugger
20
20
  # e.g. { return_response: true }
21
21
  #
22
22
  # @option options [String/Symbol] default_delivery_system
23
- # e.g. 'defined_api'
23
+ # e.g. 'api_client'
24
24
  #
25
25
  # @option options [Boolean] debug
26
26
  # if true, it will show debug information
@@ -119,6 +119,7 @@ module FakePlugger
119
119
  # # Using API:
120
120
  #
121
121
  # MailPlugger.plug_in('test_api_client') do |api|
122
+ # api.client = DefinedApiClientClass
122
123
  # api.delivery_options = %i[from to subject body]
123
124
  # api.delivery_settings = {
124
125
  # fake_plugger_debug: true,
@@ -126,7 +127,6 @@ module FakePlugger
126
127
  # fake_plugger_use_mail_grabber: true,
127
128
  # fake_plugger_response: { response: 'OK' }
128
129
  # }
129
- # api.client = DefinedApiClientClass
130
130
  # end
131
131
  #
132
132
  # message = Mail.new(from: 'from@example.com', to: 'to@example.com',
@@ -140,8 +140,8 @@ module FakePlugger
140
140
  # subject: 'Test email', body: 'Test email body')
141
141
  #
142
142
  # FakePlugger::DeliveryMethod.new(
143
- # delivery_options: %i[from to subject body],
144
143
  # client: DefinedApiClientClass,
144
+ # delivery_options: %i[from to subject body],
145
145
  # debug: true,
146
146
  # raw_message: true,
147
147
  # use_mail_grabber: true,
@@ -177,7 +177,7 @@ module FakePlugger
177
177
  end
178
178
 
179
179
  # Debug information for API
180
- def debug_info_for_api
180
+ def debug_info_for_api # rubocop:disable Metrics/AbcSize
181
181
  puts <<~DEBUG_INFO
182
182
 
183
183
  ===================== FakePlugger::DeliveryMethod =====================
@@ -190,6 +190,14 @@ module FakePlugger
190
190
 
191
191
  ==> @delivery_settings: #{@delivery_settings.inspect}
192
192
 
193
+ ==> @passed_default_delivery_system: #{
194
+ @passed_default_delivery_system.inspect
195
+ }
196
+
197
+ ==> @default_delivery_options: #{@default_delivery_options.inspect}
198
+
199
+ ==> @sending_method: #{@sending_method.inspect}
200
+
193
201
  ==> @default_delivery_system: #{@default_delivery_system.inspect}
194
202
 
195
203
  ==> @message: #{@message.inspect}
@@ -221,6 +229,12 @@ module FakePlugger
221
229
 
222
230
  ==> @delivery_settings: #{@delivery_settings.inspect}
223
231
 
232
+ ==> @passed_default_delivery_system: #{
233
+ @passed_default_delivery_system.inspect
234
+ }
235
+
236
+ ==> @sending_method: #{@sending_method.inspect}
237
+
224
238
  ==> @default_delivery_system: #{@default_delivery_system.inspect}
225
239
 
226
240
  ==> @message: #{@message.inspect}
@@ -282,15 +296,16 @@ module FakePlugger
282
296
 
283
297
  # Extract settings values and update attributes.
284
298
  def update_settings
285
- # rubocop:disable Naming/MemoizedInstanceVariableName
286
- @debug ||= settings[:fake_plugger_debug] || false
299
+ @response = settings[:fake_plugger_response] if @response.nil?
287
300
 
288
- @raw_message ||= settings[:fake_plugger_raw_message] || false
301
+ %w[debug raw_message use_mail_grabber].each do |variable_name|
302
+ next unless instance_variable_get("@#{variable_name}").nil?
289
303
 
290
- @response ||= settings[:fake_plugger_response]
291
-
292
- @use_mail_grabber ||= settings[:fake_plugger_use_mail_grabber] || false
293
- # rubocop:enable Naming/MemoizedInstanceVariableName
304
+ instance_variable_set(
305
+ "@#{variable_name}",
306
+ settings[:"fake_plugger_#{variable_name}"] || false
307
+ )
308
+ end
294
309
  end
295
310
  end
296
311
  end
@@ -22,20 +22,33 @@ module MailPlugger
22
22
  # @option options [String/Symbol] default_delivery_system
23
23
  # e.g. 'defined_api'
24
24
  def initialize(options = {})
25
- @client = options[:client] || MailPlugger.client
25
+ @client = options[:client] ||
26
+ MailPlugger.client
26
27
 
27
- @delivery_options = options[:delivery_options] ||
28
- MailPlugger.delivery_options
28
+ @delivery_options = options[:delivery_options] ||
29
+ MailPlugger.delivery_options
29
30
 
30
- @delivery_settings = options[:delivery_settings] ||
31
- MailPlugger.delivery_settings
31
+ @delivery_settings = options[:delivery_settings] ||
32
+ MailPlugger.delivery_settings
32
33
 
33
- @delivery_systems = MailPlugger.delivery_systems
34
+ @passed_default_delivery_system = options[:default_delivery_system] ||
35
+ MailPlugger.default_delivery_system
34
36
 
35
- @default_delivery_system = options[:default_delivery_system] ||
36
- default_delivery_system_get
37
+ # -----------------------------------------------------------------------
37
38
 
38
- @message = nil
39
+ @default_delivery_options = MailPlugger.default_delivery_options
40
+
41
+ @delivery_systems = MailPlugger.delivery_systems
42
+
43
+ @rotatable_delivery_systems = MailPlugger.rotatable_delivery_systems
44
+
45
+ @sending_method = MailPlugger.sending_method
46
+
47
+ # -----------------------------------------------------------------------
48
+
49
+ @default_delivery_system = default_delivery_system_get
50
+
51
+ @message = nil
39
52
  end
40
53
 
41
54
  # Using SMTP:
@@ -98,8 +111,8 @@ module MailPlugger
98
111
  # # Using API:
99
112
  #
100
113
  # MailPlugger.plug_in('test_api_client') do |api|
101
- # api.delivery_options = %i[from to subject body]
102
114
  # api.client = DefinedApiClientClass
115
+ # api.delivery_options = %i[from to subject body]
103
116
  # end
104
117
  #
105
118
  # message = Mail.new(from: 'from@example.com', to: 'to@example.com',
@@ -113,8 +126,8 @@ module MailPlugger
113
126
  # subject: 'Test email', body: 'Test email body')
114
127
  #
115
128
  # MailPlugger::DeliveryMethod.new(
116
- # delivery_options: %i[from to subject body],
117
- # client: DefinedApiClientClass
129
+ # client: DefinedApiClientClass,
130
+ # delivery_options: %i[from to subject body]
118
131
  # ).deliver!(message)
119
132
  #
120
133
  def deliver!(message)
@@ -6,6 +6,14 @@ module MailPlugger
6
6
  # type.
7
7
  class WrongApiClient < Error; end
8
8
 
9
+ # Specific error class for errors if tries to add undeclared option
10
+ # in cofigure block.
11
+ class WrongConfigureOption < Error; end
12
+
13
+ # Specific error class for errors if default delivery opitons has a wrong
14
+ # type.
15
+ class WrongDefaultDeliveryOptions < Error; end
16
+
9
17
  # Specific error class for errors if delivery options are not given or
10
18
  # has a wrong type.
11
19
  class WrongDeliveryOptions < Error; end
@@ -13,6 +13,12 @@ module MailPlugger
13
13
  return_response
14
14
  smtp_settings
15
15
  ].freeze
16
+ SENDING_METHODS = %i[
17
+ default_delivery_system
18
+ plugged_in_first
19
+ random
20
+ round_robin
21
+ ].freeze
16
22
 
17
23
  # Check the version of a gem.
18
24
  #
@@ -69,21 +75,42 @@ module MailPlugger
69
75
  end
70
76
  end
71
77
 
72
- Mail::IndifferentHash.new(data)
78
+ Mail::IndifferentHash.new(default_data.merge(data))
79
+ end
80
+
81
+ # Extract 'default_delivery_options'. If it's a hash, then it'll return the
82
+ # right sending options belongs to the delivery system.
83
+ # If 'default_delivery_options' is nil, it'll return an empty hash. But if
84
+ # the value doesn't a hash, it'll raise an error.
85
+ #
86
+ # @return [Hash] the data which was defined in 'default_delivery_options'
87
+ def default_data
88
+ options = option_value_from(@default_delivery_options)
89
+
90
+ return {} if options.nil?
91
+
92
+ unless options.is_a?(Hash)
93
+ raise Error::WrongDefaultDeliveryOptions,
94
+ '"default_delivery_options" does not a Hash'
95
+ end
96
+
97
+ options.transform_keys(&:to_sym)
73
98
  end
74
99
 
75
- # Tries to set up a default delivery system, if the 'delivery_system'
76
- # wasn't defined in the Mail::Message object, and we're using
77
- # MailPlugger.plug_in then it returns with first key of the stored
78
- # 'delivery_systems'.
79
- # When we aren't using MailPlugger.plug_in method and 'delivery_options',
80
- # 'client' and/or 'delivery_settings' is a hash, then it tries to get the
81
- # 'delivery_system' from the hashes.
82
- # Otherwise, it returns with nil.
100
+ # Tries to set up the 'default_delivery_system'.
83
101
  #
84
- # @return [Stirng/NilClass] the first key from the extracted keys or nil
102
+ # @return [Stirng/NilClass] the name of a delivery system or nil
85
103
  def default_delivery_system_get
86
- extract_keys&.first
104
+ case sending_method_get
105
+ when :default_delivery_system
106
+ @passed_default_delivery_system
107
+ when :plugged_in_first
108
+ extract_keys&.first
109
+ when :random
110
+ extract_keys&.sample
111
+ when :round_robin
112
+ @rotatable_delivery_systems&.next
113
+ end
87
114
  end
88
115
 
89
116
  # Extract 'delivery_options'. If it's a hash, then it'll return the right
@@ -109,7 +136,9 @@ module MailPlugger
109
136
  #
110
137
  # @return [String] with the name of the delivery system
111
138
  def delivery_system
112
- @delivery_system ||=
139
+ return @delivery_system unless @delivery_system.nil?
140
+
141
+ @delivery_system =
113
142
  (@message && message_field_value_from(@message[:delivery_system])) ||
114
143
  @default_delivery_system
115
144
 
@@ -119,7 +148,7 @@ module MailPlugger
119
148
  end
120
149
 
121
150
  # Check the given 'delivery_options', 'client' and 'delivery_settings' are
122
- # hashes and if one of that does then check the 'delivery_system' is valid
151
+ # hashes and if one of that does, then check the 'delivery_system' is valid
123
152
  # or not.
124
153
  # If the given 'delivery_system' is nil or doesn't match with extracted
125
154
  # keys, then it will raise error.
@@ -169,9 +198,7 @@ module MailPlugger
169
198
  #
170
199
  # @return [Array/NilClass] with the keys or nil
171
200
  def extract_keys
172
- return @delivery_systems unless @delivery_systems.nil?
173
-
174
- extract_keys_from_other_variables
201
+ @delivery_systems || extract_keys_from_other_variables
175
202
  end
176
203
 
177
204
  # Extract keys from 'delivery_options', 'client' or 'delivery_settings',
@@ -213,8 +240,8 @@ module MailPlugger
213
240
  message_field.public_send(*mail_field_value)
214
241
  end
215
242
 
216
- # Check if either 'deliviery_options' or 'client' is a hash.
217
- # Or delivery_settings is a hash but not contains 'DELIVERY_SETTINGS_KEYS'
243
+ # Check if either 'deliviery_options' or 'client' is a hash, or
244
+ # 'delivery_settings' is a hash but not contains 'DELIVERY_SETTINGS_KEYS'
218
245
  # in first level.
219
246
  #
220
247
  # @return [Boolean] true/false
@@ -247,23 +274,39 @@ module MailPlugger
247
274
  false
248
275
  end
249
276
 
250
- # Extract 'settings'. If it's a hash, then it'll return the right
251
- # settings, belongs to the delivery system. If 'delivery_settings' is nil,
252
- # it'll return an empty hash. But if the value doesn't a hash, it'll raise
253
- # an error.
277
+ # Choose a 'sending_method' for the given conditions.
278
+ #
279
+ # @return [Symbol] the appropriate sending method
280
+ def sending_method_get
281
+ if @sending_method.nil? && !@passed_default_delivery_system.nil?
282
+ :default_delivery_system
283
+ elsif @sending_method.nil? ||
284
+ !SENDING_METHODS.include?(@sending_method.to_sym) ||
285
+ (@sending_method.to_sym == :default_delivery_system &&
286
+ @passed_default_delivery_system.nil?)
287
+ :plugged_in_first
288
+ else
289
+ @sending_method.to_sym
290
+ end
291
+ end
292
+
293
+ # Extract 'settings'. If 'delivery_settings' is a hash, then it'll return
294
+ # the right settings, belongs to the delivery system. If 'delivery_settings'
295
+ # is nil, it'll return an empty hash. But if the value doesn't a hash, it'll
296
+ # raise an error.
254
297
  #
255
298
  # @return [Hash] settings for Mail delivery_method and/or FakePlugger
256
299
  def settings
257
- @settings ||= option_value_from(@delivery_settings)
300
+ return @settings unless @settings.nil?
258
301
 
259
- return {} if @settings.nil?
302
+ extracted_settings = option_value_from(@delivery_settings) || {}
260
303
 
261
- unless @settings.is_a?(Hash)
304
+ unless extracted_settings.is_a?(Hash)
262
305
  raise Error::WrongDeliverySettings,
263
306
  '"delivery_settings" does not a Hash'
264
307
  end
265
308
 
266
- @settings
309
+ @settings = extracted_settings.transform_keys(&:to_sym)
267
310
  end
268
311
  end
269
312
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MailPlugger
4
- VERSION = '1.6.1'
4
+ VERSION = '1.7.1'
5
5
  end
data/lib/mail_plugger.rb CHANGED
@@ -13,12 +13,38 @@ require 'fake_plugger/railtie' if defined?(Rails)
13
13
 
14
14
  module MailPlugger
15
15
  class << self
16
+ attr_accessor :default_delivery_system,
17
+ :sending_method
18
+
16
19
  attr_reader :client,
20
+ :default_delivery_options,
17
21
  :delivery_options,
18
22
  :delivery_settings,
19
- :delivery_systems
23
+ :delivery_systems,
24
+ :rotatable_delivery_systems
25
+
26
+ # Configure MailPlugger.
27
+ #
28
+ # @example using Rails `config/initializers/mail_plugger.rb`
29
+ #
30
+ # MailPlugger.configure do |config|
31
+ # # It should be defined with the plug_in method.
32
+ # config.default_delivery_system = 'api_client'
33
+ # # It can be:
34
+ # # - default_delivery_system
35
+ # # - plugged_in_first
36
+ # # - random
37
+ # # - round_robin
38
+ # config.sending_method = 'default_delivery_system'
39
+ # end
40
+ #
41
+ def configure
42
+ yield self
43
+ rescue NoMethodError => e
44
+ raise Error::WrongConfigureOption, e.message
45
+ end
20
46
 
21
- # Plug in SMTP(s) or defined API(s) class.
47
+ # Plug in SMTP(s) or defined API(s) class(es).
22
48
  #
23
49
  # @param [String/Symbol] delivery_system the name of the SMTP/API
24
50
  #
@@ -72,6 +98,9 @@ module MailPlugger
72
98
  # type: 'text/html; charset=UTF-8',
73
99
  # value: @options[:html_part]
74
100
  # }
101
+ # ],
102
+ # tags: [
103
+ # @options[:tag]
75
104
  # ]
76
105
  # }
77
106
  # end
@@ -85,11 +114,13 @@ module MailPlugger
85
114
  # end
86
115
  # end
87
116
  #
88
- # MailPlugger.plug_in('defined_api') do |api|
89
- # # It will search these options in the Mail::Message object
117
+ # MailPlugger.plug_in('api_client') do |api|
118
+ # api.client = DefinedApiClientClass
119
+ # # Default delivery options for the plugged in client.
120
+ # api.default_delivery_options = { tag: 'test_tag' }
121
+ # # It will search these options in the Mail::Message object.
90
122
  # api.delivery_options = [:to, :from, :subject, :text_part, :html_part]
91
123
  # api.delivery_settings = { return_response: true }
92
- # api.client = DefinedApiClientClass
93
124
  # end
94
125
  #
95
126
  def plug_in(delivery_system)
@@ -97,16 +128,23 @@ module MailPlugger
97
128
 
98
129
  @delivery_system = delivery_system
99
130
  (@delivery_systems ||= []) << delivery_system
131
+ @rotatable_delivery_systems = @delivery_systems.cycle
100
132
 
101
133
  yield self
102
134
  rescue NoMethodError => e
103
135
  raise Error::WrongPlugInOption, e.message
104
136
  end
105
137
 
106
- # Define 'client', 'delivery_options' and 'delivery_settings' setter
107
- # methods. These methods are generating a hash, where the key is the
108
- # 'delivery_system'. This let us set/use more than one STMP/API.
109
- %w[client delivery_options delivery_settings].each do |method|
138
+ # Define 'client', 'default_delivery_options', 'delivery_options' and
139
+ # 'delivery_settings' setter methods. These methods are generating a hash,
140
+ # where the key is the 'delivery_system'. This let us set/use more than one
141
+ # STMP/API.
142
+ %w[
143
+ client
144
+ default_delivery_options
145
+ delivery_options
146
+ delivery_settings
147
+ ].each do |method|
110
148
  define_method "#{method}=" do |value|
111
149
  variable = instance_variable_get("@#{method}")
112
150
  variable = instance_variable_set("@#{method}", {}) if variable.nil?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_plugger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Norbert Szivós
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-27 00:00:00.000000000 Z
11
+ date: 2023-01-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mail
@@ -38,8 +38,8 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.3'
41
- description: Delivery Method to send emails via SMTP(s) and API(s). We can use this
42
- Delivery Method with Ruby on Rails ActionMailer or other solutions.
41
+ description: MailPlugger helps you to use one or more mail providers. You can send
42
+ emails via SMTP and API as well.
43
43
  email:
44
44
  - sysqa@yahoo.com
45
45
  executables: []
@@ -84,8 +84,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
84
  - !ruby/object:Gem::Version
85
85
  version: '0'
86
86
  requirements: []
87
- rubygems_version: 3.1.4
87
+ rubygems_version: 3.1.6
88
88
  signing_key:
89
89
  specification_version: 4
90
- summary: Plug in required mailer SMTP(s) and API(s) with MailPlugger.
90
+ summary: Plug in required mailer(s) with MailPlugger.
91
91
  test_files: []