Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Confident ruby

Dombi Attila
March 12, 2015
110

Confident ruby

A small presentation about the book "Confident Ruby" by Avdi Grimm, all of the content credit goes to him.

Dombi Attila

March 12, 2015
Tweet

Transcript

  1. Fun factor Level of awesomeness 0% 25% 50% 75% 100%

    Size of the project Small Medium Large
  2. A good story, poorly told. • If you fight the

    angry troll with your bare hands, turn to page 137. • If you try to reason with the troll, turn to page 29. • If you don your invisibility cloak, turn to page 6. • You exit the passageway into a large cavern. Unless you came from page 59, in which case you fall down the sinkhole into a large cavern. • A huge troll, or possibly a badger (if you already visited Queen Pelican), blocks your path. • Unless you threw a button down the wishing well on page 8, in which case there nothing blocking your way. http://en.wikipedia.org/wiki/Choose_Your_Own_Adventure
  3. The four parts of a method 1. Collecting input 2.

    Performing work 3. Delivering output 4. Handling failures
  4. Sending a strong message Writing maintainable code is all about

    focusing our mental energy on one task at a time, and equally importantly, on one level of abstraction at a time.
  5. Example scenario • Consider a system that distributes e-books in

    various formats to buyers. • There is a lot of preexisting purchase data which needs to be imported into the new system. The purchase data has the following form: name,email,product_id,date Crow T. Robot,[email protected],123,2012-06-18
  6. Identifying the messages 1. #parse_legacy_purchase_records. 2. For #each purchase record,

    use the record's #email_address to #get_customer. 3. Use the record's #product_id to #get_product. 4. #add_purchased_product to the customer record. 5. #notify_of_files_available for the purchased product. 6. #log_successful_import of the purchase record.
  7. Identifying the roles Message Receiver Role #parse_legacy_purcha se_records legacy_data_parser #each

    purchase_list #email_address, #product_id purchase_record #get_customer customer_list #get_product product_inventory #add_purchased_prod uct customer
  8. Avoiding MacGyver method • We never once talked about the

    already existing classes or methods defined in the system. • We didn't discuss the app's strategy for persisting data such as customer records; whether it uses an ORM. • We didn't even talk about what class we are defining this method on, and what methods or attributes it might already have.
  9. Strategies 1.Coerce objects into the roles we need them to

    play. 2.Reject unexpected values which cannot play the needed roles. 3.Substitute known-good objects for unacceptable inputs.
  10. 1.1 Use built-in conversion protocols • Indications: You want to

    ensure that inputs are of a specific core type. For instance, you are writing a method with logic that assumes Integer inputs. • Synopsis: Use Ruby's defined conversion protocols, like #to_str, #to_i, #to_path, or #to_ary.
  11. 1.2 Define your own conversion protocol • Indications: You need

    to ensure inputs are of a core type with context-specific extra semantics. For instance, you expect to work with two-element arrays of integers representing X/Y coordinates. • Synopsis: Define new implicit conversion protocols mimicking Ruby's native protocols such as #to_path. • Rationale: By exposing a conventionalized way for third-party objects to convert themselves to the type and "shape" our method needs, we make our method more open to extension.
  12. 1.3 Define conversions to user-defined types • Indications: Your method

    expects to work with a Meter type, but you would like to leave the possibility open for Feet or other units to be supplied instead. • Synopsis: Define your own conversion protocol for converting arbitrary objects to instances of the target class. • 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".
  13. 1.4 Use built-in conversion functions • Indications: You really, really

    want to convert an input object into a core type, no matter what the original type is. For instance, you need to ensure that any input is coerced into an Integer if there is any reasonable way to do so; whether the incoming data is a Float, a nil, or even a hexidecimal string. • Synopsis: Use Ruby's capitalized conversion functions, such as Integer and Array. • Rationale: When a method's internals deal with core types, using a conversion function to pre- process input values allows maximum flexibility for inputs while preventing nonsensical conversions.
  14. Core conversion functions • There are other conversion methods than

    #to_* in ruby. • The Kernel module also provides a set of unusually-named conversion functions: ˒ Array(), Float(), String(), Integer(), Rational(), and Complex().! • These share the same name with ruby core classes, but technically they are ordinary methods. • Other examples, uri module provides URI(), pathname library provides Pathname()! • They are idempotent. Calling the method with an argument which is not of the target class will cause it to attempt a conversion. Calling it with an argument of the target type will simply return the unmodified argument. • They can convert a wider array of input types than the equivalent #to_* methods.
  15. Take Integer() for example 1.Given a Numeric value, it will

    convert it to an Integer or Bignum. A Float will be truncated. 2.It will convert strings containing integers in decimal, hexidecimal, octal, or binary formats to an integer value. 3.It will attempt to convert other objects using #to_int if it exists. 4.It will fall back to #to_i if none of the above rules apply.
  16. About module_function • 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.
  17. • Extensibility • We no longer exclude methods that are

    Array like but are not descendent from Array (using #to_ary) • Client objects now can define an explicit #to_point conversion method method Benefits