From: Brian Hempel Date: 2014-12-04T11:35:35-05:00 Subject: [ruby-core:66693] Re: [ruby-trunk - Feature #10481] Add "if" and "unless" clauses to rescue statements --Apple-Mail=_16DB0168-2E29-4791-AD6D-4E19862180C2 Content-Type: multipart/alternative; boundary="Apple-Mail=_7796332A-D96E-47EE-B4BE-A8EFC954DD12" --Apple-Mail=_7796332A-D96E-47EE-B4BE-A8EFC954DD12 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=windows-1252 You=92re right there may be some risk for anti-patterns, but from my = experience this example in the original proposal is practical: begin ... rescue =3D> e if e.message =3D=3D "some error" ...handle error... end Sometime code libraries forget to make a separate object for every error = and you are forced to match on the error message. Right now, you have to = write code like: begin =85 rescue =3D> e raise unless e.message =3D~ /card declined/i =85handle error=85 end Granted, the best solution is to fix the code library. Brian Hempel On Dec 4, 2014, at 11:25 AM, bruka wrote: > I'm trying to think of use cases for this, but every scenario I think = of, leads towards anti-patterns and bad programming practices. Here is = my 2 (3) cents: >=20 > Anti-pattern #1: Using rescue clauses as conditionals to direct = program flow. >=20 > I use the 'raise if my_obj.not_as_expected?' style as well, but I = always make sure that those statements are trivially simple, that they = appear in obvious places, and that there is at most 1 rescue clause. I = think even this style is an anti-pattern, but I tell myself that if the = code is simple enough that it's ok. >=20 > Anti-pattern #2: Too many rescue clauses. >=20 > Things like: > rescue SomeError =3D> e if e.error_code =3D=3D 1 > ...handle error code 1... > rescue SomeError =3D> e if e.error_code =3D=3D 2 > ...handle error code 2... > end >=20 > is a code smell to me. Indicative that the method is doing too many = things and that perhaps has too much fan-out. Might be a violation of = the Law of Demeter, where the method knows the types of objects it is = working with, the types of errors they throw, and the internal structure = of those error objects. >=20 > Anti-pattern #3: Programming flow is more difficult to follow. >=20 > It reminds of conditionals at the end of loops e.g. >=20 > my_arr.each do |ele| > ...10 lines of code... > end if some_condition >=20 > You read the entire loop and at the end you realize that it only runs = in certain cases. Similar with the conditional rescues. You're jumping = back and forth in your method to try to figure out what happens when. = Not fun. >=20 > Moreover, as another person noted, skipping the rescue clause can = deprive you of valuable debugging information. Why not just send the = error to another object to be processed: >=20 > rescue StandardError =3D> ex > MyErrorProcessor.handle( ex, self ) > end >=20 > These are just opinions on software architecture and I have nothing to = say about the technical implementation of this. I may just be = overthinking it, but again I think you need to show some good real-world = use cases. >=20 > On Thu, Dec 4, 2014 at 3:24 AM, wrote: > Issue #10481 has been updated by Alex Boyd. >=20 >=20 > Any update on this? Is this just waiting for 2.2 to make it out the = door? >=20 > ---------------------------------------- > Feature #10481: Add "if" and "unless" clauses to rescue statements > https://bugs.ruby-lang.org/issues/10481#change-50283 >=20 > * Author: Alex Boyd > * Status: Assigned > * Priority: Normal > * Assignee: Yukihiro Matsumoto > * Category: > * Target version: > ---------------------------------------- > I'd like to propose a syntax change: allow boolean "if" and "unless" = clauses to follow a rescue statement. >=20 > Consider the following: >=20 > ~~~ruby > begin > ... > rescue SomeError =3D> e > if e.error_code =3D=3D 1 > ...handle error... > else > raise > end > end > ~~~ >=20 > This is a fairly common way of dealing with exceptions where some = condition above and beyond the exception's type determines whether the = exception should be rescued. It's verbose, though, and it's not obvious = at first glance exactly what conditions are being rescued, especially if = "...handle error..." is more than a few lines long. I propose that the = following be allowed: >=20 > ~~~ruby > begin > ... > rescue SomeError =3D> e if e.error_code =3D=3D 1 > ...handle error... > end > ~~~ >=20 > "unless" would, of course, be allowed as well: >=20 > ~~~ruby > begin > ... > rescue SomeError =3D> e unless e.error_code =3D=3D 2 > ...handle error... > end > ~~~ >=20 > A rescue statement whose boolean condition failed would be treated the = same as if the exception being raised didn't match the exception being = rescued, and move on to the next rescue statement: >=20 > ~~~ruby > begin > ... > rescue SomeError =3D> e if e.error_code =3D=3D 1 > ...handle error code 1... > rescue SomeError =3D> e if e.error_code =3D=3D 2 > ...handle error code 2... > end > ~~~ >=20 > And finally, catch-all rescue statements would be allowed as well: >=20 > ~~~ruby > begin > ... > rescue =3D> e if e.message =3D=3D "some error" > ...handle error... > end > ~~~ >=20 >=20 > ---Files-------------------------------- > rescue-conditions.diff (6.76 KB) > rescue-conditions.diff (6.57 KB) >=20 >=20 > -- > https://bugs.ruby-lang.org/ >=20 --Apple-Mail=_7796332A-D96E-47EE-B4BE-A8EFC954DD12 Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset=windows-1252 You=92re= right there may be some risk for anti-patterns, but from my experience = this example in the original proposal is = practical:

begin
  ...
rescue =3D> e if = e.message =3D=3D "some error"
  ...handle = error...
end

Sometime code = libraries forget to make a separate object for every error and you are = forced to match on the error message. Right now, you have to write code = like:

begin
=85
rescue =3D> e
  raise unless e.message =3D~ /card = declined/i
  =85handle = error=85
end

Granted, the = best solution is to fix the code library.

Brian Hempel


On Dec 4, 2014, at 11:25 AM, bruka <bruka@idatainc.com> = wrote:

I'm trying to think of use cases for = this, but every scenario I think of, leads towards anti-patterns and bad = programming practices. Here is my 2 = (3) cents:

Anti-pattern #1: Using rescue = clauses as conditionals to direct program = flow.

I use the 'raise if = my_obj.not_as_expected?' style as well, but I always make sure that = those statements are trivially simple, that they appear in obvious = places, and that there is at most 1 rescue clause. I think even = this style is an anti-pattern, but I tell myself that if the code is = simple enough that it's ok.

Anti-pattern #2: = Too many rescue clauses.

Things = like:
rescue SomeError =3D> e if e.error_code =3D=3D 1
=   ...handle error code 1...
rescue SomeError =3D> e if = e.error_code =3D=3D 2
  ...handle error code 2...
= end

is a code smell to me. Indicative that = the method is doing too many things and that perhaps has too much = fan-out. Might be a  violation of the Law of Demeter, where the = method knows the types of objects it is working with, the types of = errors they throw, and the internal structure of those error = objects.

Anti-pattern #3: Programming flow is = more difficult to follow.

It reminds of = conditionals at the end of loops = e.g.

my_arr.each do |ele|
...10 lines = of code...
end if some_condition

You = read the entire loop and at the end you realize that it only runs in = certain cases. Similar with the conditional rescues. You're jumping back = and forth in your method to try to figure out what happens when. Not = fun.

Moreover, as another person noted, = skipping the rescue clause can deprive you of valuable debugging = information. Why not just send the error to another object to be = processed:

rescue StandardError =3D> = ex
      MyErrorProcessor.handle( ex, = self )
end

These are just opinions on = software architecture and I have nothing to say about the technical = implementation of this. I may just be overthinking it, but again I think = you need to show some good real-world use cases.

On Thu, Dec 4, 2014 = at 3:24 AM, <alex@opengroove.org> = wrote:
Issue #10481 has been updated by Alex Boyd.


Any update on this? Is this just waiting for 2.2 to make it out the = door?

----------------------------------------
Feature #10481: Add "if" and "unless" clauses to rescue statements
https://bugs.ruby-lang.org/issues/10481#change-50283=

* Author: Alex Boyd
* Status: Assigned
* Priority: Normal
* Assignee: Yukihiro Matsumoto
* Category:
* Target version:
----------------------------------------
I'd like to propose a syntax change: allow boolean "if" and "unless" = clauses to follow a rescue statement.

Consider the following:

~~~ruby
begin
  ...
rescue SomeError =3D> e
  if e.error_code =3D=3D 1
    ...handle error...
  else
    raise
  end
end
~~~

This is a fairly common way of dealing with exceptions where some = condition above and beyond the exception's type determines whether the = exception should be rescued. It's verbose, though, and it's not obvious = at first glance exactly what conditions are being rescued, especially if = "...handle error..." is more than a few lines long. I propose that the = following be allowed:

~~~ruby
begin
  ...
rescue SomeError =3D> e if e.error_code =3D=3D 1
  ...handle error...
end
~~~

"unless" would, of course, be allowed as well:

~~~ruby
begin
  ...
rescue SomeError =3D> e unless e.error_code =3D=3D 2
  ...handle error...
end
~~~

A rescue statement whose boolean condition failed would be treated the = same as if the exception being raised didn't match the exception being = rescued, and move on to the next rescue statement:

~~~ruby
begin
  ...
rescue SomeError =3D> e if e.error_code =3D=3D 1
  ...handle error code 1...
rescue SomeError =3D> e if e.error_code =3D=3D 2
  ...handle error code 2...
end
~~~

And finally, catch-all rescue statements would be allowed as well:

~~~ruby
begin
  ...
rescue =3D> e if e.message =3D=3D "some error"
  ...handle error...
end
~~~


---Files--------------------------------
rescue-conditions.diff (6.76 KB)
rescue-conditions.diff (6.57 KB)


--
https://bugs.ruby-lang.org/


= --Apple-Mail=_7796332A-D96E-47EE-B4BE-A8EFC954DD12-- --Apple-Mail=_16DB0168-2E29-4791-AD6D-4E19862180C2 Content-Disposition: attachment; filename=smime.p7s Content-Type: application/pkcs7-signature; name=smime.p7s Content-Transfer-Encoding: base64 MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIIPNTCCBN0w ggPFoAMCAQICEHGS++YZX6xNEoV0cTSiGKcwDQYJKoZIhvcNAQEFBQAwezELMAkGA1UEBhMCR0Ix GzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwR Q29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMMGEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0w NDAxMDEwMDAwMDBaFw0yODEyMzEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQx FzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsx ITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVROLVVTRVJGaXJz dC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3BYHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIx B8dOtINknS4p1aJkxIW9hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8 om+rWV6lL8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLmSGHG TPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM1tZUOt4KpLoDd7Nl yP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws6wIDAQABo4IBJzCCASMwHwYDVR0j BBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYDVR0OBBYEFImCZ33EnSZwAEu0UEh83j2uBG59 MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdJQQWMBQGCCsGAQUFBwMCBggr BgEFBQcDBDARBgNVHSAECjAIMAYGBFUdIAAwewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5j b21vZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwu Y29tb2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDARBglghkgBhvhCAQEEBAMCAQYw DQYJKoZIhvcNAQEFBQADggEBAJ2Vyzy4fqUJxB6/C8LHdo45PJTGEKpPDMngq4RdiVTgZTvzbRx8 NywlVF+WIfw3hJGdFdwUT4HPVB1rbEVgxy35l1FM+WbKPKCCjKbI8OLp1Er57D9Wyd12jMOCAU9s APMeGmF0BEcDqcZAV5G8ZSLFJ2dPV9tkWtmNH7qGL/QGrpxp7en0zykX2OBKnxogL5dMUbtGB8SK N04g4wkxaMeexIud6H4RvDJoEJYRmETYKlFgTYjrdDrfQwYyyDlWjDoRUtNBpEMD9O3vMyfbOeAU TibJ2PU54om4k123KSZB6rObroP8d3XK6Mq1/uJlSmM+RMTQw16Hc6mYHK9/FX8wggUaMIIEAqAD AgECAhBtGeqnGU9qMyLmIjJ6qnHeMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYDVQQGEwJVUzELMAkG A1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNU IE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UEAxMtVVRO LVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWlsMB4XDTExMDQyODAwMDAw MFoXDTIwMDUzMDEwNDgzOFowgZMxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNo ZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTkwNwYD VQQDEzBDT01PRE8gQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBTZWN1cmUgRW1haWwgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCShIRbS1eY1F4vi6ThQMijU1hfZmXxMk73nzJ9 VdB4TFW3QpTg+SdxB8XGaaS5MsTxQBqQzCdWYn8XtXFpruUgG+TLY15gyqJB9mrho/+43x9IbWVD jCouK2M4d9+xF6zC2oIC1tQyatRnbyATj1w1+uVUgK/YcQodNwoCUFNslR2pEBS0mZVZEjH/CaLS TNxS297iQAFbSGjdxUq04O0kHzqvcV8H46y/FDuwJXFoPfQP1hdYRhWBPGiLi4MPbXohV+Y0sNsy fuNK4aVScmQmkU6lkg//4LFg/RpvaFGZY40ai6XMQpubfSJj06mg/M6ekN9EGfRcWzW6FvOnm//B AgMBAAGjggFLMIIBRzAfBgNVHSMEGDAWgBSJgmd9xJ0mcABLtFBIfN49rgRufTAdBgNVHQ4EFgQU ehNOAHRbxnhjZCfBL+KgW7x5xXswDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAw EQYDVR0gBAowCDAGBgRVHSAAMFgGA1UdHwRRME8wTaBLoEmGR2h0dHA6Ly9jcmwudXNlcnRydXN0 LmNvbS9VVE4tVVNFUkZpcnN0LUNsaWVudEF1dGhlbnRpY2F0aW9uYW5kRW1haWwuY3JsMHQGCCsG AQUFBwEBBGgwZjA9BggrBgEFBQcwAoYxaHR0cDovL2NydC51c2VydHJ1c3QuY29tL1VUTkFkZFRy dXN0Q2xpZW50X0NBLmNydDAlBggrBgEFBQcwAYYZaHR0cDovL29jc3AudXNlcnRydXN0LmNvbTAN BgkqhkiG9w0BAQUFAAOCAQEAhda+eFdVbTN/RFL+QtUGqAEDgIr7DbL9Sr/2r0FJ9RtaxdKtG3Nu PukmfOZMmMEwKN/L+0I8oSU+CnXW0D05hmbRoZu1TZtvryhsHa/l6nRaqNqxwPF1ei+eupN5yv7i kR5WdLL4jdPgQ3Ib7Y/9YDkgR/uLrzplSDyYPaUlv73vYOBJ5RbI6z9Dg/Dg7g3B080zX5vQvWBq szv++tTJOjwf7Zv/m0kzvkIpOYPuM2kugp1FTahp2oAbHj3SGl18R5mlmwhtEpmG1l1XBxunML5L SUS4kH7K0Xk467Qz+qA6XSZYnmFVGLQh1ZnV4ENAQjC+6qXnlNKw/vN1+X9u5zCCBTIwggQaoAMC AQICEQDSJaSZ/99CQ/y/ApzAvSEgMA0GCSqGSIb3DQEBBQUAMIGTMQswCQYDVQQGEwJHQjEbMBkG A1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P RE8gQ0EgTGltaXRlZDE5MDcGA1UEAxMwQ09NT0RPIENsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQg U2VjdXJlIEVtYWlsIENBMB4XDTE0MDczMTAwMDAwMFoXDTE1MDczMTIzNTk1OVowKTEnMCUGCSqG SIb3DQEJARYYcGxhc3RpY2NoaWNrZW5AZ21haWwuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEA0dzax3nYF+i70dL0gfdfJl8ZgL38lK60pECUINRDXTvL2rOVdYLFH1A5k+9QvYYZ iPGEfkfcAIR6fKVQ08w0xatPer0CEKGlj6Efak5TaXzati0p5MCLh+dJVp9wcRRgvER+D+YziTHz 04FBcoh33awQEX4fSIxG3PxcGPQ70aWaO4NbSq2w8bbGRj3cuESFuhWmU6VL/d/wB2lDs/VQOtD4 qGOypAsPODoPGNFr7LLYVLRtQ0LeyPsoTFAeLKXTSXaVGpolzw03otj400oi9kPn9fZZcbsfT4re kpvi7FYxXY4FMez0imNGbg6jX38liThTlQMcUB7odPZhunuCEQIDAQABo4IB6DCCAeQwHwYDVR0j BBgwFoAUehNOAHRbxnhjZCfBL+KgW7x5xXswHQYDVR0OBBYEFP0/xec3FM8rombf6m5Opgmv2JLR MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMCAGA1UdJQQZMBcGCCsGAQUFBwMEBgsrBgEE AbIxAQMFAjARBglghkgBhvhCAQEEBAMCBSAwRgYDVR0gBD8wPTA7BgwrBgEEAbIxAQIBAQEwKzAp BggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLm5ldC9DUFMwVwYDVR0fBFAwTjBMoEqg SIZGaHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPQ2xpZW50QXV0aGVudGljYXRpb25hbmRT ZWN1cmVFbWFpbENBLmNybDCBiAYIKwYBBQUHAQEEfDB6MFIGCCsGAQUFBzAChkZodHRwOi8vY3J0 LmNvbW9kb2NhLmNvbS9DT01PRE9DbGllbnRBdXRoZW50aWNhdGlvbmFuZFNlY3VyZUVtYWlsQ0Eu Y3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wIwYDVR0RBBwwGoEYcGxh c3RpY2NoaWNrZW5AZ21haWwuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQA3jrCaafepHidz9uzXAa/Z 1e6w6vAijH8vi66nYfvaRS5EkFERAQYEzX6Zhz/TWDBYstiCTAhcvVDAj3ZIglJ7dhZNeLlVTJ/7 CuEJ0qCmfwy2vRAKm9GGwiXsWG4OXgkhX1mz/yvg7qs7w6j7nZzQb65E5NTdN1lSfPdUaoSbD8MW eeJsHTFTPRaKyyJutuyGBeligeIyv5gZeM8o/Fy4Ok/iSRziGaN5NoEGHH1Mw/S25HHI+KMyrZ1K kxVZJ740qbnbjmjBEtQMBnTqtganSMouMV4P7kslL4EZzV5luKZyIs5mKxVoprNC0W6WhxXY9ixv h5eZ6Hgo0xMSazZVMYIDrjCCA6oCAQEwgakwgZMxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVh dGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1p dGVkMTkwNwYDVQQDEzBDT01PRE8gQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBTZWN1cmUgRW1h aWwgQ0ECEQDSJaSZ/99CQ/y/ApzAvSEgMAkGBSsOAwIaBQCgggHZMBgGCSqGSIb3DQEJAzELBgkq hkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE0MTIwNDE2MzUzNVowIwYJKoZIhvcNAQkEMRYEFInZ QJMHMa/vrYjx1V30T/ZZVPjiMIG6BgkrBgEEAYI3EAQxgawwgakwgZMxCzAJBgNVBAYTAkdCMRsw GQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNP TU9ETyBDQSBMaW1pdGVkMTkwNwYDVQQDEzBDT01PRE8gQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFu ZCBTZWN1cmUgRW1haWwgQ0ECEQDSJaSZ/99CQ/y/ApzAvSEgMIG8BgsqhkiG9w0BCRACCzGBrKCB qTCBkzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMH U2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxOTA3BgNVBAMTMENPTU9ETyBDbGll bnQgQXV0aGVudGljYXRpb24gYW5kIFNlY3VyZSBFbWFpbCBDQQIRANIlpJn/30JD/L8CnMC9ISAw DQYJKoZIhvcNAQEBBQAEggEAPamtdJXD/ytbzd9kl+GftAI72gp1CayM5RDv6OwmRUsPItc5zLKl vlKgpjBFiGBsMVvM5V4SDRH0oZTr6oUILxrDEk5nkYF8co93nUulZThmg1q3xgr0y8TxeD4i1k3g LWYsn25d4Q0zK1tvM1pENhRejOmB6Y2o+LXQ2GMaFU9XPGsdrhoRJ6OW1Gd+oFPwDjCZ1KLbBvSz schN9XeyXO3GRXkNbbN3PkjDUG/ISwEveQ+EtIZbwUtdQhHJexO7uSeYkJUnLyiTVALP65kUHOUE O34YK1OcbSBg4AFilXA9XEsuenjlmVFIcraj1eM5PFhLEf4kXZZLBGdLgqHt5gAAAAAAAA== --Apple-Mail=_16DB0168-2E29-4791-AD6D-4E19862180C2--