<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Reinhardt.io</title>
    <description>Ross Reinhardt&apos;s site.</description>
    <link>http://reinhardt.io/</link>
    <atom:link href="http://reinhardt.io/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Thu, 02 Oct 2025 16:20:29 +0000</pubDate>
    <lastBuildDate>Thu, 02 Oct 2025 16:20:29 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Leveraging Documentation for AI Tools</title>
        <description>&lt;p&gt;Memories of the past prepare us for the future. If every day we had to re-learn how to tie our shoes, brush our teeth, walk, and a million other things we’ve learned over the years then it would be hard for us to make any progress beyond that. Using AI coding tools can feel a bit like this today, where every new feature or bug fix requires the tool to re-learn how the codebase is structured, how a particular class or method works, what kinds of preferences you have, what CLI tools it has at its disposal and when they are helpful. The tool ends up spending quite a bit of work just to acquire the context it needs before it even gets to making meaningful changes.&lt;/p&gt;

&lt;p&gt;But like so many things, if you look at it in the right light these problems aren’t new. When a new developer starts work on a new codebase or in an area of the code they haven’t worked in recently, they too need to re-acquire the context needed before they begin work. Whether human or AI tool, we need these connections to the past to understand the future.&lt;/p&gt;

&lt;p&gt;When using AI tools (as without) lost context costs you time and money. The more research the AI tool has to do to learn your code structure the more money you’ll be spending in tokens that could have been used for more productive ends. If the tool then forgets what it’s learned from session to session (or even just when compacting memory) the time and money you spent on research is lost for any future use.&lt;/p&gt;

&lt;p&gt;To help solve the human problem, we work to make our code easy to understand and add documentation to aid in the journey; documenting patterns and calling out pitfalls. I would put forth that we can leverage this same solution for AI and in the process help both machine and human get the knowledge and context they need faster.&lt;/p&gt;

&lt;h2 id=&quot;overall-strategy&quot;&gt;Overall Strategy&lt;/h2&gt;

&lt;p&gt;We want our documentation to be there when it’s needed without being too heavy to consume up front. We could place all of our documentation in a single file (perhaps the main README) but that file is going to get really big really fast. You or the AI tool are going to need to sort through lots of irrelevant information to find what you are looking for and this gets expensive fast in either case. Instead, we want to make our documentation discoverable in the moment of need:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Inline Documentation&lt;/strong&gt;: this will be our first line of defence. Documenting public methods and classes is going to be most discoverable and likely to stay up to date since it’s right next to the code it’s describing.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;README for the project&lt;/strong&gt;: This will serve as our main entry point. We’ll import it into tool specific files (like CLAUDE.md). It will contain references to other places things can be found in our documentation as well as describe our main documentation strategy.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;READMEs in subdirectories&lt;/strong&gt;: Sometimes subsystems have things we want to describe that are too specific for the main README but too general for in-inline documentation. For these, we can utilize READMEs in subdirectories that contain information specific to that area of the code.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;docs/ directory&lt;/strong&gt;: In many projects, you might choose to have a separate docs/ directory containing things like style guides and other general documentation. To make use of these, we can link back to them in the main README so they can be referenced as needed.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;~/ENGINEERING.md&lt;/strong&gt;: This file will be used to store your own personal engineering preferences so that AI codes more in your style. It’s also a nice chance to take stock of what principles are important to you. It can be included in local tool specific memory files like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.claude/CLAUDE.md&lt;/code&gt; to make the tools aware.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Project File&lt;/strong&gt;: When working on a larger project, starting a file to contain your objective and progress can help AI stay focused on the task at hand and remember relevant information along the way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at these some more detail!&lt;/p&gt;

&lt;h2 id=&quot;inline-documentation&quot;&gt;Inline Documentation&lt;/h2&gt;

&lt;p&gt;Inline documentation has been a best practice for a long time already to help share context with anyone reading your code; using AI tools only makes it more relevant. For our strategy it’s going to be our first line of defence. Any machine or human looking at the code is going to have that documentation available right there making it very discoverable. Since it’s right next to the code it’s discussing, it’s also much more likely to be kept up to date as the code changes.&lt;/p&gt;

&lt;p&gt;As with any good inline documentation, avoid just describing the implementation but instead focus on documenting usage, examples, and gotchas. The documentation can also help point to other relevant classes using things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@see&lt;/code&gt; tags in Yardoc.&lt;/p&gt;

&lt;p&gt;Not only can AI make use of the documentation to get context faster, it can also help keep the documentation up to date and appropriately document new public methods and classes!&lt;/p&gt;

&lt;p&gt;We can make sure AI is aware of and uses inline documentation by making a note of it in our memory strategy described in the main README file that we’ll look at in a little more detail next.&lt;/p&gt;

&lt;h2 id=&quot;project-readme&quot;&gt;Project README&lt;/h2&gt;

&lt;p&gt;We’ve been adding READMEs to our projects forever to help engineers get up and running quicker. We don’t need new tool specific solutions like CLAUDE.md, we just need make sure our AI tools know where to look for this foundational document!&lt;/p&gt;

&lt;p&gt;When using Claude for example, this can be accomplished by importing the README into your project’s CLAUDE.md file like:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@README.md
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Underneath that, you can still add anything that might be relevant for that specific tool in it’s tool file; but otherwise we’re now free to keep general project information in the main README where it can be used by any human or AI tool.&lt;/p&gt;

&lt;p&gt;One thing that might be a helpful addition to the README is a specific section for AI tools to store their context in. Some of the things it might want to store might not make as much sense for a larger audience, so we can create a section of the README called something like “AI Context Notes” and then inform AI that this is the section it should feel free to store context in for later reference.&lt;/p&gt;

&lt;p&gt;The README should list common commands to do things like start the server, run the tests, etc which also adds these things to AI’s repertoire. It can contain links to other documentation to make it more discoverable. It can also contain high-level descriptions of the architecture of the app and the major objects in it.&lt;/p&gt;

&lt;p&gt;Let’s look at one possible example.&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# My App Name&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## Overview&lt;/span&gt;
Brief description of the app

&lt;span class=&quot;gu&quot;&gt;## Getting Started&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; How to get the project up and running

&lt;span class=&quot;gu&quot;&gt;## Common Commands&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Running Tests&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;bash
&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;# Run all tests&lt;/span&gt;
bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec

&lt;span class=&quot;c&quot;&gt;# Run a specific test&lt;/span&gt;
bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec spec/path/to/test_file.rb

&lt;span class=&quot;c&quot;&gt;# Run tests with specific line number&lt;/span&gt;
bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec spec/path/to/test_file.rb:42
&lt;span class=&quot;p&quot;&gt;```&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Starting Server&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;bash
&lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rails server
&lt;span class=&quot;p&quot;&gt;```&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## Architecture &amp;amp; Patterns&lt;/span&gt;
In this section, describe the major systems and objects as needed to give an overview of the app&apos;s architecture.

&lt;span class=&quot;gu&quot;&gt;## Related Documentation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**[style_guide_overview.md](docs/style_guide/style_guide_overview.md)**&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**[engineering_practices.md](docs/style_guide/engineering_practices.md)**&lt;/span&gt;: Development workflow and best practices
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**[ui_library.md](docs/style_guide/ui_library.md)**&lt;/span&gt;: UI components and design system

&lt;span class=&quot;gu&quot;&gt;## AI Context Notes&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Memory Discovery Protocol&lt;/span&gt;
When working in any directory:
&lt;span class=&quot;p&quot;&gt;1.&lt;/span&gt; Look for inline documentation on the method or class
&lt;span class=&quot;p&quot;&gt;2.&lt;/span&gt; Look for README.md in current directory first
&lt;span class=&quot;p&quot;&gt;3.&lt;/span&gt; Check parent directories up to project root for additional context
&lt;span class=&quot;p&quot;&gt;4.&lt;/span&gt; Focus on &quot;AI Context Notes&quot; sections for AI-specific guidance
&lt;span class=&quot;p&quot;&gt;5.&lt;/span&gt; Reference @docs/style_guide/ files for coding standards
&lt;span class=&quot;p&quot;&gt;6.&lt;/span&gt; Update README files when learning new patterns

&lt;span class=&quot;gu&quot;&gt;### Memory System Architecture&lt;/span&gt;
This project uses a hierarchical memory system:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Inline Documentation**&lt;/span&gt;: Documentation on public methods and classes describes usage and examples
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Project Memory**&lt;/span&gt;: This README.md serves both humans and AI tools
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**User Memory**&lt;/span&gt;: &lt;span class=&quot;sb&quot;&gt;`~/.claude/CLAUDE.md`&lt;/span&gt; for personal preferences
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Directory Memory**&lt;/span&gt;: README.md files in subsystem directories
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Memory Imports**&lt;/span&gt;: Use &lt;span class=&quot;sb&quot;&gt;`@path/to/import`&lt;/span&gt; syntax for modular organization

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is really just an example and you should definitely make it your own in your own project. Be sure to consider what would be helpful to both humans and any potential AI tools. The cleaner and more organized and informative you can make it, the better for everyone!&lt;/p&gt;

&lt;p&gt;The sections on memory discovery and architecture in the README are critical to what we are trying to accomplish. These sections make AI tools aware of where it can obtain memory files and the information it needs.&lt;/p&gt;

&lt;h2 id=&quot;subsystem-readme&quot;&gt;Subsystem README&lt;/h2&gt;

&lt;p&gt;As your app grows, you will likely get to a point where whole subsystems of your app may take quite a bit to explain on their own. Or potentially that part of the app contains a specific pattern you would like to see followed (like, for example, a directory containing web-components for the UI library). In cases like these, you can choose to create a README in that subdirectory. Here’s a quick example for one in the Web-components directory:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# Web Components&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Web components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/Web_Components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
make use of the browser native &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;customElements API&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; to create custom elements to be used on a page just like standard HTML elements (or, in some cases, to extend existing HTML elements).

&lt;span class=&quot;gu&quot;&gt;## Structure of this Directory&lt;/span&gt;

In this directory, you will find subdirectories that contain the JS and CSS to
define an individual component. There will also be a README file in that
directory with a description and examples of usage.

&lt;span class=&quot;gu&quot;&gt;## Creating a new component&lt;/span&gt;

If you are creating a new component, you will need to import it&apos;s files in the
index.js and index.scss files found in this top level directory.

&lt;span class=&quot;gu&quot;&gt;## AI Context Notes&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Some note based on something AI learned while implementing a web component
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And here’s a possible example for a complicated subsystem:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# Payment Processing&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## Overview&lt;/span&gt;
This module contains all code that pertains to the processing of payments (for plans, new products, etc).

&lt;span class=&quot;gu&quot;&gt;## Quick Start&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Key files and their purposes
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Common operations
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Where to find examples

&lt;span class=&quot;gu&quot;&gt;## Architecture &amp;amp; Patterns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; All payment processors should respond to &lt;span class=&quot;sb&quot;&gt;`#process_payment`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; There is a shared spec example testing the interface for a payment processor in &apos;spec/shared_examples/payment_processor.rb&apos;

&lt;span class=&quot;gh&quot;&gt;# Related Documentation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Links to @docs/ files
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; External resources

&lt;span class=&quot;gu&quot;&gt;## AI Context Notes&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Recent Patterns Discovered&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Pattern: New payment processors should use single table inheritance from the base PaymentProcessor

&lt;span class=&quot;gu&quot;&gt;## Recent Changes&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; We now explicitly round down to the nearest cent when calculating payments
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;integrating-existing-documentation&quot;&gt;Integrating Existing Documentation&lt;/h2&gt;

&lt;p&gt;Often a project will have other places documentation is stored and if you have that, let’s make sure it’s more discoverable by human engineers and AI by providing links! In some of the examples I referenced a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docs/&lt;/code&gt; directory. Perhaps in your project this directory contains style guide information as well as descriptions of various major systems. If you have this, be sure to place links to them in the readme that’s most appropriate so that they can be discovered and leveraged or updated as needed. You’ve already put the work in to create that documentation, make sure you’re getting all the use you can out of it!&lt;/p&gt;

&lt;p&gt;By linking to your coding conventions and style guide docs in your project README, you’re helping your tools stick to the agreements you’ve made as a team and therefore reducing the amount of re-work you’ll need to do at the end.&lt;/p&gt;

&lt;h2 id=&quot;your-engineering-philosophy&quot;&gt;Your Engineering Philosophy&lt;/h2&gt;

&lt;p&gt;Writing code isn’t a purely mechanical exercise, you as the author leverage your craft and experience to make it really shine. If you’re using an AI tool, you’ve experienced how much time you can lose trying to explain to it why it should favor one approach over another; or you’ve experienced the frustration of needing to manually clean up a less than optimal implementation from AI so that it matches your definition of good code.&lt;/p&gt;

&lt;p&gt;To help avoid this round and round, we can help the tool better understand how to code &lt;em&gt;in your style&lt;/em&gt; by creating an ENGINEERING.md that contains notes on all the things that are important to YOU as an individual engineer (and that AI can add to as it learns more about you). It can also provide an interesting point of reflection for you as you consider what your engineering philosophy is.&lt;/p&gt;

&lt;p&gt;This can be a hard one to put together; I experienced a bit of writer’s block at the beginning. To help get me started I explained what I wanted to create to AI and asked it to interview me about my approach to engineering and what patterns were important to me, then assemble the results into an initial ENGINEERING.md. Doing this gave me a starting point, but I’m still not happy with the representation and want to go back and spend more time crafting this file in the future. Here’s an example of some of what is in my (far from finished) ENGINEERING.md:&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;gh&quot;&gt;# Ross Reinhardt - Engineering Philosophy&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;## Core Principles&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Object-Oriented Design First&lt;/span&gt;
I strongly favor proper object-oriented programming over procedural patterns. I actively avoid service objects as they become dumping grounds for procedural logic that obscures domain objects and concepts that should be modeled explicitly.

&lt;span class=&quot;gu&quot;&gt;### Interface-Driven Development&lt;/span&gt;
I focus on the interfaces between objects and the messages being sent. I prefer &quot;tell, don&apos;t ask&quot; patterns - instead of querying an object and updating its state, I send it a message about what happened (e.g., &lt;span class=&quot;sb&quot;&gt;`job_updated`&lt;/span&gt;) and let it handle the response.

&lt;span class=&quot;gu&quot;&gt;### Command/Query Separation&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Query methods**&lt;/span&gt; (return values) should have no side effects
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Command methods**&lt;/span&gt; (side effects) should not return values we depend on
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Command methods often return &lt;span class=&quot;sb&quot;&gt;`self`&lt;/span&gt; to avoid accidental dependence on return values

&lt;span class=&quot;gu&quot;&gt;## Code Organization &amp;amp; Patterns&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Domain Modeling&lt;/span&gt;
When encountering business logic, I ask &quot;who would I send this message to?&quot; to identify the appropriate object. If no existing object fits, I look for new domain objects that emerge from the problem space.

&lt;span class=&quot;gu&quot;&gt;### Rails/Ruby Preferences&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Vanilla Rails/Ruby**&lt;/span&gt;: Stick to standard patterns whenever possible
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Callbacks**&lt;/span&gt;: Use model callbacks with async processing to avoid blocking saves
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Concerns**&lt;/span&gt;: Only use for clear, shared concepts (e.g., &lt;span class=&quot;sb&quot;&gt;`Archivable`&lt;/span&gt;). Never as junk drawers to hide code
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Helpers**&lt;/span&gt;: Rarely used, only for generic, reusable frontend utilities
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Presenters**&lt;/span&gt;: For complex page-level view logic
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Decorators**&lt;/span&gt;: Use SimpleDelegator for context-specific object behavior

&lt;span class=&quot;gu&quot;&gt;### Architecture Guidelines&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Follow Sandi Metz&apos;s Rules for Developers
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Watch for Law of Demeter violations
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Prefer dependency injection to reduce coupling
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Avoid &lt;span class=&quot;sb&quot;&gt;`stub_any_instance_of`&lt;/span&gt; and &lt;span class=&quot;sb&quot;&gt;`stub_chain`&lt;/span&gt; - they indicate coupling issues

&lt;span class=&quot;gu&quot;&gt;## Testing Philosophy&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Test-Driven Development&lt;/span&gt;
Always lead with tests, whether debugging or building new features. Start with &quot;outside-in&quot; approach:
&lt;span class=&quot;p&quot;&gt;1.&lt;/span&gt; Begin with failing feature test
&lt;span class=&quot;p&quot;&gt;2.&lt;/span&gt; Follow exceptions to lower levels (request specs, unit tests)
&lt;span class=&quot;p&quot;&gt;3.&lt;/span&gt; Work back &quot;out&quot; to ensure higher-level tests pass
&lt;span class=&quot;p&quot;&gt;4.&lt;/span&gt; Repeat cycle

&lt;span class=&quot;gu&quot;&gt;### Test Organization&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Feature specs**&lt;/span&gt;: Comprehensive but expensive - use sparingly for smoke testing happy paths
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Request specs**&lt;/span&gt;: Integration-style testing for edge cases, preferred over controller specs
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Unit tests**&lt;/span&gt;: Abundant and cheap for testing individual objects and edge cases
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Test pyramid**&lt;/span&gt;: More unit tests at base, fewer integration tests, minimal feature tests at top

&lt;span class=&quot;gu&quot;&gt;### Testing Patterns&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Use &lt;span class=&quot;sb&quot;&gt;`describe`&lt;/span&gt; instead of &lt;span class=&quot;sb&quot;&gt;`RSpec.describe`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Keep nesting minimal - prefer flat structure over deep context nesting
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Factory preferences: &lt;span class=&quot;sb&quot;&gt;`build`&lt;/span&gt; &amp;gt; &lt;span class=&quot;sb&quot;&gt;`build_stubbed`&lt;/span&gt; &amp;gt; &lt;span class=&quot;sb&quot;&gt;`create`&lt;/span&gt; (only when necessary)
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Integration specs**&lt;/span&gt;: No mocks/stubs - test end-to-end system behavior
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Unit specs**&lt;/span&gt;: Mock other objects, use as refactoring opportunity
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Duck typing**&lt;/span&gt;: Create shared examples for any role-playing objects
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Use &lt;span class=&quot;sb&quot;&gt;`instance_double`&lt;/span&gt; over generic mock objects

&lt;span class=&quot;gu&quot;&gt;### Sandi Metz Testing Guidelines&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Incoming queries**&lt;/span&gt;: Test expected results, don&apos;t test outgoing queries
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Incoming commands**&lt;/span&gt;: Test direct public side effects
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Outgoing commands**&lt;/span&gt;: Test that expected messages are sent
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Never assert on messages an object sends to itself

&lt;span class=&quot;gu&quot;&gt;## Code Quality Standards&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Review Criteria&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Code is easy to understand and open to change
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; No Law of Demeter violations
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Comprehensive test coverage
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Rubocop compliance
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Public methods documented with YARD (markdown format)
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Classes have top-level documentation

&lt;span class=&quot;gu&quot;&gt;### Documentation Philosophy&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**YARD Documentation**&lt;/span&gt;: All public methods and classes require YARD documentation using markdown format
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Focus on Usage**&lt;/span&gt;: Document &lt;span class=&quot;gs&quot;&gt;**what**&lt;/span&gt; the method is for and &lt;span class=&quot;gs&quot;&gt;**when**&lt;/span&gt; to call it, not &lt;span class=&quot;gs&quot;&gt;**how**&lt;/span&gt; it works
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Complete Tags**&lt;/span&gt;: Include proper &lt;span class=&quot;sb&quot;&gt;`@param`&lt;/span&gt; and &lt;span class=&quot;sb&quot;&gt;`@return`&lt;/span&gt; tags with types and descriptions
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Context over Implementation**&lt;/span&gt;: Provide high-level context for consumers, not implementation details
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Self-Documenting Code**&lt;/span&gt;: Code should be clear through names and structure
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Minimal External Docs**&lt;/span&gt;: Avoid external documentation that can become outdated
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Living Documentation**&lt;/span&gt;: Inline docs serve both humans and AI tools, update when understanding changes

&lt;span class=&quot;gu&quot;&gt;### Red Flags&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Procedural code stuffed into service objects
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Violations of Sandi&apos;s Rules
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Heavy coupling between objects
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Missing tests or poor test quality

&lt;span class=&quot;gu&quot;&gt;## Communication &amp;amp; Workflow&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### Git Practices&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Clear, informative commit history
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Follow commit template guidelines
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Top-line summary + JIRA ticket + detailed body
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Commits should tell a story of the change

&lt;span class=&quot;gu&quot;&gt;### Gem Selection&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Actively maintained and frequently updated
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; High usage stats indicating community adoption
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Well-documented with clear APIs

&lt;span class=&quot;gu&quot;&gt;### Performance Philosophy&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Optimize for readability first
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Only optimize for performance when absolutely necessary
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; If performance optimization maintains readability (e.g., eliminating N+1), implement it

&lt;span class=&quot;gu&quot;&gt;### Refactoring Approach&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Leave working code alone unless actively working in that area
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; When making changes, evaluate if refactoring would make the change easier
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Refactor to improve understandability
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Use refactoring as opportunity to reduce coupling

&lt;span class=&quot;gu&quot;&gt;## Development Tools &amp;amp; CLI Utilities&lt;/span&gt;

&lt;span class=&quot;gu&quot;&gt;### JIRA CLI&lt;/span&gt;
I have the JIRA CLI configured locally, which AI tools should leverage for:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Ticket Research**&lt;/span&gt;: Retrieve detailed information about any JIRA ticket including descriptions, acceptance criteria, and comments
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Context Gathering**&lt;/span&gt;: Understand requirements and background when starting work on a ticket
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**History Tracking**&lt;/span&gt;: Research the evolution of features or bug fixes through ticket discussions and updates
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Requirement Clarification**&lt;/span&gt;: Access stakeholder feedback and technical discussions captured in ticket comments

&lt;span class=&quot;gu&quot;&gt;### GitHub CLI (`gh`)&lt;/span&gt;
The GitHub CLI is available for comprehensive repository operations:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Pull Request Management**&lt;/span&gt;: Create, review, and research pull requests with full context
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Repository Insights**&lt;/span&gt;: Understand project history, contributors, and development patterns
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Issue Research**&lt;/span&gt;: Investigate related issues, discussions, and their resolution history
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Workflow Automation**&lt;/span&gt;: Streamline common GitHub operations like creating PRs with proper templates and metadata
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Code History**&lt;/span&gt;: Research commit history, blame information, and change context

&lt;span class=&quot;gu&quot;&gt;### AI Usage Patterns&lt;/span&gt;
AI tools should proactively use these utilities:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Initial Research Phase**&lt;/span&gt;: When starting any ticket, fetch JIRA details and research related PRs/issues
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Context Building**&lt;/span&gt;: Before making changes, understand the full history and requirements
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**PR Creation**&lt;/span&gt;: Use &lt;span class=&quot;sb&quot;&gt;`gh`&lt;/span&gt; to create well-formatted PRs with appropriate context and links
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Code Investigation**&lt;/span&gt;: Leverage both tools to understand why code exists and how it evolved
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;gs&quot;&gt;**Documentation**&lt;/span&gt;: Reference ticket numbers and PR links in commits and documentation

&lt;span class=&quot;gu&quot;&gt;#### RSpec Execution Preferences&lt;/span&gt;
When running RSpec tests, AI tools should optimize for token efficiency while maintaining useful feedback:

&lt;span class=&quot;gs&quot;&gt;**Default Command Pattern**&lt;/span&gt;:
&lt;span class=&quot;p&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;bash
&lt;/span&gt;bundle &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;rspec &lt;span class=&quot;nt&quot;&gt;--format&lt;/span&gt; progress &lt;span class=&quot;nt&quot;&gt;--deprecation-out&lt;/span&gt; /dev/null spec/path/to/file_spec.rb
&lt;span class=&quot;p&quot;&gt;```&lt;/span&gt;

&lt;span class=&quot;gs&quot;&gt;**Rationale**&lt;/span&gt;:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`--format progress`&lt;/span&gt; uses dots (&lt;span class=&quot;sb&quot;&gt;`.`&lt;/span&gt;) instead of full test descriptions, reducing output by 40-50%
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`--deprecation-out /dev/null`&lt;/span&gt; suppresses repeated deprecation warnings that add noise
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; For large test suites, this can save thousands of tokens per run
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Test descriptions are still visible in the spec file itself when context is needed

&lt;span class=&quot;gs&quot;&gt;**When to Use Documentation Format**&lt;/span&gt;:
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Debugging failing tests where you need context about what was being tested
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Exploring unfamiliar test suites to understand coverage
&lt;span class=&quot;p&quot;&gt;-&lt;/span&gt; Override with: &lt;span class=&quot;sb&quot;&gt;`bundle exec rspec --format documentation spec/path/to/file_spec.rb`&lt;/span&gt;

Both show the same essential information (2 passing examples), but progress format is dramatically more token-efficient for AI processing.
&lt;span class=&quot;p&quot;&gt;
---
&lt;/span&gt;
&lt;span class=&quot;ge&quot;&gt;*This document captures my core engineering philosophy and preferences. It serves as a guide for how I approach software development and can inform AI tools about my preferred patterns and practices.*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Toward the end of this file, you can see that I also use this opportunity to inform AI of the CLI tools I make use of in my workflow and when they could come in handy so that it knows it can leverage them as well (without me constantly needing to remind it).&lt;/p&gt;

&lt;p&gt;I’ve also noted some AI specific information at the end about how I want it to approach project, and ways it can run specs that reduce the output from the run to save tokens.&lt;/p&gt;

&lt;h2 id=&quot;project-files&quot;&gt;Project Files&lt;/h2&gt;

&lt;p&gt;The final type of memory I’m going to look at here is project focused memory files. For projects that span more than just some basic back and forth a project file can give be a helpful artifact to hold the context of that what it is you are trying to do. Without this, AI can start to forget what it was trying to accomplish or you can end up having to repeat a lot of context when a session gets interrupted.&lt;/p&gt;

&lt;p&gt;I’ve experimented with two different approaches for starting a project:&lt;/p&gt;

&lt;p&gt;The first approach would be to create the project document ahead of time giving an overview of the project, it’s goals, and a rough overview of what it would entail. This might be an artifact from your planning processes like a Project Brief document. When starting the project, you tell your AI tool to refer to that project document and save it’s context back to the file as it works.&lt;/p&gt;

&lt;p&gt;Another way would be just to tell AI to create a file documenting the project plan when you are conversing with it about a new project. It’ll create a new file and use it to manage it’s memory of the current project. If you are coming back to a new session later, you can refer to that document in your prompt to quickly regain the context you had before in a previous session.&lt;/p&gt;

&lt;p&gt;Using a project file on more complex projects can result in behavior that is more focused and less wandering. Once you are done with the project, you can just delete the project file as needed (or use it to have AI update related documentation/READMEs with relevant context before it’s deleted).&lt;/p&gt;

&lt;h2 id=&quot;checking-your-context&quot;&gt;Checking Your Context&lt;/h2&gt;

&lt;p&gt;With all this work on memory files, it helps to know what context your AI tools have at that moment. If you are using Claude, you can run the slash command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/context&lt;/code&gt; which will list in its output all the memory files it currently knows about. This can be helpful as you set things up to make sure the memory files are actually being picked up as you want them to be.&lt;/p&gt;

&lt;h2 id=&quot;remember-me-to-one-who-codes-there&quot;&gt;Remember Me to One Who Codes There&lt;/h2&gt;

&lt;p&gt;Using the strategies above for documentation that are mostly already best practices in our craft, we’ll be able to make use of AI tools in a focused, efficient, and tool agnostic manner.&lt;/p&gt;

&lt;p&gt;Maintaining this documentation and saving learnings will become something we do regularly to continue to add value. In a future post I’ll look at some custom commands that we could leverage to help us with obtaining and saving these learnings from our sessions. In the meanwhile, I can share this &lt;a href=&quot;https://thomaslandgraf.substack.com/p/claude-codes-memory-working-with&quot;&gt;post by Thomas Landgraf&lt;/a&gt; that I found inspirational and which discusses some custom prompts that can be used in these ways.&lt;/p&gt;

&lt;p&gt;Done right, the value we build with this documentation will be something that benefits not just us, but our team, and even future engineers working on the code base we never meet. By leveraging classic and proven forms of documentation, these memories will survive the hype cycle for any particular tool and prepare us for a future yet unwritten.&lt;/p&gt;

</description>
        <pubDate>Thu, 02 Oct 2025 15:29:00 +0000</pubDate>
        <link>http://reinhardt.io/ruby/ai/2025/10/02/leveraging-documentation-with-ai-tools.html</link>
        <guid isPermaLink="true">http://reinhardt.io/ruby/ai/2025/10/02/leveraging-documentation-with-ai-tools.html</guid>
        
        
        <category>ruby</category>
        
        <category>AI</category>
        
      </item>
    
      <item>
        <title>Creating a Custom Git Commit Template</title>
        <description>&lt;p&gt;If you write commit messages every day, you might find it helpful to have a
template to help you format your messages. I’ve found a template helpful for
including the names of co-authors I work with often so that I don’t have to look
them up every time and for making sure my commits follow the format expected by
the rest of my team. Setting one up is very easy!&lt;/p&gt;

&lt;p&gt;In this quick tip I’ll show you how to set a template for an individual
repository as well as globally.&lt;/p&gt;

&lt;h2 id=&quot;local-commit-template&quot;&gt;Local Commit Template&lt;/h2&gt;

&lt;p&gt;To set a template up locally, first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; to your project’s directory. Now create
a template file at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.git/.gitmessage.txt&lt;/code&gt;. The name of the template file can be
whatever you want it to be as long as it matches what we set in the
configuration later.&lt;/p&gt;

&lt;p&gt;The contents of the template can also be whatever you want it to be! Lines that
begin with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt; are comments and will not appear in the final commit message.&lt;/p&gt;

&lt;p&gt;Here is my current template. I pair often and so I include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Co-authored-by&lt;/code&gt; tags
for the people I commonly pair with. When I pair with one of them, I just
uncomment the line for them (remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#&lt;/code&gt;). In GitHub, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Co-authored-by&lt;/code&gt; tags
will make the pictures of collaborators appear on the commit with the main
author, which is pretty cool. On my current team we will include a link to the
story from our project tracking software in the commits, so I include a prompt
for that in my template. I also included a link to a handy blog post as a
reference for writing good commit messages.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;

Story:


# Co-authored-by: Bea &amp;lt;bea@acme.com&amp;gt;
# Co-authored-by: Abby &amp;lt;abby@acme.com&amp;gt;
# Co-authored-by: Juan &amp;lt;juan@acme.com&amp;gt;
# Guide: https://thoughtbot.com/blog/5-useful-tips-for-a-better-commit-message
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, set this file as your commit template for the current repository using:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git config commit.template .git/.gitmessage.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The next time you make a commit in that repo, (eg. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit&lt;/code&gt;) you will see
your template when Git opens your editor for creating the commit message.&lt;/p&gt;

&lt;h2 id=&quot;global-commit-template&quot;&gt;Global Commit Template&lt;/h2&gt;

&lt;p&gt;Recently, I’ve been working on several projects across several repos and I
didn’t want to have to set up the same template in each. Thankfully, we can set
up a global template!&lt;/p&gt;

&lt;p&gt;This time, we’ll put the template file in the home directory &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.gitmessage.txt&lt;/code&gt;
and then set our global git configuration to look for it:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git config --global commit.template ~/.gitmessage.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s it! Local git configuration supersedes global, so if you want to add
local templates on specific repos you still can and they will be used instead of
the global template.&lt;/p&gt;
</description>
        <pubDate>Thu, 03 Jun 2021 01:08:00 +0000</pubDate>
        <link>http://reinhardt.io/2021/06/03/creating-a-custom-git-commit-template.html</link>
        <guid isPermaLink="true">http://reinhardt.io/2021/06/03/creating-a-custom-git-commit-template.html</guid>
        
        
      </item>
    
      <item>
        <title>Stubbing and Mocking in MiniTest</title>
        <description>&lt;p&gt;When I started using MiniTest one of the things I struggled with was how to mock
and stub objects when constructing my tests. Here are a few patterns I’ve
collected over time and now find helpful when stubbing and mocking in MiniTest.&lt;/p&gt;

&lt;h2 id=&quot;basic-minitest-stub&quot;&gt;Basic MiniTest Stub&lt;/h2&gt;

&lt;p&gt;MiniTest comes with a way to stub a value for a test. Here is a classic example
stubbing time:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stub&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2012&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;14&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;utc&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;MyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;are_we_there_yet?&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;:now&lt;/code&gt; method on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Time&lt;/code&gt; object is stubbed so that it
always returns the same time while testing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyObject&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#stub&lt;/code&gt; takes a few arguments, the first is the method you wish to stub and the
second is the value it should return when it’s called. This second value can
also be an object that responds to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#call&lt;/code&gt; and if it does the return value is
the result of calling the object. Which leads us to a second pattern.&lt;/p&gt;

&lt;h2 id=&quot;turn-your-stub-into-a-spy&quot;&gt;Turn Your Stub Into a Spy&lt;/h2&gt;

&lt;p&gt;Since we can supply an object that responds to call for our stub, let’s turn our
stub into a “spy”. Spies not only return a canned answer but are concerned with
&lt;em&gt;how&lt;/em&gt; they are called. To do this, we can use a Ruby lambda. Inside of our
lambda we’ll make some assertions about the arguments the stubbed method is
called with.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Disabling Alerts&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;AlertsPause&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;args_checker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;1234qwfp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert_equal&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;team1234&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:group_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;no&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stub&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:disable_notifications&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args_checker&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;response&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;assert_match&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/paused alerts/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this example we are using a lambda to ensure that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable_notifications&lt;/code&gt;
gets called with the expected arguments. When the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable_notifications&lt;/code&gt; method
is called on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Connection&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;args_checker&lt;/code&gt; will be called with the arguments
passed to that method. Inside the lambda we use assertions to “spy” on the
caller of this method and make sure it passes us the arguments we are expecting.&lt;/p&gt;

&lt;p&gt;One other thing to note here, if you are using this approach and want to make
assertions on the result of something called within the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stub&lt;/code&gt; block, you have
to declare the local variable before the block for it to be available in the
scope outside of the block. In this example &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;response&lt;/code&gt; is used in this way; it
is declared and set to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nil&lt;/code&gt; at the beginning of the spec, assigned in the
block, and the used in an assertion at the end.&lt;/p&gt;

&lt;h2 id=&quot;minitestmock&quot;&gt;MiniTest::Mock&lt;/h2&gt;
&lt;p&gt;MiniTest::Mocks are object doubles you can use as a stand-in for some other
object. Their use is really simple:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MiniTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:fine?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;checker&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;EverythingsFineChecker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;checker&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;is_everything_fine?&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# =&amp;gt; true&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#expect&lt;/code&gt; to stub methods and set return values on our mock object.
If we want it to spy for us, we can call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;verify&lt;/code&gt; at the end to make sure that
the stub was actually called. The first argument of expect is the method to
stub, the second is the value to return, and the third is optional and can be an
array of arguments. If we want to stub a method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_speed&lt;/code&gt; that accepts an
argument of speed, we might do it like this for a speed of 55:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;car&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MiniTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;car&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:set_speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;car&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;return-a-minitestmock-from-a-stub&quot;&gt;Return a MiniTest::Mock from a Stub&lt;/h2&gt;
&lt;p&gt;You can combine stubs and mocks! In this example, a client is stubbed so that
it returns a mock client when it’s initialized. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mock_client&lt;/code&gt; is stubbing a
call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list_all&lt;/code&gt; to always return an empty array.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;mock_client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MiniTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;mock_client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:list_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stub&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock_client&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# do something using the client&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;using-standard-ruby-objects-as-test-doubles&quot;&gt;Using Standard Ruby Objects as Test Doubles&lt;/h2&gt;

&lt;p&gt;We can also use plain old Ruby in our test! One example of this is using a test
object created in the test as a stand-in for something else. Here we create a
mock client object that responds to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list&lt;/code&gt; with some pre-canned answers and then
use dependency injection to pass it into our job instead of one that might make
real requests to an API&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ProcessingTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MiniTest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestCase&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MockClient&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:)&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;item 1&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;item 2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;item 3&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;item 4&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Process Items&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;ProcessingJob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;client: &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MockClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;perform&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# assert something&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;redefine-methods-on-ruby-objects&quot;&gt;Redefine Methods on Ruby Objects&lt;/h2&gt;

&lt;p&gt;Here is a cool trick you might remember from &lt;a href=&quot;https://www.reinhardt.io/ruby/2020/06/22/the-return-of-the-eigenclass.html&quot;&gt;another one of my recent
posts&lt;/a&gt;,
using plain Ruby you can redefine a method on any object. We can use that in a
test to stub methods! Here is an example of stubbing some methods on an instance
of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;team&lt;/code&gt;, one of which raises an exception.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;team&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;name: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Engineering&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;active?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_member&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;TeamError&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;NotAuthorized&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;TeamFinder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;stub&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:engineering_team&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;team&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Do something&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# assert something&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In that example, team is stubbed so that it always returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;active?&lt;/code&gt;
and so that it will raise an exception when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;add_member&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The syntax is a little weird at first, but you’re likely familiar with defining
class methods like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;say_hello&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;hello&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is the same thing! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; in this case refers to the instance of class
representing the object and you are defining the “say_hello” method on it.&lt;/p&gt;

&lt;p&gt;Using this technique is pretty powerful because it lets you quickly stub methods
on any object and is especially handy when you have several methods to stub. The
cool part is, it’s just Ruby!&lt;/p&gt;

&lt;h2 id=&quot;use-the-tests-luke&quot;&gt;Use The Tests, Luke&lt;/h2&gt;

&lt;p&gt;There are some patterns I’ve found helpful when writing tests in MiniTest. If
you find that you are having to stub and mock a lot or that you are stubbing
things to return other stubbed things that return other stubbed things it could
mean that your class &lt;a href=&quot;https://en.wikipedia.org/wiki/Law_of_Demeter&quot;&gt;knows too much about other
objects&lt;/a&gt; in your system. I’ve
found it helpful to use this pain when I encounter it as an indicator that I
should re-consider some closely coupled objects and find ways to make them less
coupled.&lt;/p&gt;
</description>
        <pubDate>Tue, 25 May 2021 03:16:00 +0000</pubDate>
        <link>http://reinhardt.io/ruby/testing/2021/05/25/stubbing-and-mocking-in-minitest.html</link>
        <guid isPermaLink="true">http://reinhardt.io/ruby/testing/2021/05/25/stubbing-and-mocking-in-minitest.html</guid>
        
        
        <category>ruby</category>
        
        <category>testing</category>
        
      </item>
    
      <item>
        <title>Git Autostash</title>
        <description>&lt;h1 id=&quot;today-i-learned-git-rebase-autostash&quot;&gt;Today I Learned: git rebase –autostash&lt;/h1&gt;

&lt;p&gt;Rebasing is something I do often in Git. The most common time is when I’m updating my branch with the latest “main” branch; I’ll typically use a rebase strategy on pull to avoid creating extra merge commits and to help keep a clean history. Often I’ll have uncommitted changes in my worktree that need to be stashed before I can do a rebase so I do something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git stash
$ git pull --rebase . origin/main
$ git stash pop
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Well today, I was looking in the help documentation for rebase (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase -h&lt;/code&gt;) and stumbled on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--autostash&lt;/code&gt; flag. According to &lt;a href=&quot;https://git-scm.com/docs/git-pull#Documentation/git-pull.txt---autostash&quot;&gt;the docs&lt;/a&gt; it will:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Automatically create a temporary stash entry before the operation begins, and apply it after the operation ends.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nice! This is a nice shortcut for exactly what I was doing above! I can now do the same thing using:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git pull -r --autostash . origin/main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s also possible to set this up as a default mode for every rebase if you should desire in your git config using the &lt;a href=&quot;https://git-scm.com/docs/git-config#Documentation/git-config.txt-mergeautoStash&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;merge.autoStash&lt;/code&gt; variable&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git config merge.autoStash true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Wed, 31 Mar 2021 02:47:00 +0000</pubDate>
        <link>http://reinhardt.io/ruby/til/2021/03/31/git-autostash.html</link>
        <guid isPermaLink="true">http://reinhardt.io/ruby/til/2021/03/31/git-autostash.html</guid>
        
        
        <category>ruby</category>
        
        <category>TIL</category>
        
      </item>
    
      <item>
        <title>The Return of the Eigenclass</title>
        <description>&lt;p&gt;So many times once I’ve learned how to do something, I’ll find myself doing it but maybe not fully understanding what I’m doing. Ruby class method definitions are one of these things for me; I do it all the time but it wasn’t until recently that a light bulb went of and I reached a deeper understanding of what I was REALLY doing. Here is that journey, which started with looking for a way to stub methods in ruby tests.&lt;/p&gt;

&lt;h2 id=&quot;taking-a-shortcut&quot;&gt;Taking a Shortcut&lt;/h2&gt;

&lt;p&gt;While writing some tests in Minitest, I was looking for a way to stub methods on an instance of an object either because the method has side effects I want to avoid in my test or because I want to control the environment of the object under test. Let’s say we have two classes, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Race&lt;/code&gt;. We want to test &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Race&lt;/code&gt;, but don’t want all the runners to have to run the full marathon for this test. The implementation could look like:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_marathon&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is running a long way&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Race&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@runners&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:run_marathon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Martin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Lila&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Race&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Running this file gives you:&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ruby ./runners.rb
Martin is running a long way
Lila is running a long way
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Both runners are running the full race. You can “stub” a method on a ruby object by redefining it on the instance of the object. We’ll use this method to make Martin take a shortcut.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Runner hasn&apos;t changed...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Race&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Race hasn&apos;t changed...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Martin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Lila&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Make Martin take a shortcut&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;runner_m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;run_marathon&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is taking a shortcut&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Race&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running the file now shows Martin taking a shortcut&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ruby ./runners.rb
Martin is taking a shortcut
Lila is running a long way
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;bringing-the-class&quot;&gt;Bringing the Class&lt;/h2&gt;

&lt;p&gt;This is pretty cool! We are using plain old ruby techniques to stub &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#run_marathon&lt;/code&gt; for Martin so that he doesn’t have to run the full marathon! This is now my go-to stubbing method when writing tests in Minitest or TestUnit. But I recognized that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def object.method&lt;/code&gt; syntax from somewhere, hmm. Oh yeah! Defining a class method like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def self.method&lt;/code&gt;. This is something we all do every day! Let’s improve the API of our runner object to add a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::named&lt;/code&gt; class method we can use to initialize a new runner instead of calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::new&lt;/code&gt; directly.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# The rest of Runner...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Race&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# Race hasn&apos;t changed...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Martin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Lila&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Race&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Running this we get&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ruby ./runners.rb
Martin is running a long way
Lila is running a long way
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Great! Everything in Ruby is an object; here &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; is an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt;. We can use the same notation that we used to redefine a method on an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; to redefine a class method on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; because it is itself an instance of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; object.&lt;/p&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# No change...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Race&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# No change...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; (participant)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Martin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;Lila&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;no&quot;&gt;Race&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runner_m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner_l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;ruby ./runners.rb
Martin &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;participant&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; is running a long way
Lila &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;participant&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; is running a long way
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve redefined the class method to have “(participant)” after each of the names. What we did here was open up the “singleton” object &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; and change the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;named&lt;/code&gt; method. A singleton is a “pattern that restricts the instantiation of a class to one “single” instance” (&lt;a href=&quot;https://en.wikipedia.org/wiki/Singleton_pattern&quot;&gt;wikipedia&lt;/a&gt;); so there is and will only ever be one instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; representing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt;. When we first load our ruby program a single instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Class&lt;/code&gt; is created for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; and is accessed via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; constant. These single instances are also called “metaclass” or “eigenclass” (eigen - “own” or “individual”) which are also pretty cool sounding. I might actually watch something called “The Return of the Eigenclass” (would the return just be self? lol).&lt;/p&gt;

&lt;p&gt;Here, saying &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def Runner.named&lt;/code&gt; looks pretty weird. But when used inside a class it looks very familiar to many Rubyists. The way we initially defined &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::named&lt;/code&gt; is doing this very thing using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; (and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; just represents the singleton &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt;)&lt;/p&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# The rest...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It turns out, this is common enough that Ruby &lt;a href=&quot;https://ruby-doc.org/core-2.7.1/doc/syntax/modules_and_classes_rdoc.html#label-Singleton+Classes&quot;&gt;provides a notation for this&lt;/a&gt;. Instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def Runner.named&lt;/code&gt; we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; object&lt;/code&gt;. That’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt;&lt;/code&gt; followed by the singleton object.&lt;/p&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; (participant)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This leads us to the final step in the journey; understanding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; self&lt;/code&gt; syntax. I have often seen and understood it as an alternative to writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def self.method&lt;/code&gt;, but I never really felt comfortable with it.&lt;/p&gt;

&lt;p&gt;When we use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; object&lt;/code&gt; notation inside of the runner class we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; and so it becomes&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot; (participant)&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_marathon&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is running a long way&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So now it’s clear; what we are doing here is just opening up a singleton representing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; object so that we can add methods to it using that special Ruby syntax for doing so and just like we did with our more literal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def Runner.named&lt;/code&gt; example. Since we are actually inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; object, we can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self&lt;/code&gt; to access the singleton. Although the syntax is a little more intimidating at first, it’s not as scary once you understand what it’s representing.&lt;/p&gt;

&lt;h2 id=&quot;the-journeys-end&quot;&gt;The Journey’s End&lt;/h2&gt;

&lt;p&gt;This is what clicked for me! Just like redefining a method on an instance of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; (like we did to make Martin take a shortcut), we are “opening up” the singleton representing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; to define additional methods (class methods; or methods on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Runner&lt;/code&gt; singleton).&lt;/p&gt;

&lt;p&gt;After discovering this and feeling more familiar, I’ve actually started to prefer it. For one, you don’t have to type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;self.&lt;/code&gt; as a prefix for each of the class method names. It also keeps the class methods nicely grouped together. Where it really shines for me is when I want to define private class methods.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Runner&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;self&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;named&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;kp&quot;&gt;private&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;suffix&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot; (participant)&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_marathon&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; is running a long way&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We can use the private keyword just like we would in any other Ruby class! This came in handy when I had some complex Rails “scopes” I was defining (they are defined as class methods) and I wanted to break some of the logic out into their own methods for understandabilty (but keep them private, so that they weren’t exposed to the public API).&lt;/p&gt;

&lt;p&gt;We now have a great way to stub methods using plain old Ruby, a better understanding of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;class &amp;lt;&amp;lt; object&lt;/code&gt; notation used for accessing the singleton class of an object instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def self.method&lt;/code&gt;, and another great tool in our Ruby toolbox that we can use whenever we need to redefine a method on any instance of an object.&lt;/p&gt;
</description>
        <pubDate>Mon, 22 Jun 2020 15:33:00 +0000</pubDate>
        <link>http://reinhardt.io/ruby/2020/06/22/the-return-of-the-eigenclass.html</link>
        <guid isPermaLink="true">http://reinhardt.io/ruby/2020/06/22/the-return-of-the-eigenclass.html</guid>
        
        
        <category>ruby</category>
        
      </item>
    
      <item>
        <title>Git Strategy For Pair Programming</title>
        <description>&lt;p&gt;I’ve recently been doing a lot more pair programming than I have ever done. Most times we will be sharing a branch and will switch drivers by committing some code so the new driver can pull it down and take over. This works great, but it can start to lead to a messy commit history. After much experimentation I really like our current workflow and thought I’d share it! There are things here that apply even if you don’t pair program but will still help you tell a clear story for future developers with your git history.&lt;/p&gt;

&lt;h2 id=&quot;first-what-does-this-look-like-with-one-person&quot;&gt;First, What Does This Look Like With One Person?&lt;/h2&gt;

&lt;p&gt;A good place to start is with what my git workflow looks like when I’m working by myself. It’s important to me that when I’m done working on some code my git history tells future developers a clear and comprehensive story of why I did what I did so that when they &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git blame&lt;/code&gt; those lines of code what they find helps them understand why that code is the way it is. Because of that, when I’m done working on something I make sure that work is grouped logically into commits (or just a single commit in most cases) using an interactive rebase.&lt;/p&gt;

&lt;p&gt;Let’s say I have 3 commits on the branch I’m working on:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git log --oneline -n3
2b010a4a1f (HEAD -&amp;gt; hard-work) One last tweak
e7bf81daa9 Woops, fix spec
3a26300ae4 Fix the bug that caused X
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I have an initial bug fix commit that will have a descriptive commit message, but then I have two follow up commits with less than ideal temporary messages. When I am done with work I can do an interactive rebase using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase -i origin/master&lt;/code&gt;. This will drop me into the default editor I have configured in my shell with something that looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pick 3a26300ae4 Fix the bug that caused X
pick e7bf81daa9 Woops, fix spec
pick 2b010a4a1f One last tweak

# Rebase dd071b1302..2b010a4a1f onto dd071b1302 (3 commands)
#
# Commands:
# p, pick &amp;lt;commit&amp;gt; = use commit
# r, reword &amp;lt;commit&amp;gt; = use commit, but edit the commit message
# e, edit &amp;lt;commit&amp;gt; = use commit, but stop for amending
# s, squash &amp;lt;commit&amp;gt; = use commit, but meld into previous commit
# f, fixup &amp;lt;commit&amp;gt; = like &quot;squash&quot;, but discard this commit&apos;s log message
# x, exec &amp;lt;command&amp;gt; = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with &apos;git rebase --continue&apos;)
# d, drop &amp;lt;commit&amp;gt; = remove commit
# l, label &amp;lt;label&amp;gt; = label current HEAD with a name
# t, reset &amp;lt;label&amp;gt; = reset HEAD to a label
# m, merge [-C &amp;lt;commit&amp;gt; | -c &amp;lt;commit&amp;gt;] &amp;lt;label&amp;gt; [# &amp;lt;oneline&amp;gt;]
# .       create a merge commit using the original merge commit&apos;s
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c &amp;lt;commit&amp;gt; to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are some helpful instructions in the comment. What I’ll choose to do here is “fixup” the two commits I don’t want to keep separate which will make them a part of the one that I want to keep. I’ll designate the one I’m keeping by leaving it with “pick”. Additionally I have the option to reword that main commit if I change that “pick” to a “reword”. So now I’ll have:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pick 3a26300ae4 Fix the bug that caused X
f e7bf81daa9 Woops, fix spec
f 2b010a4a1f One last tweak
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After doing that, all my work is now in one unified commit with a helpful message for future developers to discover.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git log -n3
commit 3735ddb5e50316869ec3b96974dd2babee0801f1 (HEAD -&amp;gt; hard-work)
Author: My Name &amp;lt;me@gmail.com&amp;gt;
Date:   Thu Nov 7 09:35:18 2019 -0500

    Fix the bug that caused X

    Story: link to story

    More information on why this change was made

# Other unrelated commits
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That works, but git has some features that can help us with this workflow to avoid having to name commits that we have to manually fixup later. There are flags you can pass to &lt;a href=&quot;https://git-scm.com/docs/git-commit&quot;&gt;git-commit&lt;/a&gt; to tell it that you either want to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--fixup&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--squash&lt;/code&gt; this commit automatically. For those commits I added after the main one, I could have done &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit --fixup 3735ddb5e50316&lt;/code&gt; where that numbers and letters are the SHA of the main commit. No throw away commit message needed! If I had done this, I would have a git history that looked like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ git log --oneline -n3
4a5e93e675 (HEAD -&amp;gt; hard-work) fixup! Fix the bug that caused X
95e48fd49a fixup! Fix the bug that caused X
3735ddb5e5 Fix the bug that caused X
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Awesome, this signals our intention for these commits as fixups. The real magic comes in when we rebase using the autosquash option &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase -i --autosquash origin/master&lt;/code&gt;. Now, since we have already signaled that those commits will be fixups, the interactive page we are presented with will already list those commits as fixups!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pick 3735ddb5e5 Fix the bug that caused X
fixup 95e48fd49a fixup! Fix the bug that caused X
fixup 4a5e93e675 fixup! Fix the bug that caused X
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sometimes, I’ll skip the middle step entirely and if I make a change after making my initial commit I’ll just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git commit --amend&lt;/code&gt; to immediately make my changes a part of the original commit. Note that if you are using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--amend&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rebase&lt;/code&gt; you will need to force push your branch using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git push -f&lt;/code&gt; and if someone is working with you on this branch, they will not be able to just pull the branch down using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; (but more on that next).&lt;/p&gt;

&lt;p&gt;That is my basic daily workflow in Git. There are great resources out there with more detail on &lt;a href=&quot;https://thoughtbot.com/blog/autosquashing-git-commits&quot;&gt;autosquashing&lt;/a&gt;, &lt;a href=&quot;https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html&quot;&gt;writing good commit messages&lt;/a&gt;, and &lt;a href=&quot;https://thoughtbot.com/blog/better-commit-messages-with-a-gitmessage-template&quot;&gt;setting up templates for your commit messages&lt;/a&gt; that I have found helpful toward the end of effectively telling a story with my git history.&lt;/p&gt;

&lt;h2 id=&quot;but-what-about-pairing-on-the-same-branch&quot;&gt;But What About Pairing On The Same Branch?&lt;/h2&gt;

&lt;p&gt;Whether you are actually pairing or if you are just reviewing someone else branch locally, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--amend&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rebase&lt;/code&gt; can change how you need to approach retrieving the latest work from the shared repository. The reason is because those commands “re-write history” in git. If someone else has an old copy of your branch locally, this can cause problems because git now can’t figure out what it should do because the two histories are different. Typically, re-writing history is considered a “Bad Thing” for any shared branches because git can no longer figure out accurately where the common histories diverge. Assuming though that we are working on a PR that hasn’t been merged into a larger shared branch (like “master”) it’s our chance to craft the history before it’s set in stone by being merged and shared.&lt;/p&gt;

&lt;p&gt;When pairing, if we want to switch drivers what options do we have?&lt;/p&gt;

&lt;p&gt;Option 1 is to use fixup commits as shown above. Using this option allows us to commit a chunk of work with the intention to clean it up later and we can still &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; the branch down until it’s rebased. One time this is particularly useful is when you are pairing and someone has to step away. In that case, it can be helpful to put the changes in a fixup commit so that when they come back they can clearly see what has changed. This can also come in handy during an asynchronous code review when you make a change based on some feedback you receive. If you put it in a fixup commit, it’s clear to the reviewer both what changes where made and the intention to clean up the commit when the collaboration is finished.&lt;/p&gt;

&lt;p&gt;But having a bunch of fixup commits would start to get annoying and make merge conflict resolution more difficult if that was our only tool. Option 2 is to just go straight to using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--amend&lt;/code&gt; on the main commit when it’s time to switch drivers. Here is the key though, the new driver won’t be able to just pull the branch down using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git pull&lt;/code&gt; because the commit was changed. What we really want to do is just reset what we have locally to be the latest version in the remote repository and we can do just that! Here is what that looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Time to switch drivers.
# Let&apos;s assume you already have already made the first commit and we are adding to it.
# On the current drivers machine run:
git ci --amend
# Change the message or don&apos;t and save
git push -f

# Now on the new driver&apos;s machine on this same branch
git fetch
git reset --hard origin/branch-name-here
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s all there is to it! It’s key that you make sure to fetch before resetting though to make sure you have the latest they just pushed up. There are a couple of shortcuts you can use here. First, if you don’t want to edit the commit message now you can skip that step using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git ci --amend --no-edit&lt;/code&gt;. If the upstream branch is configured for your current branch there is a shortcut there too. Instead of typing out the branch’s name you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@{u}&lt;/code&gt; (think u for “upstream”). Now we have:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# Current driver
git ci --amend --no-edit
git push -f

# New driver
git reset --hard @{u}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you need to set the upstream, you can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git branch -u origin/my-branch-name&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;once-upon-a-commit&quot;&gt;Once Upon A Commit&lt;/h2&gt;

&lt;p&gt;Those are some tricks and tips I’ve learned while trying to pair and still tell effective stories with my git history. Just remember, be careful when using commands that re-write history. They are great tools when you are working on a branch that is not branched off of by others but be aware that these tools are “sharp knives” and can cause a headache if you use them on a branch others are building off of (like master).&lt;/p&gt;

&lt;p&gt;If you are careful to craft your commit messages to tell a clear and comprehensive story, maybe you will be the future developer to be pleasantly surprised by a well written message and cohesive changeset when you &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git blame&lt;/code&gt; a mysterious line of code you are working so hard to understand and debug.&lt;/p&gt;
</description>
        <pubDate>Thu, 07 Nov 2019 00:00:00 +0000</pubDate>
        <link>http://reinhardt.io/2019/11/07/git-strategy-for-pair-programming.html</link>
        <guid isPermaLink="true">http://reinhardt.io/2019/11/07/git-strategy-for-pair-programming.html</guid>
        
        
      </item>
    
      <item>
        <title>Low Pain Data Migrations In Rails</title>
        <description>&lt;h3 id=&quot;in-search-of-a-good-way-to-update-existing-data-in-a-mature-rails-application&quot;&gt;&lt;em&gt;In search of a good way to update existing data in a mature Rails application&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;There have been many times where I’ve encountered the need to change some data in response to a change our application. As the project and team I’ve been working on have grown the need for a robust method to run these data migrations has as well.&lt;/p&gt;

&lt;p&gt;What I’m referring to as a “data migration” is just some code that makes changes to data in your system. It’s not making changes to the actual structure of your database, but changing the records in it. For example, maybe you had some data stored as an attribute on a model but now that same information is being moved out to a related record. That is to differentiate it from a “Rails migration” which is just the normal migration files that you use in your rails app for managing the state of your database.&lt;/p&gt;

&lt;p&gt;I’ve been looking for a way to do data migrations that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;runs automatically in most environments including developer’s local environments and non production environments to reduce burden imposed on the team&lt;/li&gt;
  &lt;li&gt;can run alongside production traffic without interruption&lt;/li&gt;
  &lt;li&gt;provides the option to run the migration on demand in production separate from deploys so that we could run the migration at an off time without having to perform a full deploy&lt;/li&gt;
  &lt;li&gt;allows the Rails migration to continue to work without updating if the code for the data migration changes or is removed&lt;/li&gt;
  &lt;li&gt;is easy to test&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rake tasks are a handy way to run some code in a testable way; what if we called a rake task from a migration? That seems like a good place to start! Putting the data migration in a rake task would also help isolate the rails migration from the data migration code itself (which in some cases might included calls to an external library that could break if the library’s API changed or we stopped using the library). It can be a pain to have to go back and update old migrations just because a method or a class changed names in your code years after the migration was run.&lt;/p&gt;

&lt;p&gt;Here is a first attempt at our rake task data migration:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateFooBarPermissionParity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say_with_time&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Creating parity between old and new verions of foo bar permission&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;permission:create_foo_bar_parity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Nothing to do&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;say&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;say_with_time&lt;/code&gt; are just helpful little methods that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ActiveRecord::Migration&lt;/code&gt; provides us to output information from our migration. If we tried to run the migration after the rake task had been removed, we would get a helpful error telling us that the task could not be found (but the migration would still break because of the missing task). Since running migrations is a normal part of a Rails application’s life cycle, this method has advantages over a stand alone rake task because we do not need to remember to run it manually in all environments; our data migration code would be run when the rails migrations are run as a normal part of the life cycle of your app.&lt;/p&gt;

&lt;p&gt;Putting the data in a temporary rake task has other advantages as well. If the need arises later to re-run the task to fix an issue we can call our task from the command line ad hoc. The task can also be used as a tool while you are working on the feature! Many times while working on a feature I might end up with my local data in a crazy state and having the rake task to call and get my data back in a particular state can be really helpful. For me the rake task has been easier to test as well; while you can write tests for migrations it feels more intuitive to me to write a spec for a rake task containing the data migration.&lt;/p&gt;

&lt;p&gt;Just using the rake task in the migration helps us meet several of the goals we had for our data migration! An important one to consider next is that when this data migration runs in production it should not interfere with production traffic. Since each rails migration runs in it’s own transaction by default, there is the potential that we could create locks in the database during a long running data migration that could tie up records in the database and have a noticeable effect our our users. That would happen because the production traffic would be forced to wait on any records it needed to access while our giant migration transaction finished. Yikes! I can say from experience that this leads to Bad Things :-). To avoid this it would be a good rule of thumb to disable the transaction that surrounds the rails migration so that we can handle adding transactions explicitly where they are needed for either data integrity or performance (care should be given though that the transactions are as small as possible if you need them at all). You can do this by adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;disable_ddl_transaction!&lt;/code&gt; to the top of your rails migration. Now we have:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateFooBarPermissionParity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;disable_ddl_transaction!&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say_with_time&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Creating parity between old and new verions of foo bar permission&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;permission:create_foo_bar_parity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Nothing to do&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;While it’s nice that we get a helpful error message if the rake task no longer exists and the Rails migration is run, most of the time a data migration is just needed to update the state of existing data in the database and will be completely removed when we are finished with it so it would be nice if we handled this common situation gracefully. We can handle this case by rescuing the exception and outputing a message from our rails migration. A missing rake task will raise a generic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeError&lt;/code&gt; which is not specific enough to target (we don’t want to rescue all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RuntimeError&lt;/code&gt;s), but what we can do is match on the message from the exception to make sure that it’s coming from a missing rake task:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateFooBarPermissionParity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;disable_ddl_transaction!&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say_with_time&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Creating parity between old and new verions of foo bar permission&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;permission:create_foo_bar_parity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RuntimeError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^Don&apos;t know how to build task/&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Rake task &apos;permission:create_foo_bar_parity&apos; did not run because it doesn&apos;t exist&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Nothing to do&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sometimes we would prefer to run a data migration at an off time in production just to play things safe (nobody likes to watch the app go up in flames). But we would still like it to run automatically in all of our other environments that are not production and might have less traffic and risk. For this purpose, we could check for an environment variable and if we find it we would skip running the rake task in that environment with a message reminding you to run it manually. This would allow us to define which environments it should not run in as a part of the rails migrations while still allowing it to run by default in all other environments. That leads us to this final iteration:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateFooBarPermissionParity&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;5.0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;disable_ddl_transaction!&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;SKIP_DATA_MIGRATIONS&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Skipping rake task, please run &apos;permission:create_foo_bar_parity&apos; manually&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;say_with_time&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Creating parity between old and new verions of foo bar permission&quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Rake&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;permission:create_foo_bar_parity&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;invoke&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RuntimeError&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;unless&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;/^Don&apos;t know how to build task/&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Rake task &apos;permission:create_foo_bar_parity&apos; did not run because it doesn&apos;t exist&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;say&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Nothing to do&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You could make the name of the variable what ever works best for you. With that environment variable set, we can deploy at a hight traffic time to production without effecting users and run the data migration at night when traffic is slower without needing the full deploy process. We may not need this option in all cases, but it’s nice to have it if we need it; especially if your process for deploying code requires some work.&lt;/p&gt;

&lt;p&gt;This configuration has helped me get as close as I’ve ever been to having a painless way to update data across multiple environments. Hopefully some of the ideas might prove helpful to you too!&lt;/p&gt;

</description>
        <pubDate>Wed, 30 Jan 2019 00:00:00 +0000</pubDate>
        <link>http://reinhardt.io/rails/migrations/ruby/2019/01/30/low-pain-data-migrations-in-rails.html</link>
        <guid isPermaLink="true">http://reinhardt.io/rails/migrations/ruby/2019/01/30/low-pain-data-migrations-in-rails.html</guid>
        
        
        <category>Rails</category>
        
        <category>Migrations</category>
        
        <category>Ruby</category>
        
      </item>
    
      <item>
        <title>Resources For Learning To Program</title>
        <description>&lt;h3 id=&quot;learn-to-program-using-mostly-free-resources-available-online&quot;&gt;&lt;em&gt;Learn to program using (mostly free) resources available online&lt;/em&gt;&lt;/h3&gt;

&lt;p&gt;There are so many wonderful resources that are available to someone who is wanting to enter the world of being a programmer. But sometimes it can be daunting to know what is good, where to start, and what kind of path to follow through these resources. When I was just beginning to get interested programming, I didn’t have the funds or time for a boot camp since I needed my day job as a source of income; but I was fortunate to be able to pull on resources I could find on-line and experience from mentors to begin my journey into programming. It wasn’t easy, but it was possible and so worth it!&lt;/p&gt;

&lt;h3 id=&quot;the-resources&quot;&gt;The Resources&lt;/h3&gt;
&lt;p&gt;These are things that were helpful to me as I launched into learning Ruby and becoming a web developer, so pick and choose as you find your own path!&lt;/p&gt;

&lt;p&gt;You will need a robust text editor. This is the tool that you will use to craft your raw material as a programmer, and you will always be tweaking and improving it to fit your needs. &lt;a href=&quot;http://atom.io&quot;&gt;&lt;strong&gt;Atom&lt;/strong&gt;&lt;/a&gt; is an excellent choice that comes preconfigured in smart ways and is approachable.&lt;/p&gt;

&lt;p&gt;At the very start &lt;a href=&quot;https://www.codecademy.com&quot;&gt;&lt;strong&gt;www.codecademy.com&lt;/strong&gt;&lt;/a&gt; is a great resource to just explore some of the very basics. They have some great programs now that lead you through building a website from scratch, for example. You can also pick and choose individual courses as well; maybe you want to just explore Ruby or JavaScript.&lt;/p&gt;

&lt;p&gt;Once you start to get your sea legs, you will probably be looking for a bit of a challenge to expand your new skills. A great thing for this is &lt;a href=&quot;http://exercism.io&quot;&gt;&lt;strong&gt;exorcism.io&lt;/strong&gt;&lt;/a&gt;. It’s like a collection of puzzles that you can solve while learning specifics of a new language. It’s still my go to when learning a new language (or just for fun :-))&lt;/p&gt;

&lt;p&gt;All through your programming career it will be important to know where to look for help on an issue, and the beginning is no exception. Documentation for your language or framework is one place to find answers; another is your favorite search engine! Searching for an error message or issue can often turn up other people who have encountered the same problem. In addition, there is a whole community of people that ask and answer question on &lt;a href=&quot;https://stackoverflow.com&quot;&gt;&lt;strong&gt;Stack Overflow&lt;/strong&gt;&lt;/a&gt;. Many of the problems I encounter every day as a programmer I can solve by drawing on the knowledge of others on there.&lt;/p&gt;

&lt;p&gt;Now for one of my favorites! &lt;a href=&quot;https://www.railstutorial.org/book&quot;&gt;&lt;strong&gt;Michael Hartl’s Rails Tutorial&lt;/strong&gt;&lt;/a&gt; was absolutely essential in my journey. This one is Ruby and Rails specific, but even if that is not your jam there is still value in working through this tutorial. What makes it such a masterwork is that not only does Michael teach you the Rails framework, but he helps you learn what it means to be a developer. He walks you through what it’s like to develop an application as you would in the real world. He teaches you about using essential tools like version control (git), Github/ Bitbucket. The course helps you turn the skills you are building into a cohesive package. This amazing free (!!!) resource is like having a mentor walk you through building your first web application.&lt;/p&gt;

&lt;p&gt;After working through the above, you will be off to a really great start! At this point, it really helped me to just start getting my hands dirty with some “real” applications. You can work on an idea that you might have, or just explore building something. It is super helpful to work on something with more experienced developers; whether that is contributing to open source or finding someone who is willing to mentor you as you contribute to their codebase. This feedback from others will be one of the best educations you will be able to get, and it will be way more engaging to be working on “real” things. Learn how to find answers to things that you don’t know, ask questions, and know that you will always be learning. These things continue to be important to me today!&lt;/p&gt;

&lt;p&gt;At this point you can make things that “work”, now it’s time to make things that work even better and are easy to maintain. Any book by &lt;a href=&quot;http://www.sandimetz.com&quot;&gt;&lt;strong&gt;Sandi Metz&lt;/strong&gt;&lt;/a&gt; is worth the read and will change the way that you look at the code you write. She also has a free newsletter that has great insights. There are an abundance of conference talks that have been recorded and made available online; these are a goldmine of information just waiting to be viewed! You can search in &lt;a href=&quot;http://confreaks.tv/&quot;&gt;&lt;strong&gt;confreaks&lt;/strong&gt;&lt;/a&gt; or on YouTube for recent conferences you are interested in. If you do happen to be looking into Ruby and Rails, &lt;a href=&quot;http://railscasts.com&quot;&gt;&lt;strong&gt;RailsCasts&lt;/strong&gt;&lt;/a&gt; is an amazing resource! Ryan Bates created a trove of screencasts many things you would want to do when building a Rails app. It might be getting a bit dated now, but much of it is still very relevant and he’s now made it available for free!&lt;/p&gt;

&lt;p&gt;This is far from an exhaustive list (and is a little skewed towards Ruby), but these are some great resources to propel you forward. I wouldn’t be too strict on order; you may need to jump around, speed up, or slow down. Go at a pace that works for you and keeps you engaged.&lt;/p&gt;

&lt;h3 id=&quot;being-my-own-biggest-obstacle&quot;&gt;Being My Own Biggest Obstacle&lt;/h3&gt;
&lt;p&gt;For me, the hardest part at the start of the journey wasn’t even a technical one. It was one of building confidence. Hearing others around around you that are further along in their journey talk about things that sound like another language can be intimidating. Take those opportunities to ask questions and build your knowledge! People who aren’t jerks will love the chance to explain. Since you will be learning on your own, building relationships with other people that do what you want to do will be an essential part of your learning and will be your ticket to finding jobs as well (if that is what you are going for). One great way to do this is to look for meetups in your area that you can attend. Be sure to search the interwebz for something called “imposter syndrome” so that it doesn’t slow you down and you can be comfortable being at the beginning of your journey. Although it can be hard to learn these things on your own, learning to solve problems for yourself will continue to serve you well beyond the beginning. You’ve got this!&lt;/p&gt;

&lt;h3 id=&quot;links&quot;&gt;Links&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://atom.io&quot;&gt;&lt;strong&gt;Atom&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.codecademy.com&quot;&gt;&lt;strong&gt;www.codecademy.com&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://exercism.io&quot;&gt;&lt;strong&gt;exorcism.io&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com&quot;&gt;&lt;strong&gt;Stack Overflow&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.railstutorial.org/book&quot;&gt;&lt;strong&gt;Michael Hartl’s Rails Tutorial&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.sandimetz.com&quot;&gt;&lt;strong&gt;Sandi Metz&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://confreaks.tv/&quot;&gt;&lt;strong&gt;Confreaks&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://railscasts.com&quot;&gt;&lt;strong&gt;Rails Casts&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Sun, 18 Feb 2018 00:00:00 +0000</pubDate>
        <link>http://reinhardt.io/learning/resources/new%20programmer/ruby/rails/2018/02/18/resources-for-learning-to-program.html</link>
        <guid isPermaLink="true">http://reinhardt.io/learning/resources/new%20programmer/ruby/rails/2018/02/18/resources-for-learning-to-program.html</guid>
        
        
        <category>learning</category>
        
        <category>resources</category>
        
        <category>new programmer</category>
        
        <category>ruby</category>
        
        <category>rails</category>
        
      </item>
    
      <item>
        <title>All My Work Will Be Destroyed; a story of inspiration</title>
        <description>&lt;p&gt;You could hear no breeze in the tops of the trees bordering the edge of this 5 acre plot. The only motion in the atmosphere around seemed to be the waves of heat rolling off of any exposed surface, distorting the visible edge of everything so that we seemed almost in a hazy dream land. My hands moved automatically performing one of several yearly adjustments on the grapevine in front of me; adjustments that were crucial to the quality of the harvest but right now seemed trivial compared to the travails of performing them. On an acre of vines planted this way there are exactly 545 vines; that meant that on this 5 acres my coworkers and I had to repeat this operation 2725 time in the blazing sun. After these 5 acres where done there where 50 more, or somewhere around 27250 more vines to go.&lt;/p&gt;

&lt;p&gt;The vines were arranged in rows about 8 feet apart and at this point of the season meant that they formed a curtain of foliage about 6 feet high and effectively blocked you from seeing much else than the far off ends of the rows and the blazing sun above. Through this curtain of foliage in both directions I could here a persistent rustle mimicking the sound of my own work. Today the normally ubiquitous low hum of talk was absent; lost is the hot vapors that swirled around us. The grapes don’t develop properly without the proper sun exposure and air movement and so today I had led our team of 6 out into this field to make those adjustments.&lt;/p&gt;

&lt;p&gt;We all reached the end of the rows around the same time and looked in vain for a little shade under the vines to sit in while we guzzled more water. Looks of exhaustion marked everyone’s sweaty, beet red faces. Looking behind me at the long line of row yet to go and then back at our group I was thankful for their determination to get to this point but wondering how we would meet the challenge ahead of us. We started making small talk and laughing together a bit. This was the summer where &lt;a href=&quot;https://www.youtube.com/watch?v=9bZkp7q19f0&quot;&gt;“Gangnam Style”&lt;/a&gt; was going viral and in jest one of the team started the song on his cell phone. There in the grassy area between the end of the rows and the edge of the woods and delirious from the heat and the absurd difficulty of it all, we broke out into an impromptu dance party while the tinny cellphone speaker belted out this unique specimen of k-pop. After this unexpected interlude and laughing heartily at each other and ourselves we met the challenge ahead of us and plunged into the next rows to begin again. The fate of that vintage and whether we met our quality goals are foggy in my memory, but what stuck with me was the bond that team formed in the crucible of these experiences. Those people and what we experienced will be vivid in my memory for much longer than the things that otherwise seemed so important at the time.&lt;/p&gt;

&lt;p&gt;Now I work in software, but I see vestiges of these dynamics there as well. The crucible is not environmental (usually) but complicated technical challenges and lofty goals of other sorts. The importance of these human connections remains something that inspires me. To be honest, the feeling is not nearly as intense as when you are experiencing harsh physical challenges together as a team; that is indeed a special feeling reserved as a reward in proportion to the suffering incurred. But the patterns are there, and the impact that we will have on those around us is real.&lt;/p&gt;

&lt;p&gt;Early in my career as a software engineer there were people who were willing to be there for me as I struggled to find my footing in a new industry. As I progressed in my understanding, there were people who were much more experienced and were willing to help me form my own insights and greater understanding. I feel like I am finally in a position where I feel reasonably confident as I continue to learn and face new challenges and in this new position it is a great joy now to be able to be that person that can help others in their own quests for understanding.&lt;/p&gt;

&lt;p&gt;One tradition that I particularly relish is gathering around a table with our various brown bag lunches and talking about life. Not work talk, just being humans together. For better or worse, we are all going to leave marks on the lives of our coworkers. It seems to me the presence of these connections that come from being individuals that are brought together to work towards a common goal contribute in a large part both to our success as a team but also and perhaps more importantly they will leave lasting impression on us as people.&lt;/p&gt;

&lt;p&gt;All the work that we are doing now will fade away. The grape vintage will be consumed, the difficult new addition to the app that is so critical today will be destroyed tomorrow. Even the team we are working with now will likely look different or cease to be at some point. The more enduring mark is not found in the product of the work that we are doing today, but in the marks we leave on the people around us and through them on the world around us while doing this work. I am inspired by those that have recognized this and left their mark on me; and in turn I am inspired to keep this awareness and think about what kind of marks I might be leaving on those around me.&lt;/p&gt;

</description>
        <pubDate>Thu, 11 Jan 2018 00:00:00 +0000</pubDate>
        <link>http://reinhardt.io/development/work/life/meaning/2018/01/11/all-my-work-will-be-destroyed-a-story-of-inspiration</link>
        <guid isPermaLink="true">http://reinhardt.io/development/work/life/meaning/2018/01/11/all-my-work-will-be-destroyed-a-story-of-inspiration</guid>
        
        
        <category>developement</category>
        
        <category>work</category>
        
        <category>life</category>
        
        <category>meaning</category>
        
      </item>
    
      <item>
        <title>Testing Pundit Policies</title>
        <description>&lt;p&gt;CanCan has been an awesome standby for authorization in many of the projects I’ve been involved with; but I’ve had my eye on Pundit and really appreciate the ‘just plain old Ruby objects’ approach. So on a couple of recent projects I made the jump. So far, it’s been awesome; but that’s a post for another day :-).&lt;/p&gt;

&lt;p&gt;One of the first challenges that I ran into was developing a strategy for testing my policies. Both of my projects are using MiniTest, one uses spec via the ‘minitest-spec-rails’ gem and the other is just plain vanilla with the additions that Rails provides. FactoryGirl is used in both cases.&lt;/p&gt;

&lt;h3 id=&quot;general-structure&quot;&gt;General Structure&lt;/h3&gt;

&lt;p&gt;In each policy test, the basic structure I’ve begun using is&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# MiniTest&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;test_helper&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingPolicyTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestCase&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Setup needed for all tests... maybe build different record for testing&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# against&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;a user can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# test the things a user can do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;a user can not&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# test the things a user can not do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;user scope&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# test the scope of a user&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;an admin can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# test the things an admin can do&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;#MiniTest::Spec&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;test_helper&apos;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;SomethingPolicyTest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveSupport&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;TestCase&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:something&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:another_something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:another_something&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;user&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# test what a user can do&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# More on `must_permit` and `wont_permit` later :-)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;must_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:new?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:edit?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:update?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;can not&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;c1&quot;&gt;# test what a user can&apos;t do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wont_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:destroy?&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;scope&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;includes something&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# test things about the user scope&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;admin&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:admin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:admin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# same stuff for an admin or other role&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This has been kind of a nice starting point, but I frequently deviate from this pattern if it makes sense or as the list of things they can or can not do gets longer and more complex.&lt;/p&gt;

&lt;h3 id=&quot;custom-assertions&quot;&gt;Custom Assertions&lt;/h3&gt;

&lt;p&gt;How do we make assertions about a policy? Well, a policy is just a Ruby class… so all we have to do is instantiate the policy and call it’s instance methods. Pundit provides a nice class method &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.policy&lt;/code&gt; for instantiating a policy. It infers the name of the policy from the class of the given record and then instantiates the policy using the user and record passed in. So in order to test our policy, we could make assertions using the result of calling methods on the policy.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# ...&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;user can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show?&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;# or spec&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;show?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;must_equal&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But this isn’t going to give meaningful error messages and contains a lot of boilerplate code. To help make things clearer, lets write custom MiniTest assertions to help test our policy. I’ve defined these in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/helpers/pundit.rb&lt;/code&gt; and then required that file in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_helper.rb&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MiniTest&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Assertions&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fails unless there is a Pundit policy for the user and record and permits&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# the action.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# user: instance of User&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# record: instance or class of object being authorized&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# action: string or symbol of the action being authorized or array of&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#         strings/ symbols&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;User &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should be permitted to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but isn&apos;t permitted&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Same as `assert_permit` except it fails if user IS permitted for the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# record and action&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refute_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;User &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should NOT be permitted to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but is permitted&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;refute&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This opens up the MiniTest::Assertions module and defines two newly minted assertions. Under the hood it uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Pundit.policy&lt;/code&gt; like we were before. I chose to use the ‘bang’ version, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.policy!&lt;/code&gt;, so that if the policy is not yet defined, we get a meaningful error message. Otherwise we would simply get a nil class error. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#public_send&lt;/code&gt; is used to dynamically call the given ‘action’ method on the policy. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#public_send&lt;/code&gt; is used because I only want to test the public methods available in the policy, not any private helper stuff I might have defined.&lt;/p&gt;

&lt;p&gt;We now have a much more descriptive error message for when our assertion fails. I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#.inspect&lt;/code&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;user&lt;/code&gt; in the error message so that you can see the attributes of the user when you are debugging a test… although if this is too much info you could play around with what makes most sense for you. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array()&lt;/code&gt; is used to convert whatever we pass into the ‘action’ parameter to an array, if possible; this way we can choose to pass in a single action or an array of actions.&lt;/p&gt;

&lt;p&gt;With these new assertions, we can now use them like&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;test&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;a user can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show?&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# or&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:new?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:edit?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:update?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# or&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:new?&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and then you would get a helpful error message like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  User #&amp;lt;User id: nil, email: &quot;test558484455@example.com&quot;, password_digest: &quot;$2a$04$u0rOr5nwXzYHcydrVJZq.ONO5bKnZj2AoSIjE6Touw/...&quot;, role: 2, organization_id: 1&amp;gt; should be permitted to update? #&amp;lt;Something:0x007fc71ca58ff8&amp;gt;, but isn&apos;t permitted
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But we don’t want to leave the MiniTest:Spec-ers without an expectation to hide these assertions in. Fortunately, it is wonderfully easy to turn an assertion into an expectation. At the bottom of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/helpers/pundit.rb&lt;/code&gt; we’ll add the following&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Minitest&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Expectations&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# See `assert_permit`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Use like `record.must_permit user, action`&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;infect_an_assertion&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:assert_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:must_permit&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# See `refute_permit`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Use like `record.wont_permit user, action`&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;infect_an_assertion&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:refute_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:wont_permit&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and that’s it! We can now use our expectations in our tests:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;can&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;must_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:show?&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# or&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;must_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:new?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:create?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:edit?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:update?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;# or&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;Something&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;must_permit&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:new?&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;testing-scope&quot;&gt;Testing Scope&lt;/h3&gt;

&lt;p&gt;Testing scope is pretty straight forward. Pundit provides &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.policy_scope&lt;/code&gt; for retrieving records using the scope, so all we have to do is make sure that we only get the records we expect to get and not the others. For example:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;describe&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;scope&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;attr_accessor&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:another_thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:thing&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:user&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;user: &lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@another_thing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:another_thing&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy_scope!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;includes a thing&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;must_include&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;thing&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;wont include another thing&apos;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;wont_include&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;another_thing&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;the-end&quot;&gt;The End!&lt;/h3&gt;

&lt;p&gt;This pretty much sums up how I test my Pundit policies. I’ll leave you with a complete copy of my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test/helpers/pundit.rb&lt;/code&gt;. Happy testing!&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MiniTest&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Assertions&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Fails unless there is a Pundit policy for the user and record and permits&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# the action.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# user: instance of User&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# record: instance or class of object being authorized&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# action: string or symbol of the action being authorized or array of&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;#         strings/ symbols&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;User &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should be permitted to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but isn&apos;t permitted&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Same as `assert_permit` except it fails if user IS permitted for the&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# record and action&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;refute_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;actions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;User &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;inspect&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; should NOT be permitted to &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;, but is permitted&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;refute&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Pundit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;policy!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_send&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MiniTest&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Expectations&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# See `assert_permit`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Use like `record.must_permit user, action`&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;infect_an_assertion&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:assert_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:must_permit&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;##&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# See `refute_permit`&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# Use like `record.wont_permit user, action`&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;infect_an_assertion&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:refute_permit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:wont_permit&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
        <pubDate>Thu, 21 Jul 2016 00:00:00 +0000</pubDate>
        <link>http://reinhardt.io/pundit/testing/ruby/2016/07/21/testing-pundit-policies</link>
        <guid isPermaLink="true">http://reinhardt.io/pundit/testing/ruby/2016/07/21/testing-pundit-policies</guid>
        
        
        <category>pundit</category>
        
        <category>testing</category>
        
        <category>ruby</category>
        
      </item>
    
  </channel>
</rss>
