Avdi Grimm - Confident - Free ebook download as PDF File .pdf), Text File .txt) or read book online for free. Avdi Grimm - Confident - Ebook download as PDF File .pdf), Text File . txt) or read book online. Confident Ruby. by Avdi Confident Ruby is, first and foremost, a book about joy . Download a PDF sample, containing the introduction and three patterns.

Confident Ruby Pdf

Language:English, Indonesian, Dutch
Country:Czech Republic
Genre:Health & Fitness
Published (Last):04.09.2016
ePub File Size:18.60 MB
PDF File Size:17.75 MB
Distribution:Free* [*Registration Required]
Uploaded by: KENNA

But my programs grew, and as they grew, they started to change. The real world poked its ugly head in, in the form of failure modes and edge cases. Little by. Confident Ruby. by Avdi Grimm. Are your Ruby methods timid? Do they constantly second-guess themselves, checking for nil values, errors. 32 Patterns for Joyful CodingFor many programmers, discovering Ruby is a Главная | Компьютерная литература | Avdi Grimm Confident Ruby ().pdf.

Given an even-numbered list of arguments. Kernel Array takes an argument and tries very hard indeed to convert that argument into an Array.

Rationale Array ensures that no matter what form the input data arrives in. Accepting one or many arguments We mentioned the Kernel Array method in the preceding section about conversion functions. Array "foo" Array [1. Use the Array conversion function to array-ify inputs 3. Let's look at some examples. Synopsis Use the Array conversion function to coerce the input into an Array..

As such. Take the very first example: At one time in Ruby's history. But this has been removed in the 1. Calling it is an informational side-effect. The worst case scenario should be that no instrument reading is logged. But not all of them. I throw Kernel Array around it and stop worrying about whether the input arrived in the expected form.

Kernel Array is my go-to tool for coercing inputs into Array form. Anytime I know that code needs an Array—or simply an Enumerable of some kind—to operate on.

Synopsis Define an idempotent conversion function, which is then applied to any incoming objects. For example, define a Point method which yields a Point object when given pairs of integers, two-element arrays, specially formatted strings, or Point objects.

Rationale When inputs are immediately converted to the needed type or rejected , we can spend less time worrying about input uncertainty and more time writing business logic. A conversion function for 2D points Let's reuse the example of a 2D graphics library.

For convenience, client code can specify points on the canvas as two-element arrays; as strings of the form " Internally, however, we want to do all of our processing in terms of Point instances. Clearly we need some kind of conversion method. This method should have a couple of properties: It should be concise, since we'll be calling it a lot. It should be idempotent. That way we won't have to spend time worrying about whether a given input object has already been converted.

We'll adopt the convention found in Ruby core and standard libraries, and define a specially-named "conversion function" for this purpose. It will be named after the "target" class, Point. Like other conversion functions such as Kernel Array and Kernel Pathname, it will be camel-cased, breaking from ordinary Ruby method naming conventions.

We'll define this method in a module, so that it can be included into any object which needs to perform conversions. Conversions Point Point.

We proceed to use this conversion function liberally within our library, especially when working with input values form external sources. In any given stanza of code, Then we can shift our attention back to the task at hand, knowing that we'll be dealing with Point objects only. As an example, here's the beginning of a method which draws a rectangle: This obscurely-named built-in method does two things: First, it marks all following methods as private. Second, it makes the methods available as singleton methods on the module.

By marking the Point method private, we keep the method from cluttering up the public interfaces of our objects. So for instance if I have a canvas object which includes Conversions, it would make very little sense to call canvas. Point 1,2 externally to that object. The Conversions module is intended for internal use only.

Marking the method private ensures that this distinction is observed. Of course, we could have accomplished that with a call to private. That way, it's possible to access the conversion function without including Conversions, simply by calling e. Point 1,2. Combining conversion protocols and conversion functions Let's add one more tweak to our Point conversion function. So far it works well for normalizing a number of pre-determined types into Point instances.

But it would be nice if we could also make it open to extension, so that client code can define new conversions to Point objects. To accomplish this, we'll add two hooks for extension: We can demonstrate this: As you probably know.

Confident Ruby x. We are no longer excluding Array-like objects which don't happen to be descended from Array. Ruby's Proc objects have the threequals defined as an alias to call. Using the Ruby convention of naming the conversion function identically to the target class gives a strong hint to users about the semantics of the method.

There are numerous case statements switching on the content of the String. Not only is this no longer necessary… it actually interferes with smooth and proper communication between the parts of your program… Because bits. It is a perfect vehicle for hiding information.

Synopsis Replace strings that have special meanings with user-defined types. Rationale Leaning on polymorphism to handle decisions can remove unnecessary redundancy. Pattern Languages of Program Design The string is a stark data structure and everywhere it is passed there is much duplication of process. Replace "string typing" with classes Example: Traffic light states In several of the preceding sections. Consider a class which controls a traffic light. It receives inputs describing traffic light states: Let's dig into an example which will help to show why preferring user-defined types to strings.

All those case statements… And besides. But it doesn't keep us from accidentally misspelling the value in internal code. Replace "string typing" with classes For one thing.

Confident Ruby What if we represented the states of the traffic light as a special kind of object instead? Checking for valid input values is now a bit easier. We'll see if we can improve on that as we iterate on this approach. One of the first things that strikes us.

Ruby will quickly let us know. If we misspell a constant. And we're protected against internal typos now because we use constants everywhere instead of strings.. On the other hand. Java-esque feel to it.. Confident Ruby The "caution" case is different from the others. Is there some way to incorporate this difference into our State objects? To address this. Instead of making them instances. The remainder of the TrafficLight class is now almost vestigial: Confident Ruby.

Next state is: Both of these concerns have now been addressed. In addition. It was all too easy to introduce an invalid value for the state variable. There were repetitive case statements all switching on the same variable. Although less common. Representing this concept as a class or set of classes can not only make the code less error-prone.

And our traffic light state classes provide an obvious extension point. Note that this is just as true of Symbol inputs as it is for strings. Confident Ruby Conclusion Let's look back at the pain points which triggered this series of refactorings. The key to working productively in an object-oriented language is to make the type system and polymorphic method dispatch do your work for you.

When dealing with string or Symbol values coming from the "borders" of our code. Logging to IRC To use a somewhat contrived example. Synopsis Wrap input objects with adapters to give them a consistent interface. Wrap collaborators in Adapters 3. It logs events along with how long they took. Rationale An adapter encapsulates distracting special-case handling and ensures the special case is dealt with once and only once. Here's how it will look in use: Implementing the class to be compatible with the first three sink types is straightforward: Confident Ruby We'd like to make this class compatible with a variety of log "sinks"—destinations for the log messages.

Wrap collaborators in Adapters However. And here things are not so simple. Our IRC logging bot looks like this: Instead of cluttering up the info method and potentially more methods down the road with type checking.

Bot sink. We've already established that there is a simple. Uncertainty about the type of the sink collaborator has introduced a large. And we know from experience that where there is one case statement switching on the type of a collaborator. Confident Ruby class BenchmarkedLogger. Bot then IrcBotSink. Confident Ruby from the rest of the class. Methods like info can confidently dump logging output to any type of sink. Synopsis Make adapter objects transparent delegators to the objects they adapt, easing the transition to a more decoupled design.

Rationale Improving the separation of concerns in our code is a more approachable problem if we can accomplish it one tiny step at a time. Logging to IRC, again In the section on adapters, we wrote a BenchmarkedLogger class which used an adapter object to give an IRC bot log "sink" the same interface as a file, socket, or Array sink. But what if we weren't writing that class from scratch?

What if, instead, we came to it after it already had numerous switches on the type of the "sink" collaborator, coupled with calls to Cinch:: Bot-specific method calls?

Confident Ruby class BenchmarkedLogger As careful, disciplined developers who don't have an infinite amount of time on our hands! But wrapping Cinch:: We'd have to carefully audit each method for its interactions with IRC bot-type sinks.

And if the class lacks a comprehensive test suite, we still might miss a few cases which won't show up until they crash in production. Instead, we decide to introduce a transparent adapter object.

But any other messages sent to the object will be passed through to the underlying Cinch:: Bot object. We use the DelegateClass class generator from the delegate standard library as a basis for our transparent adapter class.

This generates a base class in which all the methods of the passed class are implemented to delegate to an underlying object.

So calling handlers on IrcBotSink will call handlers on an internal Cinch:: Bot instance. The actual Cinch:: Bot instance to which method calls will be delegated is passed as an argument to the class initializer. DelegateClass provides this initializer for us, so we don't need to write our own. All we need to provide are the methods that we want to add to the adapter's interface.

We make one more change to make this work: Bot class, we replace it with IrcBotSink. Otherwise, case Since this is a straightforward search-and-replace operation, we judge it to be a fairly safe change.

Conclusion At first glance, it doesn't seem like we've accomplished much here. But by replacing direct Cinch:: Bot instances with transparent adapter objects, we've opened a path towards unifying the interfaces of various sink types. Accepting them into the method has potentially harmful or difficult-to-debug side effects.

Synopsis Reject unacceptable values early, using precondition clauses. Rationale It is better to fail early and obviously than to partially succeed and then raise a confusing exception. Employee hire dates Here's some code that's experiencing several symptoms of paranoid insecurity:. It looks like over the course of development. They each chose to handle this fact in a slightly different way.

This is an example of a class which needs to set some boundaries. Reject unworkable values with preconditions the hire date is missing.

The Ruby Way, Second Edition: Solutions and Techniques in

But the constructor. This class has some serious problems with second-guessing itself. Since there is no obvious "right" way to handle a missing hire date.

One of the purposes of a constructor is to establish an object's invariant: But preconditions can be used to check for invalid inputs to individual methods as well. First and foremost. But secondly. Executable documentation Preconditions serve double duty. In Ruby we don't have any built-in support for DbC. When reading the code. In some cases.

Want to add to the discussion?

Confident Ruby Conclusion Some inputs are simply unacceptable. More insidiously. Decisively rejecting unusable values at the entrance to our classes can make our code more robust. But in other cases. Rationale fetch is concise. Synopsis Implicitly assert the presence of the required key with Hash fetch. It receives various attributes for the new user as a Hash. Use fetch to assert the presence of Hash keys 3. Certain hash keys are required for correct operation.

Have you spotted it? There's a bigger problem with this method than just verbose input-checking code. But in fact.

Use fetch to assert the presence of Hash keys This method handles the: They bomb out early if the passed Hash is missing required attributes. In theory. The unless. Go fetch As it happens. Confident Ruby that far Because false is. Hash fetch. Let's test the same sequence of calls we tried before: But how does it also fix the bug where: To explain. Let's take a closer look at the output of the second call. The Hash fetch behaves like the subscript [] operator with a key difference: Where they differ is in how they handle a missing key.

Confident Ruby

As we can see from the results. In the other. In one. The last two examples of the subscript operator are particularly interesting. So far we've only looked at the simplest form of calling fetch. And in fact. In effect.

Customizing fetch We've killed two birds with one fetch. Confident Ruby By contrast. But fetch can also take a block. When the block is supplied and fetch encounters a missing key. Otherwise when the key is found. It would be nice if we could communicate this hint using fetch. Section The semantics of fetch are similar in each case.

Use fetch to assert the presence of Hash keys With this knowledge in hand. This is not the last we'll be seeing of fetch. Other third-party libraries have adopted the fetch protocol as well. We've also seen how it is more precise than using subscript. It has some more tricks up its sleeve. It's worth noting here that Hash is not the only core class to define fetch. Optionally receiving a logger Here's a method that performs some work. Rationale fetch clearly expresses intent.

Some hash keys are optional. Because the work takes some time. Synopsis Provide default values for optional hash keys using Hash fetch. Downloading cuteness INFO -. In order to better support this case. So for instance if the calling code wants to log all events using a custom formatter. Finding cuteness INFO -. INFO -. Because false is "falsey". We can fix this by using Hash fetch with a block to conditionally set the default logger. Because fetch only executes and returns the value of the given block if the specified key is missing—not just falsey—an explicit false passed as the value of the: It's the same problem we saw back in "Use fetch to assert the presence of Hash keys".

When we call it with logger: Kitten path: In this case.. But I prefer it for more than that reason alone. To me.. Reusable fetch blocks In some libraries. The default logger is the same for each method. Confident Ruby This time.. That is.. Two-argument fetch If you have some familiarity with the fetch method you may be wondering why I haven't used the two-argument form in any of these examples.. Here's why: Now our expensive default code is being executed every time we fetch a value.

By our premature optimization. Because the default value is used in more than one place. Maybe one that has to communicate with an remote service before returning. I never use the two-argument form. Use fetch for defaults This avoids the slight overhead of executing a block. I prefer to always use the block form.. Confident Ruby everywhere our default method is used as an argument.

If nothing else. Conclusion Like using fetch to assert the presence of Hash elements. The fact that the default is expressed with a block means that a common default can easily be shared between multiple fetch calls.

Since I use fetch a lot. Defaults can also be provided as a second argument to fetch. What if we introduce code into the default method which has side-effects.

I'd rather not have to think about whether future iterations of the defaulting code might be slow or have side effects. We might have intended for those side effects to only occur when the default value is needed. I just make a habit of always using the block form of fetch.

If we had used the block form. And it's not just about avoiding performance hits. The first thing we decide to do is to stash the loaded transactions into a local data store. The format of the input is under-documented and potentially volatile. According to the meager documentation we can find. Importing bank transactions Let's say we're working on some budget management software. Rationale When dealing with inconsistent. Use assertion failures to improve your understanding of the data.

The next user story requires the application to pull in transaction data from a third-party electronic banking API. Synopsis Document every assumption about the format of the incoming data with an assertion at the point where the assumed feature is first used. Document assumptions with assertions 3. But in this world of data uncertainty we've now entered..

Array or raise TypeError. But what if there are no transactions found? What if the account is not found? Will it raise an exception. Given enough time we might be able to work it out by reading the API library's source code. An Array seems likely. We decide to simply make an assumption… but as insurance. Before we can store the value locally. The more precisely we can characterize the data now.

We ask our teammate. We make another trial run and we don't get any exceptions. She says she thinks they are Hashes with string keys.

Document assumptions with assertions can find. Nobody in the office So instead. We'd prefer to document our assumption more explicitly. Hash fetch will raise a KeyError if the given key is not found. We decide to tentatively try looking at the "amount" key. We manually test the code against a test account and it doesn't blow up. In order to once again document our assumption. We see this: It seems the amounts are reported as decimal strings.

We decide to go ahead and convert them to integers. Integer or raise TypeError. We know that many financial system store dollar amounts as an integer number of cents.

Confident Ruby seems to know what format the amounts come in as. We get an error. Document assumptions with assertions transactions.

Having an unrecognized amount format silently converted to zero could lead to nasty bugs down the road.. Yet again. What about negative numbers? Will they be formatted as "4. This time. A little experimentation proves this to be true. It communicates a great deal of information about our understanding of the external API at the time we wrote it. It explicitly establishes the parameters within which it can operate confidently…and as soon as any of its expectations are violated it fails quickly.

By failing early rather than allowing misunderstood inputs to contaminate the system. And Document assumptions with assertions not only does this code document our assumptions now. Conclusion There is a time for duck-typing. We may have little understanding of what form input may take. We free our internal code from having to protect itself from changes in the input data structure.

And we establish a canary in our integration coal-mine. But sometimes we have to communicate with systems we have no control over. By stating our assumptions in the form of assertions at the borders. When we have some control over the incoming data. At times like this.

Ruby Edition Indications In certain unusual cases. Synopsis Use a guard clause to effect an early return from the method. Instead the guard clause says.

This communicates to the reader that the legs are equally likely and important. Rationale Dispensing with a corner case quickly and completely lets us move on without distraction to the "good stuff".. And in this case.. One day. From that perspective. So we add a quiet flag. The real "meat" of this method is how readings are logged: But every level of conditional nesting in a program forces the reader to hold more mental context in his or her head.

Put the return first We could also have expressed the guard clause above thus: Confident Ruby the extra context that the unless quiet. We are left with the primary stanza of where it belongs. But then we are done. Either the method proceeds forward. And where methods become confusing, at least for me, is when those are all mixed together. And so, I did this kind of visual aid in the talk where I would take a method that was really long and had all these things mixed together.

And like James was saying, I would just mark it up in four different colors, showing which parts were collecting input, which parts were actually doing the work of the method, which parts were delivering results and which parts were dealing with failure and showing that it would just go back and forth.

When I say collecting input, I sometimes mean going and finding input like organizing and pulling it out of the database or descending through a chain of methods. But I also sometimes mean getting input into the form that we need.

So sometimes, that involves conversions as well. I feel like this mixture of parts of a method is what confuses things. A lot of what I'm going through in this book is sorting these out. And by the end of that stanza, there's going to be no more code in the method that deals with collecting input.

Everything you need is ready in a variable. And then, moves on to performing work and so on. It depends on the context and how big those parts are. A lot of the stuff that I show here, especially when it comes to collecting input, is really intended to go at the border lands of your code.

A lot of this stuff is sort of guard the borders. No, just check once and deal with it there and be done. I love the guard the borders metaphor that you used there. It sets up the object and then you can make sure that all of the instance variables are set up right.

And then you never need to check them again. And I have these very small bottlenecks that control how things get written so that I can always be assured that the values will be in the same state. It just grabs it and instantiates it. So obviously, I need to guard my inputs. Is there some way to say these classes are on the border and these classes are in the protected area where their inputs are already checked?

We have protected, which is pretty much useless, very close to useless. We could use [caller] to get the location of where it was instantiated. And you talk about that as getting away from what the BRE. So, Ruby has support for that in the language. It is not the sort of language where we declare types everywhere. So, maybe a different way of looking at this is how do you think the Ruby 2 keyword arguments would change how you talk about some of these patterns?

It does change it. But now, you can in Ruby 2. So, I feel like I need to dig into that some more. And it would be nice to release an update to the book that talked about keyword arguments a bit. I think the target version for this book was on 1.

I might have talked a little bit about some 2. Here is the default. But again, 2.

You gave us the Confident Ruby book and then you included a, I think it was about minute video of you going into an actual codebase. It was Discourse. And going in and applying these techniques to Discourse. So literally, you would pause the video and then highlight the code in the various colors like I was talking about earlier.

And then you would play around a little bit and do that. Also, you gave us a side book on a library built on the null object pattern. We can talk about that in a bit. But the video especially, one thing I noticed in there is that you have far better restraint than I do.

And it looks dramatically better. But I would obsess over it. And I would have to do the other 20 things that make up the rest of the difference. But you stopped there. The goals of the video were to specifically show refactorings derived from the book, refactorings that were about making the code more confident. Give me a chunk of code and I could probably refactor it infinitely long past the point of it making any difference. But for the video, I really just wanted to show stuff that was from the book, stuff that was about confidence.

So, I did have to really restrain myself. I have to say that that was one nice thing about the video, is it did give me the opportunity to live demonstrate the marking up methods in terms of the different parts of a method. That process is actually painful coming up with one of those pictures for the book.

I wanted to do more of them for the book but it was just this really tedious process. But the video process that I have makes it relatively easy. Not so much the code analysis part of it. But I really like the video. I thought it was great as far as just getting to see it in action kind of helps put the whole thing together. You had some really cool examples in there. And then you get one with dynamic dispatch on an after save thing. There were these different after save handling three different things and you were so ruthlessly practical in that.

You [inaudible] so good. You talked about the different options and how you could go too far with it. But it is one of the reasons that I like the video format so much.

And obviously I like it because I do Ruby Tapas for a living. But I love the video format because I feel like for whatever reason, it really helps me just talk about when things are pragmatic versus when things are overkill.

And I also really like doing recorded pair programming sessions like I did with Josh. But do you want to take a moment to talk now about how does looking at things this way and the patterns in the book, how do you address all that using a TDD process?

So a lot of them start with code A and then refactor it to use a pattern and end up with code B. If an argument, dumb example, an argument should never ever, ever be nil. This is guaranteed to fail. Somewhere deep in the code you get a no method error on nil and it turns out that that was actually the tertiary result of three nils back in a completely different part of the code. Do you focus more on testing how objects get created and initialized? Do you mock things more or less?

In general, more and more, I like to test based on the idea of roles. Now, what would be a good thing to actually play this role? Sometimes, they felt long. I figured you were trying to avoid it. So, I try to avoid it. I would never write that much code just to refactor that tiny piece of code. And I got completely distracted by that because of elephants. Some of this I think could be addressed by just better publishing techniques. Eventually I would like to have in my tool chain the ability to do a long code sample which is then marked up in such a way that you can see where the changes are easily.

The change is highlighted and maybe have some numbered call outs or side-by-side explanation or something along those lines. Are there types of stories? Are there canonical stories that code should tell?

And you can zoom out and actually see the shape. And then inside the do, end, open an output file, do. And then inside there iterate over a list of things, do.

And I do feel like there are these sorts of archetypal method shapes. And I would love to catalog them sometime. Dave Thomas I know used to talk about the way he would first come to understand a brand new codebase is he would just dump it all into a Word document and then use Word features to just zoom the thing out until he was looking at it spread across, looking at the entire codebase or at least a whole long file or something zoomed out, just looking at the whole thing on one screen.

It was barely little dots at that point. Confident Ruby is, first and foremost, a book about joy. The structure of the book is that of a patterns catalog. But these are not large, heavy-weight architectural patterns. These patterns are small, most of them taking place at the level of an individual method or even a single line of code.

They are related by a single organizing principle: Curious about the kind of material you'll find in this book? Here's a video of the talk that inspired it.But what about when we call it with a larger page size and no auth credentials? Say it again please. The most unambiguous form of input is an argument: We ask our teammate. It is this step—bridging the gap from the objects we have.

GINO from Bethlehem
Browse my other posts. I have a variety of hobbies, like cooking. I am fond of playfully .