All books Eloquent Ruby
• Bookshelf Eloquent Ruby
The Ruby Way
- Embrace idiomatic code: Write Ruby that feels natural to experienced Rubyists
- Focus on readability: Optimize code for human understanding, not just machine execution
- Value clarity over cleverness: Choose clear, straightforward solutions over complex ones
- Follow community conventions: Adhere to widely accepted Ruby style practices
- Write code that communicates intent: Make your purpose clear through well-chosen constructs
- Trust other programmers: Assume code users are competent rather than protecting against misuse
- Value expressiveness: Use Ruby’s features to create code that expresses ideas concisely
- Appreciate the whole language: Learn both core Ruby and its standard libraries
- Understand Ruby’s philosophy: Recognize that Ruby is designed to make programmers happy
- Embrace object-oriented design: Use Ruby’s OO features to create well-structured programs
Working with Strings
- Choose string literals appropriately: Use different quote styles based on content needs
- Use string interpolation: Embed expressions in strings with #{} rather than concatenation
- Leverage here documents: Use here docs for multi-line strings to maintain readability
- Understand string mutability: Be aware of when string operations modify in place versus create copies
- Use flexible comparison options: Employ case-insensitive or partial matching when appropriate
- Leverage regular expressions: Use Ruby’s powerful regex support for complex string processing
- Access string methods: Familiarize yourself with Ruby’s extensive string manipulation API
- Handle encodings appropriately: Be aware of character encoding issues when processing strings
- Use symbols when appropriate: Choose symbols over strings for identifiers and internal references
- Format output thoughtfully: Use string formatting options that enhance readability
Working with Collections
- Understand array and hash fundamentals: Master the basic collection types and their operations
- Use literal syntax: Create arrays with [] and hashes with {} for readability
- Choose the right collection type: Select appropriate collections based on access patterns
- Use symbols as hash keys: Prefer symbols over strings for hash keys in most cases
- Leverage enumerable methods: Master map, select, reject, and other collection transformations
- Chain collection operations: Combine operations for concise and readable transformations
- Use collection blocks appropriately: Understand block semantics with collection methods
- Consider performance implications: Be aware of the efficiency of different collection operations
- Initialize collections properly: Use default values and blocks when creating collections
- Use collection modifications judiciously: Understand when methods modify collections in place
Control Structures
- Use if and unless modifiers: Place conditions at the end of statements when it improves readability
- Consider statement modifiers: Use postfix conditionals for simple cases
- Leverage case statements: Use case when pattern matching is clearer than if/elsif chains
- Avoid explicit return: Allow methods to return the last evaluated expression
- Use && and || for control flow: Leverage short-circuit evaluation for concise conditional execution
- Understand truthiness: Remember that only nil and false are falsey in Ruby
- Employ loop abstractions: Use iterators and enumerable methods instead of traditional loops
- Use blocks for temporary context: Create methods that yield to blocks for localized behavior
- Handle exceptions appropriately: Use begin/rescue for error conditions, not control flow
- Keep control structures flat: Avoid deeply nested conditional structures
Methods and Functions
- Name methods clearly: Choose names that reveal intent and follow Ruby conventions
- Keep methods focused: Create methods that do one thing well
- Consider method visibility: Use private and protected appropriately to hide implementation details
- Use keyword arguments: Employ named parameters for complex method signatures
- Set sensible defaults: Provide default argument values to simplify method calls
- Return consistent types: Ensure methods return predictable value types
- Use bang methods properly: Add ! suffix only when methods modify their receiver in place
- Follow query method conventions: End predicate methods with ? when they return boolean values
- Override methods thoughtfully: Consider the impact on inheritance when replacing methods
- Document method behavior: Make clear what methods do, especially their return values
Classes and Objects
- Follow naming conventions: Use CamelCase for classes and modules
- Keep initialization simple: Create focused, clear constructors
- Implement sensible equality: Define == and other comparison operators appropriately
- Use attr_accessor family: Prefer accessors over direct instance variable access
- Leverage class methods: Create class-level functionality when instance state isn’t needed
- Consider value objects: Create immutable objects when appropriate
- Provide meaningful to_s methods: Override to_s for useful string representations
- Use self explicitly when needed: Make it clear when you’re referring to the current object
- Follow Law of Demeter: Limit object interaction to immediate collaborators
- Keep class definitions focused: Create classes with a single, clear responsibility
Modules and Mixins
- Use modules for namespacing: Organize related classes under descriptive namespaces
- Create focused mixins: Design modules that provide a coherent set of related methods
- Understand include vs. extend: Know when to add module methods to instances vs. classes
- Follow mixin conventions: Name and structure mixins according to Ruby idioms
- Use modules for shared behavior: Extract common functionality into reusable modules
- Leverage Ruby’s standard mixins: Use built-in modules like Enumerable and Comparable
- Be cautious with method conflicts: Consider naming collisions when including multiple modules
- Implement required methods: Document and implement methods that mixins depend on
- Use module hooks: Leverage included and extended hooks for setup when needed
- Prefer composition to inheritance: Consider modules and delegation before subclassing
- Understand method_missing: Use method_missing for dynamic method handling
- Create methods dynamically: Use define_method for programmatically defined methods
- Be cautious with eval: Use safer alternatives to eval when possible
- Leverage class_eval and instance_eval: Understand when to use each for metaprogramming
- Create internal DSLs thoughtfully: Design domain-specific languages with clarity in mind
- Consider respond_to_missing?: Implement alongside method_missing for interface consistency
- Use hooks for class customization: Leverage inherited, included, and other callbacks
- Understand const_missing: Use for dynamic constant resolution when appropriate
- Document metaprogramming clearly: Make dynamic behavior obvious to code readers
- Balance magic with maintainability: Use metaprogramming judiciously where it adds value
Blocks and Iterators
- Use blocks for callbacks: Pass code blocks to methods for customizable behavior
- Create methods that yield: Design methods that call blocks at appropriate points
- Understand block binding: Know how blocks capture their surrounding context
- Pass blocks explicitly: Use &block parameter for passing blocks to other methods
- Convert blocks to procs: Use block_given? and yield for flexible method design
- Create custom iterators: Design methods that incrementally yield values
- Use blocks for resource management: Employ blocks for ensuring cleanup operations
- Consider lambda vs. proc: Understand the subtle differences between lambda and proc
- Use block return values: Capture and use values returned from blocks
- Chain enumerable methods: Combine collection operations for expressive transformations
Regular Expressions
- Use literal syntax: Create regexes with // notation for better readability
- Leverage capture groups: Use parentheses to capture matched portions of strings
- Apply appropriate modifiers: Use i, m, and other modifiers to control matching behavior
- Name captures for clarity: Use named capture groups for self-documenting patterns
- Test regex performance: Be aware of potential inefficiencies in complex patterns
- Anchor patterns appropriately: Use ^ and $ to match string boundaries when needed
- Use character classes effectively: Employ built-in and custom character classes
- Balance complexity with readability: Break complex patterns into manageable pieces
- Understand greedy vs. lazy matching: Control matching behavior with ? quantifier
- Leverage regex in string methods: Use patterns with gsub, scan, and other string methods
Managing Dependencies
- Require libraries intentionally: Load only what you need when you need it
- Understand load path mechanics: Know how Ruby finds required files
- Use relative requires appropriately: Employ require_relative for files in the same project
- Manage gem dependencies: Specify version constraints for external dependencies
- Create modular designs: Structure code to minimize coupling between components
- Follow dependency injection principles: Pass collaborators rather than creating them internally
- Consider autoloading: Use autoload for on-demand loading of less-used components
- Structure project directories consistently: Follow Ruby community conventions for file organization
- Leverage RubyGems effectively: Understand gem packaging and distribution
- Document dependencies clearly: Make required libraries and versions explicit
Testing Ruby Code
- Adopt test-driven development: Write tests before implementing functionality
- Choose appropriate test frameworks: Select RSpec, Minitest, or other tools based on needs
- Create focused test cases: Test one behavior per example
- Use descriptive test names: Make test intention clear from their names
- Set up test data thoughtfully: Create minimal, focused test fixtures
- Test edge cases: Include boundary conditions and error scenarios in tests
- Mock external dependencies: Isolate tests from external services when appropriate
- Maintain test readability: Make tests clear enough to serve as documentation
- Focus on behavior, not implementation: Test what code does, not how it does it
- Run tests automatically: Set up continuous integration for ongoing verification
Key Takeaways
- Embrace Ruby’s expressiveness: Use the language’s features to write concise, readable code
- Follow community conventions: Adhere to Ruby idioms for naming, structure, and style
- Value code clarity: Prioritize readability and maintainability over cleverness
- Master collections and blocks: Understand Ruby’s powerful abstractions for data processing
- Design object-oriented systems: Use classes, modules, and mixins to create maintainable code
- Use metaprogramming judiciously: Apply dynamic techniques where they add significant value
- Test thoroughly: Develop comprehensive test suites to verify behavior
- Leverage the standard library: Use Ruby’s built-in tools instead of reinventing solutions
- Manage dependencies wisely: Control how your code interacts with external libraries
- Write for other programmers: Create code that clearly communicates intent to humans