In parts one and two of this series, we familiarized ourselves with the ins and outs of Showcase.
Now, we’ll dive into samples, Showcase’s main feature. Samples show how a component can be used in a real application.
Samples in our Ruby App
In our case, we have two samples, one for a small button and one for a large button:

Let’s look at how the samples are rendered. We’ll add one sample back to our preview file:
If we refresh the page in our browser, it should look like this:

Sample Met…
In parts one and two of this series, we familiarized ourselves with the ins and outs of Showcase.
Now, we’ll dive into samples, Showcase’s main feature. Samples show how a component can be used in a real application.
Samples in our Ruby App
In our case, we have two samples, one for a small button and one for a large button:

Let’s look at how the samples are rendered. We’ll add one sample back to our preview file:
If we refresh the page in our browser, it should look like this:

Sample Methods in Showcase for Ruby on Rails
From our understanding of the gem’s architecture, the showcase.sample method populates the @samples instance variable of the Showcase::Preview instance, which is then passed to the final view. Let’s take a look at the Showcase::Preview#sample method:
And the Showcase::Sample initializer:
There is nothing very surprising in this initializer; most of it is just setting instance variables. Here is the current state of our Showcase::Sample instance:
Now that we have our instance, we can call the evaluate method on it:
About block.arity
If you are not familiar with block.arity, it returns the number of arguments that the block expects. If the block does not expect any arguments, block.arity will return 0. In our case, the block is the following:
As we can see, our block does not expect any arguments, so block.arity will return 0. This means that the consume method will be called, with the block as an argument:
About the consume Method
The consume method calls the render and extract_source methods, passing our block as an argument. Let’s have a look at the render method first:
The render method uses ActiveSupport::Notifications to subscribe to the render_partial.action_view event. This event is triggered whenever a partial is rendered in Rails. In our case, the block passed to the showcase.sample method will render the components/button partial, so an event will be triggered and the @instrumented instance variable will be set to the ActiveSupport::Notifications::Event published by the rendering of the partial.
This ActiveSupport::Notifications::Event instance responds to the duration and allocation methods. It will be used later to display the duration and allocations of the sample in the view, as shown in the top right corner here:

Next, the extract_source method is called, with the block as an argument:
The extract_source method first calls extract_source_block_via_matched_indentation_from with *block.source_location as arguments. If you’re not familiar with block.source_location, it returns an array of two elements: the file path and line number where the block was defined. In our case, it will return something like this:
The splat operator * before block.source_location is used to unpack the array into two separate arguments: the file path and the line number.
A More Complex Method
Let’s have a look at the extract_source_block_via_matched_indentation_from method:
This is quite a complex method to read, so let’s analyze it step by step. File.readlines(file) reads all the lines of the file and returns them as an array. In our case, the result looks like this:
The slice(source_location_index.pred..) method call gets lines starting from where the block was defined. The pred method is used to get the previous index, as source_location returns a 1-based index, while Ruby arrays are 0-based:
If we now view the whole line, the starting_line variable contains the line where the block was defined, and the lines variable contains all the lines after it:
Then, the indentation variable will store the leading whitespace of the starting_line. The regex /^\s+/ matches one or more whitespace characters at the beginning of a string, and to_s converts the match result to a string. In our case, the starting_line doesn’t have any leading whitespace, so indentation is an empty string.
Then the matcher regex will match lines that start with the same indentation, followed by a non-whitespace character. If our code is properly indented, the index variable will return the index of the block’s end keyword:
Finally, lines.slice!(index..) removes all the lines from the index to the end of the array. In our case, it will remove the last line of the block, the end keyword:
The last step is simply to convert the array of lines back to a string and trim extra white space:
Phew! That was quite a complicated method, but we managed to get through it by reading carefully, line by line.
We can now go back to our extract_source method and try to understand the last line of code:
Understanding this line requires a solid grasp of the Ruby language. First, we have to know what Showcase.sample_renderer is:
The Rouge Gem in Showcase for Ruby on Rails
By default, Showcase.sample_renderer uses the rouge gem to highlight the syntax of the source code. However, developers using the showcase engine could choose a different syntax highlighter, as the sample_renderer is configurable:
If we remember correctly, the rouge gem is not listed in the showcase.gemspec file, which means that it is required to use the showcase engine:
The rescue LoadError idiom means that if rouge is not present in the Gemfile, the sample_renderer will return a simple proc that returns the source code without any syntax highlighting. If it succeeds, it will return a lambda function that takes two arguments: source and syntax, and a highlighted version of the source code.
We can actually test this in the Rails console:
The funny cp, n, s2, p, and ss classes are the CSS classes used by Rouge to style the syntax highlighting, as shown in the following picture:

Now that we have a good understanding of the Showcase.sample_renderer, we can go back to the extract_source method and ask ourselves why the last line of code is so complicated:
In fact, if we make a small experiment and change it to use the Showcase.sample_renderer directly, it will work just as well:
So why does the original code use instance_exec?
As we are in a gem, we need to allow users of the gem to use it in a wide variety of contexts. The instance_exec method allows us to execute the Showcase.sample_renderer lambda in the context of the @view_context. This makes all view helpers available in the lambda, which could be useful if the sample_renderer needed to use any view helper methods, such as sanitize, link_to, or any other method that is available in the view context. However, this is a very niche use case and probably not useful in 99% of situations.
Sample Instance Variables Render the View
We are finally done with our Showcase::Sample instance. Its instance variables now hold all the data necessary to render our view:
In the view, we can render the "showcase/engine/sample" partial for each of the preview’s samples:
If we have a look at the "showcase/engine/sample" partial, it renders a link with the name of the sample:
It also renders figures to show the time needed to render the component and its number of allocations:
It renders the actual HTML of the component:
And finally, it renders the source code necessary to render the component:
We now understand how all values are computed. There is no more magic left for us to uncover in the Showcase gem!
Wrapping Up
In this series on Showcase, we learned how Rails engines work: their main files and how to run them locally. But we also learned much more than that. We broadened our knowledge of Ruby and Rails, learning about block.arity, the view_context helper, ActiveSupport::Notifications, and how to use rouge to highlight code syntax in our Rails applications.
Instead of relying solely on documentation, we can gain a much deeper understanding of Ruby by reading source code. We can even end up adding new features to engines and contributing to open-source, improving our skills and giving back to the community.
Happy coding!