All language subtitles for pragstudio-ruby-blocks-09-manage-resources 2 (Transcribed on 27-Apr-2023 21-02-59)

af Afrikaans
ak Akan
sq Albanian
am Amharic
ar Arabic
hy Armenian
az Azerbaijani
eu Basque
be Belarusian
bem Bemba
bn Bengali
bh Bihari
bs Bosnian
br Breton
bg Bulgarian
km Cambodian
ca Catalan
ceb Cebuano
chr Cherokee
ny Chichewa
zh-CN Chinese (Simplified) Download
zh-TW Chinese (Traditional)
co Corsican
hr Croatian
cs Czech
da Danish
nl Dutch
en English
eo Esperanto
et Estonian
ee Ewe
fo Faroese
tl Filipino
fi Finnish
fr French
fy Frisian
gaa Ga
gl Galician
ka Georgian
de German
el Greek
gn Guarani
gu Gujarati
ht Haitian Creole
ha Hausa
haw Hawaiian
iw Hebrew
hi Hindi
hmn Hmong
hu Hungarian
is Icelandic
ig Igbo
id Indonesian
ia Interlingua
ga Irish
it Italian
ja Japanese
jw Javanese
kn Kannada
kk Kazakh
rw Kinyarwanda
rn Kirundi
kg Kongo
ko Korean
kri Krio (Sierra Leone)
ku Kurdish
ckb Kurdish (Soranî)
ky Kyrgyz
lo Laothian
la Latin
lv Latvian
ln Lingala
lt Lithuanian
loz Lozi
lg Luganda
ach Luo
lb Luxembourgish
mk Macedonian
mg Malagasy
ms Malay
ml Malayalam
mt Maltese
mi Maori
mr Marathi
mfe Mauritian Creole
mo Moldavian
mn Mongolian
my Myanmar (Burmese)
sr-ME Montenegrin
ne Nepali
pcm Nigerian Pidgin
nso Northern Sotho
no Norwegian
nn Norwegian (Nynorsk)
oc Occitan
or Oriya
om Oromo
ps Pashto
fa Persian
pl Polish
pt-BR Portuguese (Brazil)
pt Portuguese (Portugal)
pa Punjabi
qu Quechua
ro Romanian
rm Romansh
nyn Runyakitara
ru Russian
sm Samoan
gd Scots Gaelic
sr Serbian
sh Serbo-Croatian
st Sesotho
tn Setswana
crs Seychellois Creole
sn Shona
sd Sindhi
si Sinhalese
sk Slovak
sl Slovenian
so Somali
es Spanish
es-419 Spanish (Latin American)
su Sundanese
sw Swahili
sv Swedish
tg Tajik
ta Tamil
tt Tatar
te Telugu
th Thai
ti Tigrinya
to Tonga
lua Tshiluba
tum Tumbuka
tr Turkish
tk Turkmen
tw Twi
ug Uighur
uk Ukrainian
ur Urdu
uz Uzbek
vi Vietnamese
cy Welsh
wo Wolof
xh Xhosa
yi Yiddish
yo Yoruba
zu Zulu

Original subtitles

So blocks come in really handy when you need to control and resource.

Maybe it's a network connection, maybe it's a file.

Yeah, you need to connect and disconnect and open and close the resource and guarantee that

everything is cleaned up properly at the end.

For example, let's suppose we have a simple payment gateway class and imagine it talks

to a remote web service.

It needs to be initialized with a super secret username and password.

We have a way to connect and disconnect from the remote service.

We also have a way to submit transactions by specifying the type of transaction, maybe

a sale or a refund and also send the amount.

So here's the way we typically use it.

We create a new gateway object.

We connect to it.

We send some transactions and then we have to remember to disconnect when we're done.

So let's see that that works.

All right, we run it.

We connect it as Nicole.

We submitted two sales and transactions and then we disconnect the due.

Now code like this tends to sprout up in other areas of our app.

For example, imagine somewhere else we handle refunds.

So we basically have to do the same thing.

So let's see that that works.

Yep, we submitted your sale transactions and then we turned around, reconnected you,

sent to refunds and disconnected at the end.

Now obviously this is an ideal.

We have the need to connect and disconnect around a whole series of transactions.

Yeah, instead we could use a block and get some automatic resource management.

So we're going to define a method that takes a block and then execute around the transactions

in the block.

So I'm going to define the method right up here.

It's going to be called, let's start with a method outside the class.

We're going to call it open gateway.

It's going to need to take the user in the password.

All right, what are we doing inside that method?

Well, the very first thing we need to do is actually create a new payment gateway.

And instead of using these hard-coded values, we'll use user and password here.

So we'll just forward those onto the gateway.

Then we need to do gateway connect.

Then we want to yield to the block.

Let it do the transactions.

And then when we're done, we need to make sure that we actually disconnect from the gateway.

Now the block itself is going to need the gateway object so we can run the transactions.

So when we call yield, we're going to have to pass in that gateway object.

Now down here, we're submitting the transactions.

We'll call our open gateway method.

We just wrote here.

This will be Nicole and Secret.

The block parameter is the gateway itself.

Inside the block, we can then use that gateway object.

And we don't have to worry about disconnecting it because when the block ends, the yield is returned.

Remember, we're just going to disconnect right there.

So we've got all this encapsulated in one nice little method.

So we save this and run it.

What we get the same output before.

We saw we connected as Nicole.

Then we submitted a sale transaction.

Actually, two sale transactions.

And the block disconnected us at the very end.

So we've got some automatic resource management going on inside of this method, executing

around this block.

So now that we've got it working, let's make a little design improvement here.

Well let's say we want to move this method inside of our payment gateway class.

We're just going to move, we're inside of the class right here.

Right?

And right now it's an instance method.

We would have to have a payment gateway object in order to call this method.

We're not going to have a payment gateway object because we don't create one until the

method is actually run.

So we're going to change this to be a class method by using self dot.

And then we're going to change the name of the method because it's pretty clear if

it's a class method on the payment gateway that we don't need to call it open gateway.

We would just call it open because we know what we're opening there.

One small refinement here is if you're inside of a class method and you want to get

to hold the class, weld the self variable points to the payment gateway class here.

So we can change payment gateway class to just self right there.

Now to call that method down here instead of using open gateway, we call payment gateway.

That's the name of the class.

It defines a method called open.

It's a class level method.

Just like that.

And that should run as well.

I'm going to go ahead and change this one for doing refunds.

So we look like this.

Payment gateway.

We need to take the block parameter which is a gateway like that.

We don't need to connect.

That's already handled in the open method.

We're going to submit our transactions and we don't need to disconnect because that's

automatically handled for us.

Save those off.

And we get the same result we got before.

We submit our self transactions and our refund transactions.

But the really nice thing is this code is a lot more compact and expressive.

We don't have to worry about connecting and disconnecting in the right order.

The method called open handles all that for us.

So what if we want to create a connected gateway but not using associated block?

Like let's say we want to do a special scenario.

We want to void a transaction.

We would still need to open the gateway.

Okay, well let's take this open method here.

I'm just going to copy that so it's a bit easier.

And we'd like to just assign it to a variable.

We'd have the open method return to us a gateway object.

But then we're going to manage it from here.

So we would then use gateway and we would submit.

You said it was a void transaction, maybe for $15.

And then because we don't have a block here, we're going to go ahead and disconnect it.

So we're responsible for disconnecting it because we're not passing an associated block

to that open method.

But that's okay.

We want to take some more fine grain control when we do this.

So if we try to run this, well we get this local jump error.

We saw a little bit earlier in the course because we haven't associated a block and we're

calling yield up here, we get this error.

Now it's up to you.

It's a design decision whether you want to require a block or you want somebody to be

able to pass a block or not pass a block.

In our case, we want to support both scenarios.

All right.

And you might think the way to handle this would be to call if block is given just as we

have done before in other scenarios.

But that's not going to work in this scenario because let's look at what happens.

When we call the open method right here without a block, so it's going to create a new

gateway object, it's then going to connect to it.

It's then going to get the this line is going to say, well, a block wasn't given, so

I'm not going to yield to the block.

But it is going to try to disconnect the block.

So what's going to happen here is we're not going to get an object back because no object

was ever passed back to us.

The method tries to do everything for us inside of that method.

So what do we want to do instead?

Well, we need to get back a gateway object that's connected has been disconnected and we can

do whatever we want with it.

So here's a little trick.

Right here after we've connected, we're going to return that gateway object.

We're only going to return the object if a block hasn't been given.

So unless block is given, return the object.

Otherwise, go ahead and yield to it. We don't need the if block given right there.

So if a block isn't supplied, just go ahead and return the gateway and these lines of code

won't be run.

If a block is supplied, do the normal thing, yield to it and disconnect when you're

all done.

So if we save this, now we can use it in multiple scenarios.

We see that we can submit our void transaction, our sales, and our refunds.

We can call the open method with or without a block.

So using block given gives the method two different behaviors.

When it's called with a block, it executes the block and disconnects.

When it's called without a block, it simply returns the gateway object.

Again, it's a design decision whether you want to allow your methods to take blocks or

optionally not take blocks.

But wait, what happens if our block encounters an exception while it's issuing refunds?

Well, we know how to handle that.

We do.

So let's simulate a problem here inside of this refund block.

We're going to raise an exception.

This time, I'm just going to say, oh, problem, exclamation point.

Right?

If we run that, well, our exception is not handled.

We don't want to rescue you clause or anything.

So it propagates all the way up to the top.

But let's look at what happened in the printouts here.

We submitted a refund for $5 and $20, but notice after the refund, our connection wasn't

disconnected.

We don't see a disconnect after this line right here.

Now, and that's because we raised an exception inside of the block.

If we go look inside of our open method, it's not handling exceptions at all.

So when we yield to the block, the exception is going to stop the program from executing

right at this point.

So we're never going to get to this line of code which disconnects the gateway.

So we need to guarantee or ensure that the gateway is disconnected even if the block raises

an exception.

And we know how to do that using our old friend the insure clause.

So we're just going to take that chunk of code and put it inside of it and sure like that.

So if an exception is raised here, we know that that line of code will always be executed.

If we run it now, notice we get the line after the refunds disconnected in a call.

So the disconnect happened.

But there's a small problem here.

Imagine up in this code where we're creating a new gateway with new.

Imagine somewhere in that code, it raised an exception.

Well if it raises an exception up here in this line of code, ensure we'll still be called,

it will try to disconnect the gateway.

But if it raises an exception up here, then it will never be connected in the first place

and we'll try to disconnect a gateway that hasn't been connected in the first place.

So we need to be a little bit more selective about which lines of code the insure clause

applies to.

And we can do that using begin.

We only want to ensure to be applied to this line of code when we're yielding to the

block.

So we can say begin.

All right.

That's yielding to the block and we want to ensure that that happens after that block is run.

So we've got a begin, some statement, and then ensure only gets applied to that section

of code.

And we know by this point our gateway is already connected so we're good to go.

Now as things stand, there's no rescue clause.

So any exception is still going to get propagated all the way to the top.

And we could optionally add a rescue clause here as we did before rescue exception,

e, and we could print out the message associated with that.

And again, this rescue clause only applies to that line of code.

So the takeaway here is we've got resource management and exception handling encapsulated

in this one spot in this one open method.

And that makes it really easy to add or remove stuff being executed around.

This is a really common pattern for managing resources.

So let's look at one more example of this that's built into Ruby.

Yeah, you'll often want to open up a file, do something with its contents, and then ensure

that the file is closed when you're all done with the file.

And Ruby's got you covered.

Okay, and Ruby, you can write and read a file like this.

You can call the open method on the file class, pass in a file name, and a mode.

In this case, we're writing to the file.

We get back an open file object, in this case, it's in the variable F.

And then we can just use puts on that F object that print out some strings to that file,

and at the end, we need to make sure to close it.

When you turn around and want to read the file, you open the file with the file name,

and the mode is now set to R, which is reading, you get back an open file object again.

We can iterate through the file.

This is an example of each on a file object, gives you each line, which is pretty cool.

And then you've got to remember to close the file when you're done.

So if we run this, it wrote to a file, and then it read it back in again,

and it just prints out, hello, and goodbye, which are the contents of that file.

So this works, but it has the same problem that we ran into with the gateway.

We have to remember to close the file when we're done.

But conveniently, the open method is designed to optionally take a block,

and using it with a block makes it a lot less prone to error.

So the way to do that is instead of getting back a file object like that,

we can associate a block with the open method, and we're going to get a block parameter.

It's going to be the file object itself.

Then inside of the block, we can use that F object, print out our strings,

and we don't have to remember to close it, because that's going to be handled by the open method for us.

So we just end the block.

In the same way when we're reading, we can give it a block parameter, and a block there,

inside of the block will read those things, and we don't have to close it at the end.

So the responsibility for closing an open file has shifted from you, the coder,

to the file objects themselves, which is really nice.

So just for fun, how might we implement a file that open in its simplest form?

Well, let's go ahead and change the occurrences of open here to my open.

We're going to write our own version of this.

We know these are defined inside of the file class, so let's go ahead and reopen Ruby's file class.

The method is a class method we're calling on the class is going to be self-dot,

and this can be called my open.

All right, it takes some parameters.

The first parameter is the file name, and the second parameter is the mode.

All right, what's the first thing we need to do inside of here?

Well, we need to create a new file that's open.

Why we do that is we call file.new, pass in the file name, or just go ahead and

delegate that down to that method.

File.new actually opens the file and returns a new file handle or file object.

Oh, and as a small little refinement here, you can use self because inside of the

class method, self is going to refer to the file class itself in this case.

Last thing we need to do, we know, is to close the file in between those two things.

We need to yield to the block and give the block the file as a block parameter.

Okay, that's a pretty good start, but what happens if a block isn't given to this my open method?

Well, what we want to do there is just simply return the file objects.

Say, return the file unless a block is given.

That way we can use file.open or file.myopen, just as we did when we started out by getting back a

file object, doing what we want with it, and then taking care of closing it ourselves.

Of course, we also need to handle exceptions.

In particular, we need to make sure that the file is always closed, whether the block

raises it and exception or not, and we learned how to do that earlier.

We want to scope it to just this line of code so we're going to say begin.

Yield to the block and then ensure that the file object is closed at the very end there.

Now, in this particular case, there's no rescue clause, so any exceptions are going to

get propagated to the code that calls open, which is in a better position to handle it anyway.

All right, if we save that, but we run it, we get the same result.

So we basically re-implemented the open method using the techniques we've learned throughout this course.

Pretty cool.

So these examples pull together a lot of things we learned throughout this course.

Yeah, and hopefully you recognize some recurring patterns in all these examples.

And in the exercise that follows, you'll find some more examples to practice resource management with blocks.

Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.