Avdi Grimm - Confident raudone.info - Free ebook download as PDF File .pdf), Text File .txt) or read book online for free. Avdi Grimm - Confident raudone.info - 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.
|Language:||English, Arabic, French|
|Genre:||Science & Research|
|ePub File Size:||26.40 MB|
|PDF File Size:||10.59 MB|
|Distribution:||Free* [*Registration needed]|
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.
As we consider how to collect input for a method. This is actually accomplished on the fourth line. From Roles to Objects To explain why this is. Let's emphasize this delineation with some added whitespace: We saw that writing a method in terms of roles yielded code that told a clear and straightforward story. Introduction to collecting input It also illustrates a common idiom in method-writing: The purpose of the method.
But whether we separate it out into a distinct stanza or not. Confident Ruby determining how lenient to be in accepting many types of input. Like customs checkpoints at national boundaries. Once we've determined the inputs an algorithm needs. It is this step—bridging the gap from the objects we have. Guard the borders.
These strategies will fall into three broad categories: We'll be making assertions about inputs. Most of the techniques found in this section are best suited to the borders of your code. Reject unexpected values which cannot play the needed roles. Substitute known-good objects for unacceptable inputs. In the patterns to come we'll examine several strategies for ensuring a method has collaborators which can be relied upon to play their assigned roles.
You may reasonably wonder if this level of paranoia is really called for in most methods. Coerce objects into the roles we need them to play. Programming defensively in every method is redundant. Once objects pass through this border guard. Introduction to collecting input What constitutes the borders? It depends on what you're working on.
The public methods of these gatekeeper objects are a natural site for defensive code. On to the practices. In a larger library. In their book Object Design.
If you are writing a reusable library. Rebecca Wirfs-Brock and Alan McKean talk about "object neighborhoods" which together form discrete subsystems. For instance. Rationale By typing a few extra characters we can ensure that we only deal with the types we expect.
Synopsis Use Ruby's defined conversion protocols. Announcing winners Let's say we have an array of race winners. They each encapsulate an index into the array of winners. We can do this with a loop. In the loop. In order to do so. Peasant's Quest game! King of Town! Limozeen Album! Unfortunately this doesn't work: This is fine. What if we give it something which isn't a String representing a filename.
ConfigFile Here's another example. According to the Ruby standard library documentation. It isn't stated. This turns out to be a good thing. As a result. By stating their needs clearly in the form of calls to conversion methods. Here's a list of the standard conversion methods used in Ruby core. Note that some of these are conditionally called by Ruby core code.
Implicit conversions represent conversions from a class that is closely related to the target class. Explicit conversions represent conversions from classes which are mostly or entirely unrelated to the target class. Let's talk about what that means. By contrast. There are any number of ways of representing a Time as a String. Ruby is a dynamically-typed language. A Time object is not a String.
Confident Ruby An example will help explain this distinction. In fact. Not exactly. Use built-in conversion protocols As it turns out. This means that if we define our own "string-like" objects. It's basically just a String with some convenience methods tacked on to it. What does it do? Use built-in conversion protocols object if we want to.
You are probably already familiar with this case. When we use string interpolation. This includes a lot of classes like Time. String interpolation wouldn't be nearly as convenient if the only objects we could interpolate were other strings! Explicit conversions are there for you. You'll note that I said "Ruby core classes" never use explicit conversions. In this case. Confident Ruby If you know what you want.
Anytime we find ourselves assuming that an input to a method is one of the core types such as String. If we want to be forgiving about input types and just make sure that our logic receives the type it expects. We need an Integer?
We need a String? If we need a Hash and we're using Ruby 2. If there is both an implicit and an explicit conversion method. Note that the explicit and implicit conversion methods are also defined on their target classes. And the method's logic can then work with the converted objects confidently.
The client doesn't have to pass in objects of a specific class. Confident Ruby Conclusion If a method is written with a specific input type in mind. If we want to provide maximum leeway in input. If we want to provide a little flexibility while ensuring that client code isn't blatantly mis-using our method. Let's take a look at the code.
Conditionally call conversion methods 3. Synopsis Make the call to a conversion method conditional on whether the object can respond to that method. Here's the relevant section from MRI's file. Rationale By optionally supporting conversion protocols. Either way. In the first case. If it does. This is especially useful when we are defining our own conversion methods. This code says. Of course. We could check that the object responds to every message we will send to it: Let's look at all of our options for making sure this method gets the inputs it needs.
We could explicitly check the type: Conditionally call conversion methods Violating duck typing. It puts an arbitrary and needlessly strict constraint on the class of the input. How is this exception justified? To examine this objection. But it would also allow invalid inputs.
The protocol check at the beginning is terribly brittle.. And Pathname arguments are still not supported. Section We can pass String. But other objects passed in by mistake. Conclusion Sometimes we want to have our cake and eat it too: Inputs are checked. It doesn't ask "are you the kind of object I need?
Rationale By exposing a conventionalized way for third-party objects to convert themselves to the type and "shape" our method needs. But there may come a time when the core protocols don't capture the conversion semantics your apps or libraries need.
For simplicity. Accepting either a Point or a pair Ruby defines a number of protocols for converting objects into core types [page 34] like String. Define your own conversion protocols 3. Consider a 2D drawing library.
We'd also like to make it possible for an object to have a coordinate conversion even if otherwise it doesn't really make sense for it to have a general Array conversion. In order to capture this input requirement. Here's a method which uses the protocol: Confident Ruby little more meaning to the conversion call. When combined with conditionally calling conversion methods [page 47]. By documenting the protocol. Conclusion Ruby's conversion protocols are a great idea. You want instances of other classes to be implicitly converted to the needed type.
Because "bit integer" says nothing about the unit being represented. Converting feet to meters On September Programmers of mission-critical systems have long known of the potential dangers in using raw numeric types for storing units of measure.
Synopsis Define your own conversion protocol for converting arbitrary objects to instances of the target class. The cause of the mishap was determined to be a mix-up between the metric unit Newtons and the Imperial measure Pound-Force. Rationale A well-documented protocol for making arbitrary objects convertible to our own types makes it possible to accept third-party objects as if they were "native". Because calculations can only be performed using user-defined functions which take unit conversion into account.
Define conversions to user-defined types A common strategy for avoiding these errors. Imagine a control program for a spacecraft like the Mars Orbiter. All storage and calculation of measurements is done using these custom types rather than raw native numerics. It also delegates some methods directly to the Integer.
We'd also like to allow for measurements to be made in 58 Download from BookDL http: In addition.. We need a way to implicitly convert Meters to Feet and vice-versa. To accomplish this. The altitude change reporting method becomes: Define conversions to user-defined types feet.
Like a currency-exchange booth at an airport. Confident Ruby class Feet. But we've left the design open to the addition of arbitrary new units of measure..
Which is exactly what we want. Conclusion Working strictly with collaborator classes we design can take a lot of the uncertainty out of a method.
Synopsis Use Ruby's capitalized conversion functions. Even more unusually. Rationale When a method's internals deal with core types. As you can see. Use built-in conversion functions 3. The Kernel module also provides a set of unusually-named conversion functions: Given a Numeric value.
For the most part. Take the Integer method. Confident Ruby Some standard libraries also provide capitalized conversion methods. It will convert strings containing integers in decimal. To demonstrate: Calling it with an argument of the target type will simply return the unmodified argument. A Float will be truncated. They are idempotent. Calling the method with an argument which is not of the target class will cause it to attempt a conversion. The uri library provides the URI method. And the pathname library defines.
It doesn't much care what the class of the argument is. Here's a method which formats integers for human-readability. Consult the Kernel documentation [page 0] to learn more about how each conversion function works. As I mentioned earlier.
Here's a method which. The filename can be either a Pathname or something convertable to a Pathname. They can be called on an object of the target class and they will just return the original object.
The closest thing to it is the subscript square brackets method on the Hash class. Confident Ruby Hash. Given an even-numbered list of arguments. Conclusion By using a conversion function. Hash will produce a Hash where the first argument is a key. This is useful for converting flat arrays of keys and values into hashes. Accepting one or many arguments We mentioned the Kernel Array method in the preceding section about conversion functions. Rationale Array ensures that no matter what form the input data arrives in.
Let's look at some examples. Kernel Array takes an argument and tries very hard indeed to convert that argument into an Array. Use the Array conversion function to array-ify inputs 3. Synopsis Use the Array conversion function to coerce the input into an Array. Take the very first example: At one time in Ruby's history. As such. But this has been removed in the 1. The worst case scenario should be that no instrument reading is logged. Calling it is an informational side-effect.
But not all of them. I throw Kernel Array around it and stop worrying about whether the input arrived in the expected form. Anytime I know that code needs an Array—or simply an Enumerable of some kind—to operate on. Kernel Array is my go-to tool for coercing inputs into Array form. For convenience. A conversion function for 2D points Let's reuse the example of a 2D graphics library.
Rationale When inputs are immediately converted to the needed type or rejected. Synopsis Define an idempotent conversion function. It should be idempotent. Like other conversion functions such as Kernel Array and Kernel Pathname. It should be concise. Define conversion functions or as instances of a Point class that we've defined.
This method should have a couple of properties: We'll define this method in a module. That way we won't have to spend time worrying about whether a given input object has already been converted.
Clearly we need some kind of conversion method. It will be named after the "target" class. We'll adopt the convention [page 61] found in Ruby core and standard libraries. In any given stanza of code.
Conversions Point Point. The Conversions module is intended for internal use only. Point 1. Then we can shift our attention back to the task at hand.. This obscurely-named built-in method does two things: Marking the method private ensures that this distinction is observed.
Define conversion functions if we're uncertain what format the input variables are in. By marking the Point method private.
That way. So for instance if I have a canvas object which includes Conversions.. As an example. Confident Ruby 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. Confident Ruby x. As you probably know. 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. We can demonstrate this: 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. Pattern Languages of Program Design The string is a stark data structure and everywhere it is passed there is much duplication of process.
Synopsis Replace strings that have special meanings with user-defined types. Rationale Leaning on polymorphism to handle decisions can remove unnecessary redundancy.
There are numerous case statements switching on the content of the String. It is a perfect vehicle for hiding information. Not only is this no longer necessary… it actually interferes with smooth and proper communication between the parts of your program… Because bits.
Let's dig into an example which will help to show why preferring user-defined types to strings. It receives inputs describing traffic light states: Consider a class which controls a traffic light. Replace "string typing" with classes Example: Traffic light states In several of the preceding sections.
Replace "string typing" with classes For one thing. All those case statements… 81 Download from BookDL http: But it doesn't keep us from accidentally misspelling the value in internal code. And besides. Confident Ruby What if we represented the states of the traffic light as a special kind of object instead? And we're protected against internal typos now because we use constants everywhere instead of strings. If we misspell a constant, Ruby will quickly let us know.
On the other hand, calling light. We'll see if we can improve on that as we iterate on this approach. One of the first things that strikes us, looking at the changes above, is that we could move the concept of "next state" into the state objects themselves, thereby eliminating one of those case statements.
This gives us another idea: Unfortunately, we run into a problem with the signal method:. Confident Ruby The "caution" case is different from the others; it not only lights the appropriate lamp, it also rings a warning bell. Is there some way to incorporate this difference into our State objects?
To address this, we revisit the definitions of the individual states. Instead of making them instances, we make them subclasses. Unfortunately, the calling convention for TrafficLight methods taking a State has only gotten worse:.
Next state is: Both of these concerns have now been addressed. Note that this is just as true of Symbol inputs as it is for strings. When dealing with string or Symbol values coming from the "borders" of our code.
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. 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. Representing this concept as a class or set of classes can not only make the code less error-prone. In addition. And our traffic light state classes provide an obvious extension point.
Although less common. Rationale An adapter encapsulates distracting special-case handling and ensures the special case is dealt with once and only once. Synopsis Wrap input objects with adapters to give them a consistent interface.
By professional developers for professional developers.
Wrap collaborators in Adapters 3. Here's how it will look in use: It logs events along with how long they took.. Logging to IRC To use a somewhat contrived example. 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.
And here things are not so simple. Our IRC logging bot looks like this: Wrap collaborators in Adapters However. And we know from experience that where there is one case statement switching on the type of a collaborator.
We've already established that there is a simple. Confident Ruby class BenchmarkedLogger. Bot sink. Instead of cluttering up the info method and potentially more methods down the road with type checking. Uncertainty about the type of the sink collaborator has introduced a large. Bot then IrcBotSink. Confident Ruby from the rest of the class. Methods like info can confidently dump logging output to any type of sink.
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. Bot-specific method calls? Synopsis Make adapter objects transparent delegators to the objects they adapt. But what if we weren't writing that class from scratch? What if. Use transparent adapters to gradually introduce abstraction 3. And if the class lacks a comprehensive test suite.
But wrapping Cinch:: We'd have to carefully audit each method for its interactions with IRC bot-type sinks. But any other messages sent to the object will be passed through to the underlying Cinch:: Bot object.. Bot sink So calling handlers on IrcBotSink will call handlers on an internal Cinch:: Bot instance. We make one more change to make this work: Bot class. All we need to provide are the methods that we want to add to the adapter's interface.
DelegateClass provides this initializer for us. The actual Cinch:: Bot instance to which method calls will be delegated is passed as an argument to the class initializer.
This generates a base class in which all the methods of the passed class are implemented to delegate to an underlying object. Since this is a straightforward search-and-replace operation.
But by replacing direct Cinch:: Bot instances with transparent adapter objects. Confident Ruby statements switching on the sink object's class would no longer work. Method by method. Accepting them into the method has potentially harmful or difficult-to-debug side effects. Synopsis Reject unacceptable values early. Employee hire dates Here's some code that's experiencing several symptoms of paranoid insecurity: Rationale It is better to fail early and obviously than to partially succeed and then raise a confusing exception.
Reject unworkable values with preconditions 3. It looks like over the course of development. They each chose to handle this fact in a slightly different way. This class has some serious problems with second-guessing itself. This is an example of a class which needs to set some boundaries. One of the purposes of a constructor is to establish an object's invariant: Reject unworkable values with preconditions the hire date is missing.
Since there is no obvious "right" way to handle a missing hire date. But the constructor. But preconditions can be used to check for invalid inputs to individual methods as well. In Ruby we don't have any built-in support for DbC. Executable documentation Preconditions serve double duty. But secondly. First and foremost. When reading the code. More insidiously. But in other cases. Confident Ruby Conclusion Some inputs are simply unacceptable.
In some cases. Decisively rejecting unusable values at the entrance to our classes can make our code more robust. Rationale fetch is concise. Synopsis Implicitly assert the presence of the required key with Hash fetch. Use fetch to assert the presence of Hash keys 3. Certain hash keys are required for correct operation. It receives various attributes for the new user as a Hash. Use fetch to assert the presence of Hash keys This method handles the: In theory.
Have you spotted it? The unless. They bomb out early if the passed Hash is missing required attributes. But in fact. There's a bigger problem with this method than just verbose input-checking code.
Hash fetch. Confident Ruby that far. Because false is. Go fetch As it happens. 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: In one.
Where they differ is in how they handle a missing key. The last two examples of the subscript operator are particularly interesting. In the other. As we can see from the results. Customizing fetch We've killed two birds with one fetch. In effect. When the block is supplied and fetch encounters a missing key. But fetch can also take a block. Otherwise when the key is found.
So far we've only looked at the simplest form of calling fetch. It would be nice if we could communicate this hint using fetch. Confident Ruby By contrast. And in fact. The semantics of fetch are similar in each case. As a result.. It has some more tricks up its sleeve. Use fetch to assert the presence of Hash keys With this knowledge in hand. We've also seen how it is more precise than using subscript.
This is not the last we'll be seeing of fetch. Other third-party libraries have adopted the fetch protocol as well. It's worth noting here that Hash is not the only core class to define fetch. Some hash keys are optional. Synopsis Provide default values for optional hash keys using Hash fetch. Optionally receiving a logger Here's a method that performs some work. Rationale fetch clearly expresses intent.
Because the work takes some time. Finding cuteness INFO -. So for instance if the calling code wants to log all events using a custom formatter. INFO -. In order to better support this case. Downloading cuteness INFO -. Because false is "falsey". When we call it with logger: We can fix this by using Hash fetch with a block to conditionally set the default logger. It's the same problem we saw back in "Use fetch to assert the presence of Hash keys [page ]". 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: To me.
Kitten path: But I prefer it for more than that reason alone. Reusable fetch blocks In some libraries.. The default logger is the same for each method. 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.
Because the default value is used in more than one place. I prefer to always use the block form. I never use the two-argument form.. Maybe one that has to communicate with an remote service before returning. By our premature optimization. Now our expensive default code is being executed every time we fetch a value.. Here's why: Use fetch for defaults This avoids the slight overhead of executing a block. I'd rather not have to think about whether future iterations of the defaulting code might be slow or have side effects.
I just make a habit of always using the block form of fetch. What if we introduce code into the default method which has side-effects.
Conclusion Like using fetch to assert the presence of Hash elements. If nothing else. The fact that the default is expressed with a block means that a common default can easily be shared between multiple fetch calls. Confident Ruby everywhere our default method is used as an argument.
And it's not just about avoiding performance hits. We might have intended for those side effects to only occur when the default value is needed. If we had used the block form. Defaults can also be provided as a second argument to fetch. Since I use fetch a lot. Use assertion failures to improve your understanding of the data. The format of the input is under-documented and potentially volatile. The next user story requires the application to pull in transaction data from a third-party electronic banking API.
According to the meager documentation we can find. Document assumptions with assertions 3. The first thing we decide to do is to stash the loaded transactions into a local data store. Synopsis Document every assumption about the format of the incoming data with an assertion at the point where the assumed feature is first used.
Importing bank transactions Let's say we're working on some budget management software. Rationale When dealing with inconsistent. An Array seems likely. Given enough time we might be able to work it out by reading the API library's source code. We decide to simply make an assumption… but as insurance.. But what if there are no transactions found? What if the account is not found? Will it raise an exception. But in this world of data uncertainty we've now entered.
Array or raise TypeError.. Before we can store the value locally. Hash fetch will raise a KeyError if the given key is not found. We make another trial run and we don't get any exceptions. We'd prefer to document our assumption more explicitly. Document assumptions with assertions can find. We decide to tentatively try looking at the "amount" key. We ask our teammate. We manually test the code against a test account and it doesn't blow up.
The more precisely we can characterize the data now. So instead. She says she thinks they are Hashes with string keys. We get an error. Nobody in the office seems to know what format the amounts come in as. In order to once again document our assumption. We decide to go ahead and convert them to integers. It seems the amounts are reported as decimal strings.
Download Confident Ruby: 32 Patterns For Joyful Coding
Confident Ruby is in a format that our local transaction store can understand. We know that many financial system store dollar amounts as an integer number of cents. We see this: Integer or raise TypeError. What about negative numbers? Will they be formatted as "4. Having an unrecognized amount format silently converted to zero could lead to nasty bugs down the road.
Document assumptions with assertions transactions. This time. A little experimentation proves this to be true. Yet again. It communicates a great deal of information about our understanding of the external API at the time we wrote it. Array or raise TypeError.
And Download from BookDL http: By failing early rather than allowing misunderstood inputs to contaminate the system. It explicitly establishes the parameters within which it can operate confidently…and as soon as any of its expectations are violated it fails quickly. We free our internal code from having to protect itself from changes in the input data structure.
But sometimes we have to communicate with systems we have no control over. When we have some control over the incoming data.
By stating our assumptions in the form of assertions at the borders. At times like this. And we establish a canary in our integration coal-mine. We may have little understanding of what form input may take.
Conclusion There is a time for duck-typing. Document assumptions with assertions not only does this code document our assumptions now.
Instead the guard clause says.. This communicates to the reader that the legs are equally likely and important. Synopsis Use a guard clause to effect an early return from the method.
Ruby Edition Indications In certain unusual cases. Rationale Dispensing with a corner case quickly and completely lets us move on without distraction to the "good stuff". So we add a quiet flag. But every level of conditional nesting in a program forces the reader to hold more mental context in his or her head. And in this case.. The real "meat" of this method is how readings are logged: From that perspective. One day. Let's convert that unless block to a guard clause: Confident Ruby the extra context that the unless quiet.
We are left with the primary stanza of where it belongs. 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. But he could see the general shape of it. And then he could zoom in on parts of it and the shape would actually tell him some things.
A lot of times it was just, we can move this out here and look, now half the method is not indented anymore. Because I think when we have that, it highlights the things that are similar or it makes the things that are similar look similar.
And it also highlights the things that are different and should stand apart. You can install this gem plugin and then you can type gem sing and the name of the gem and it literally went through.
But it basically made this song off of the cadence of your code, in a way. So it was interesting. I see this idea proposed regularly, the idea that once the real world starts getting into your code, once your code makes contact with the real world, you have to start adding all these special cases and you have to start doing these big tangents in the middle of your code to handle a possible error condition so that kind of code is unavoidable.
I feel like Ruby gives us not just the ability to build abstractions around stuff but to easily built abstractions about stuff, to quickly build abstractions around stuff.
When the context has to be built up and then torn down, we can easily encapsulate that into a method that takes a block and then the method can worry about the context and whatever we put in the block can be the work that gets done within that context. We have these really good tools in Ruby. It said exactly what it was supposed to do. It was just very succinct. And it also had this thing where even the number was an object so there was that consistency there where everything was an object.
And I loved that. Instead, leverage the language. Use the tools of the language to make sure that as the complexity of your programs increases, as the complexity of the situations you have to handle increases, your abstractions and your idioms increase [fractally] so that any given part of that big complex system still looks just as beautiful as that 3.
Avdi Grimm open sources Quarto after self-publishing Confident Ruby
This is a discovery that I made as I wrote this. So those are the implicit and explicit conversion methods, or conversion protocols is another way of thinking of it. There are also things like the capital letter methods like the capital letter integer conversion function or capital string conversion function, stuff like that.
But one thing that I discovered or that I learned more about is this patter than Ruby uses internally, particularly it uses it with implicit conversion methods. But a path is also a string. And what this means is you have an extension point.
And it is not a string. And you can use that yourself. And this is a really neat pattern because you not only can you provide these conversions yourself, you can also mimic that pattern. Otherwise, use the object as is or maybe otherwise use the object converted to an array.
Are you the right type?
Download Confident Ruby: 32 Patterns For Joyful Coding
Do you support these messages? But if you specifically know how to give me a duck, then give me a duck. And now as I keep saying, now you have this extension point where other people can plug in their own things and have them just convert implicitly.
So that was something that I became more familiar with and I think I got more comfortable with the power of that pattern as I wrote this. What would that be called? Egg typing?
Do we have time for another quick topic? So, sure. So, I think one of the more interesting patterns in the book is using callbacks instead of returning status. But what is it, callback instead of returning, number 4. And I guess lots of fancier versions of that. So, I love this pattern. And this is something that object-oriented programming has done ever since Smalltalk where you invent your own control structures. Functional programming has their own take on that, too.
Can you talk about what happens to your codebase when you start pushing the conditionals down into the control structures in your block structured methods? What happens to the codebase? And you want to do something. If importing a download succeeds, then you want to send an invitation email with some new information out to the downloadr.
The alternative to this is yielding to a block when and only when that process succeeds. This has some interesting benefits. It eliminates an if statement. Do I return an array of trues and falses? So, it certainly changes your style in that the arity or operations, whether an operation is a one or a many, becomes less important, or at least it becomes less glaringly obvious in the code.
So, callbacks these days, everyone thinks of Node. Do it, but not so much. I think there are definitely better patterns out there when it comes to asynchronous programming.
As a general hand-wavy answer, I find things that do events in a pipeline interesting and sometimes easier to reason about. So, the callback pattern I think is great. But in the early days of Java, people would build callbacks using inner classes and basically specially constructed classes that had a very narrow protocol that would be used for doing the callbacks. I know you had one example where you had blocks within blocks.
When you get to that point, do you start thinking about, do I need to actually build some kind of specialized class to handle the callbacks?
You mean like having a class that maybe has various methods on this, on that, on the other? I think ideally, it would be neat to have some conventions where you could do one or the other. You can just give it a symbol for a method to run, to do the validation. You can give it a block or a lambda right there.
Or you can define a whole validator class and then use that to do the validation. I guess the big question is whether the collection, each collection of callbacks for a given call, are one-offs or not. If you find yourself wanting to handle, you always want to handle success by doing thing A, you always want to handle failure by doing thing B and you always want to handle timeout by doing thing C, then it starts to make sense to support some kind of callback object.
Because then you can just instantiate one of your standard callback objects and throw it in there and not have to rewrite the blocks every time. And you can think, oh well I could use some polymorphism and suddenly I have a lot of different classes of callbacks. I like to see you delegate off to a method somewhere.
Effectively, it turns into something like routing. The third thing you throw in is a whole other book. Do you want to tell us what that is? And what that means is that I basically wrote what I was going to do. So I wrote a little bit about what feature I wanted. In literate programming, you write prose and you write blocks of code and then you use your tool to tangle the code which means it puts the code into files and in the appropriate order to actually be run as opposed to be presented in book form.
And I would tangle the code and then the test would succeed, and so on. Basically, I wrote the library by writing a book. And the original source code since has diverged. But the original source code of the library was the book itself. And I would just hit the tangle command and it would all be taken out and put in the right order into files. Yeah, that was a neat experience doing it that way.
The book is cool. Is it about the null object pattern? Is it about meta-programming? And I think the answers are yes, yes, and yes.
It and the library were an excuse to use almost every trick in my meta-programming book and talk about each one in turn. There are some neat moments where you refactor to command classes and stuff.
I enjoyed. I definitely had a lot of fun with that one. That one was just pure fun. Well, should we wrap up and do the picks? This is going to be another one of those long episodes that people complain about. Well, James, what are your picks? I have no idea why. So, a couple of those are related to programming. Rich Hickey is the creator of Clojure so he takes a functional view of everything, which I find is good because it challenges my ideas about things and I like that.
There are lots of great videos in here. I actually watched all of them. The language of the system, just to hear him talk about what he thinks a system is and how our idea of an application breaks down when we get to that point and stuff I found particularly enlightening. But a lot of them are really good. It was by Fred George. And this video is really cool. There are some radical concepts in here like throw away the unit tests for these services and instead track business metrics, just stuff like that.
And this talk got in my head and I ended up having to go back and actually watch it, watch it the whole way through and see what it was about and stuff. There are some stuff in here that I think would be difficult to apply in every case but a lot of neat ideas in here, I think. And then finally for a less programming, more unusual pick.
After doing the talk at GoGaRuCo, one of the cool things is that people talk to me about and send me interesting videos about relating to being disabled and programming or stuff like that.
This particular video is somebody programming by voice command, which I thought is really interesting. Those are my picks. Josh, what are your picks?
Dave, what are your picks? And it basically starts 15 minutes before you set your alarm to go off. And it starts with one Zen meditation bowl ding and then it waits several minutes, does one more.
And it uses the golden ratio to slowly increase the frequency of the dings that arrive.And unfortunately. They could complete a vertices for the Testing architecture. Here's a method which. Imagine a control program for a spacecraft like the Mars Orbiter. Sometimes the type-switching is less overt. Introduction to collecting input What constitutes the borders?
Confident Ruby class GuestUser.