Let's Use Rails Partials To Render Art from Magic: the Gathering!
Tags: ruby, rails, webdev, tutorial
Published: 2019-10-27 20:52:42

---
title: "Let's Use Rails Partials To Render Art from Magic: the Gathering!"
published: true
description: a quick intro to using rails partial views using the scryfall api for art from magic the gathering
tags: ruby, rails, webdev, tutorial
series: "Rendering Card Game Art with Rails Partials"
---

When I was first learning Rails in bootcamp, I spent most of my time learning routing with regular views--and they were, uh, *simple*. At that point, I wasn't familiar with making reusable frontend components, rendering them in a dynamic and nested way.

But all that was before I learned React! Now that I'm returning to frontend Rails work, I've been spending much more time with partial views, or simply **partials**.

[Rails Guides](https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials) describes partials this way:

> Partial templates - usually just called "partials" - are another device for breaking the rendering process into more manageable chunks. With a partial, you can move the code for rendering a particular piece of a response to its own file.


# Overview

In this article, we'll create some partials to render art from Magic: the Gathering , queried from the [Scryfall API](https://scryfall.com/docs/api/). We'll cover these topics along the way:
1. Rails naming conventions for views and partials
2. Using `session` to store data from external APIs *(not specific to partials, but part of the use case)*
3. Using `render partial:` within views and other partials
4. Passing variables to partials with `locals:`
5. Rendering partials repeatedly by iterating through a collection with `collection:`

# Views and Partials

Rails' convention-over-configuration gives us a lazy option for rendering views: if a controller's method has a view with a matching name, and there's no other `render` or `redirect_to` invoked, Rails will automatically render the view when that method is called. Or, more eloquently put from [Rails Guides with the following code example](https://guides.rubyonrails.org/layouts_and_rendering.html#rendering-by-default-convention-over-configuration-in-action):


```ruby
class BooksController < ApplicationController
  def index
    @books = Book.all
  end
end
```
> Note that we don't have explicit render at the end of the index action in accordance with "convention over configuration" principle. The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller's view path and render it. So in this case, Rails will render the app/views/books/index.html.erb file.

## Partials Love Underscores

**Partials**, however, are named with underscores at the beginning. This convention gives two advantages: we can differentiate partials from regular views, and it also allows us to drop the underscore when invoking `render partial:` in ERB.

We'll explore this more once we have some examples in front of us. :)


# Use Case: Querying the Scryfall API for Magic: the Gathering Card Art

I'm a sucker for art from Magic: the Gathering. So, our Rails app will query the [Scryfall API](https://scryfall.com/docs/api/) for card art (along with identifying names and artists), and render a sampling of 9 images--with a button to refresh and re-query the API.

## Rails App Setup

Let's get started by using `rails new` to create our app:
```
$ rails new partial-view-demo
```

After that, let's add a Pages controller, along with an empty `index` method:
```
$ rails g controller pages index
```

By default, this will populate our `routes.rb` file with a `get 'pages/index` and `get page/index` route. For our use case, we'll want both Get and Post requests to `/` to go to the `index` method on our Pages controller:
```ruby
# /config/routes.rb

Rails.application.routes.draw do
  get '/', to: 'pages#index'
  post '/', to: 'pages#index'
end
```

And, because we'll be using `RestClient` in our API query, go ahead and add `gem 'rest-client` to the Gemfile, and update with Bundler:
```
$ bundle install
```

Now, let's test our `/pages/index.html.erb` view with our routes to make sure everything's working:

```html
<%# /app/views/pages/index.html.erb %>

<h1>Pages#index</h1>
<p>Find me in app/views/pages/index.html.erb</p>

<h2>Let's add some Magic: the Gathering card art!</h2>
```

Run `rails s` and open up `localhost:3000` in your browser:

![screenshot of localhost:3000 showing rails app loading index view](https://thepracticaldev.s3.amazonaws.com/i/rslpgivzqxrxo617sare.png)


Perfect! Now let's create some partials as components.

## Card Components: _card, _card_image, _card_name, _card_artist

First, we'll add components to render each piece of art on its own card component. These cards will simply have an image, a name, and the artist.

To keep our components organized, create a new `/views/pages/cards` directory.

We name partials with underscores at the beginning, so we'll create 4 new files in our new directory: `_card.html.erb`, `_card_image.html.erb`, `_card_name.html.erb`, and `_card_artist.html.erb`.

Heres how our views are looking:

![screenshot of vscode window with new partials in the cards directory highlighted](https://thepracticaldev.s3.amazonaws.com/i/my2nebsghz2lx33vktur.png)

We'll fill out these partials with HTML and ERB like any other view once we have some API data to render.

## Form Components: _refresh_button, _refresh_counter

We'll also add a `/views/pages/forms` directory, where we'll stash a button to refresh our 9 pieces of art, as well as a counter to keep track of how many times we've hit the button. *(This will help illustrate how we can pass variables through `render partial:`.)*

Add two files to our new directory: `_refresh_button.html.erb`, and `_refresh_counter.html.erb`:

![screenshot of vscode window with new partials in the forms directory highlighted](https://thepracticaldev.s3.amazonaws.com/i/v9l3h3g3cwm9k8vy8bn0.png)

We'll fill these out with our card components shortly.

## Controller Methods to Query the API

Before we dive into rendering, here's a quick snapshot of the code used to query the Scryfall API and return 9 random pieces of art:

```ruby
# /app/controller/pages_controller.rb

class PagesController < ApplicationController
  def index
    session[:img_array] = session[:img_array] || []

    if session[:img_array].empty? || params["button_action"] == "refresh"
      session[:img_array] = get_scryfall_images
    end
  end


  private

  def get_json(url)
    response = RestClient.get(url)
    json = JSON.parse(response)
  end
  
  def parse_cards(json, img_array)
    data_array = json["data"]
    data_array.each do |card_hash|
      if card_hash["image_uris"]
        img_hash = {
          "image" => card_hash["image_uris"]["art_crop"],
          "name" => card_hash["name"],
          "artist" => card_hash["artist"]
        }
        img_array << img_hash
      end
    end

    if json["next_page"]
      json = get_json(json["next_page"])
      parse_cards(json, img_array)
    end
  end
  
  def get_scryfall_images
    api_url = "https://api.scryfall.com/cards/search?q="
    img_array = []
    creature_search_array = ["merfolk", "goblin", "angel", "sliver"]

    creature_search_array.each do |creature_str|
      search_url = api_url + "t%3Alegend+t%3A" + creature_str
      json = get_json(search_url)
      parse_cards(json, img_array)

      sleep(0.1)  # per the API documentation: https://scryfall.com/docs/api
    end

    img_array.sample(9)
  end
end
```

Here, we're using the `session` variable to store an array of `img_hash` objects containing the "image" URL, the card's "name", and the "artist" (all as strings).

The API query is set up to look for "legend" card that are also creatures of the type "merfolk", "goblin", "angel", or "sliver"--my favorite creature types! *(Sorry, my old beloved elf deck...)* Also note that, per the API documentation, a 0.1 second delay is built in-between any searches, for good citizenship.

If we print the contents of `session[:img_array]` at the end of the `index` method, here's what we have when we re-navigate to `localhost:3000`:

```ruby
# session[:img_array]

    [
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/b/c/bc4c0d5b-6424-44bd-8445-833e01bb6af4.jpg?1562275603", "name"=>"Tuktuk the Explorer", "artist"=>"Volkan Baǵa"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/9/a/9a8aea2f-1e1d-4e0d-8370-207b6cae76e3.jpg?1562740084", "name"=>"Tiana, Ship's Caretaker", "artist"=>"Eric Deschamps"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/3/7/37ed04d3-cfa1-4778-aea6-b4c2c29e6e0a.jpg?1559959382", "name"=>"Krenko, Tin Street Kingpin", "artist"=>"Mark Behm"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/4/2/4256dcc1-0eee-4385-9a5c-70abb212bf49.jpg?1562397424", "name"=>"Slobad, Goblin Tinkerer", "artist"=>"Kev Walker"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/2/7/27907985-b5f6-4098-ab43-15a0c2bf94d5.jpg?1562728142", "name"=>"Bruna, the Fading Light", "artist"=>"Clint Cearley"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/7/2/722b1e02-2268-4e02-8d09-9b337da2a844.jpg?1562405249", "name"=>"Vial Smasher the Fierce", "artist"=>"Deruchenko Alexander"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/8/b/8bd37a04-87b1-42ad-b3e2-f17cd8998f9d.jpg?1562923246", "name"=>"Sliver Legion", "artist"=>"Ron Spears"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/d/d/dd199a48-5ac8-4ab9-a33c-bbce6f7c9d1b.jpg?1559959197", "name"=>"Zegana, Utopian Speaker", "artist"=>"Slawomir Maniak"}, 
     {"image"=>"https://img.scryfall.com/cards/art_crop/front/d/d/ddb92ef6-0ef8-4b1d-8a45-3064fea23926.jpg?1562854687", "name"=>"Avacyn, Angel of Hope", "artist"=>"Jason Chan"}
    ]
```

Cool! We have our array of 9 `img_hash` objects, each with a URL, name, and artist. **Now let's render them!**
  

# Rendering Partials Inside Views and Other Partials

## Render a partial inside a view

Back in our `/views/pages/index.html.erb` view, we can now use `render partial:` to access the partials we created.

Let's start by simply rendering a `_card_image` partial with `session[:img_array][0]["image"]` supplying the URL:

**_card_image.html.erb**
```ruby
<%# /app/views/pages/cards/_card_image.html.erb %>

<%= image_tag(session[:img_array][0]["image"]) %>
```

**index.html.erb**
```ruby
<%# /app/views/pages/index.html.erb %>

<h1>Pages#index</h1>
<p>Find me in app/views/pages/index.html.erb</p>

<h2>Let's add some Magic: the Gathering card art!</h2>

<%= render partial: '/pages/cards/card_image' %>
```

Note that in `render partial:`, we drop the underscore from the beginning of `_card_image.html.erb` and simply call it  as `card_image` (plus its path relative to the `views` directory).

![screenshot of rendered card art labeled "Tuktuk the Explorer" by artist Volkan Baǵa](https://thepracticaldev.s3.amazonaws.com/i/hbahpl6puvvuty79qipy.png)

Cool! Our partial is rendering with the URL from our `img_hash`.

## Render a partial inside another partial

Let's make use of our `_card.html.erb` partial, and render the `_card_image.html.erb` partial inside it. We'll also wrap each partial's contents in a `<div>` so we can see the DOM tree more clearly in our inspector:

**index.html.erb**
```ruby
<%# /app/views/pages/index.html.erb %>

<div class="card_container">
    <%= render partial: '/pages/cards/card' %>
</div>
```

**_card.html.erb**
```ruby
<%# /app/views/pages/cards/_card.html.erb %>

<div class="card">
    <h3>This is the _card partial</h3>

    <%= render partial: '/pages/cards/card_image' %>
</div>
```

**_card_image.html.erb**
```ruby
<%# /app/views/pages/cards/_card_image.html.erb %>

<div class="card_image">
    <h4>This is the _card_image partial</h4>

    <%= image_tag(session[:img_array][0]["image"]) %>
</div>
```

In our browser, with inspector open, we can see that the `_card_image` partial is its own `<div>` within the `_card` partial's `<div>`: 

![screenshot showing rendered card highlighted and developer tools with divs nested in DOM tree](https://thepracticaldev.s3.amazonaws.com/i/gw60l1a40o5ioyreh077.png)

This is exactly the nested behavior we expected!

## Rendering partials multiple times

We can also use `render partial:` to render the same partial multiple times:

**index.html.erb**
```ruby
<%# /app/views/pages/index.html.erb %>

<div class="card_container">
    <%= render partial: '/pages/cards/card' %>
    <%= render partial: '/pages/cards/card' %>
    <%= render partial: '/pages/cards/card' %>
</div>
```

This results in three cards being created as sibling DOM elements:

![screenshot showing three identical cards being rendered, with bottom one highlighted to show it as a separate sibling div](https://thepracticaldev.s3.amazonaws.com/i/xq7gokx1zfja01sw21qx.png)


# Passing Variables to Partials with `locals:`

Instead of accessing our long-winded `session[:img_array]` variable repeatedly, we can pass variables directly to partials with the `locals:` option inside `render partial:`. 

In our `index.html.erb`, let's change our three `render partial:` lines to include a different `img_hash` from `sessions[:img_array]` in each card:

**index.html.erb**
```ruby
<%# /app/views/pages/index.html.erb %>

<div class="card_container">
    <%= render partial: '/pages/cards/card', locals: {img_hash: session[:img_array][0]} %>
    <%= render partial: '/pages/cards/card', locals: {img_hash: session[:img_array][1]} %>
    <%= render partial: '/pages/cards/card', locals: {img_hash: session[:img_array][2]} %>
</div>
```

Now, each `_card` partial now has a different piece of art to render!

Let's go back and build out our `_card` partial to render the `_card_image`, `_card_name`, and `_card_artist` partials. Each `_card` will also use `locals:` to pass the contents of its `img_hash` to those partials. *(Note that, for ease of reading, we are nesting all these partials inside one `<div class="card>` tag.)*

**_card.html.erb**
```ruby
<%# /app/views/pages/cards/_card.html.erb %>

<div class="card">
    <h3>This is the _card partial</h3>

    <%= render partial: '/pages/cards/card_image', locals: {image: img_hash["image"]} %>
    <%= render partial: '/pages/cards/card_name', locals: {name: img_hash["name"]} %>
    <%= render partial: '/pages/cards/card_artist', locals: {artist: img_hash["artist"]} %>
</div>
```

**_card_image.html.erb**
```ruby
<%# /app/views/pages/cards/_card_image.html.erb %>

<%= image_tag(image) %>
```

**_card_name.html.erb**
```ruby
<%# /app/views/pages/cards/_card_name.html.erb %>

<p> Name: <%= name %> </p>
```

**_card_artist.html.erb**
```ruby
<%# /app/views/pages/cards/_card_artist.html.erb %>

<p> Artist: <%= artist %> </p>
```

Let's check back on `localhost:3000` to see if we've got some different cards now:

![screenshot showing three different cards being rendered, including names and artists](https://thepracticaldev.s3.amazonaws.com/i/bkybh26fm4jm1u8kifpt.png)

Perfect! Now, let's try **iterating** through our whole `sessions[:img_array]` to display all 9 cards!

# Rendering Partials Repeatedly by Iterating with `collection:`

We can refactor our card-rendering code in `index.html.erb` by adding `collection: session[:img_array], as: :img_hash`. This will tell Rails to use the array stored in `session[:img_array]` and pass each object aliased as `img_hash` to its partial:

**index.html.erb**
```ruby
<%# /app/views/pages/index.html.erb %>

<div class="card_container">
    <%= render partial: '/pages/cards/card', collection: session[:img_array], as: :img_hash %>
</div>
```

Now, we should expect 9 different cards to be rendered at `localhost:3000`. Since we previously used `locals:` to pass our hashes with the alias `img_hash`, our other partials should need no changes:

![screenshot showing four different cards being rendered, with the browser cut off but indicating that more are rendered underneath](https://thepracticaldev.s3.amazonaws.com/i/bgsl214p77ndofwgdur2.png)

Success!!


# Finishing Up: Adding a Form with a Refresh Button and Counter

Okay, you're probably bored of looking at Tuktuk by now--I know I am! So, let's go ahead and add our `_refresh_button` and `_refresh_counter` partials to our `index.html.erb`:

**index.html.erb**
```ruby
<%# /app/views/pages/index.html.erb %>

<div class="refresh_form">
    <%= render partial: '/pages/forms/refresh_button' %>
    <%= render partial: '/pages/forms/refresh_counter', locals: {counter: @refresh_counter} %>
</div>
```

Since we are passing a `@refresh_counter` variable through `locals:`, lets go ahead and define that in our Pages controller:

**pages_controller.rb**
```ruby
# /app/controllers/pages/page_controller.rb

  def index
	...
	
    session[:refresh_counter] = session[:refresh_counter] || 0

    if params["button_action"] == "refresh"
      session[:refresh_counter] += 1
    end
    
    @refresh_counter = session[:refresh_counter]
  end
```

Great! Now our `session` will keep track of the number of times we've hit the refresh button. 

And in our form partials:

**_refresh_button.html.erb**
```ruby
<%# /app/views/pages/forms/_refresh_button.html.erb %>

<%= form_for :form_data do |f| %>
    <%= f.button "Refresh", name: "button_action", value: "refresh" %>
<% end %>
```

**_refresh_counter.html.erb**
```ruby
<%# /app/views/pages/forms/_refresh_counter.html.erb %>

<p>Refresh counter: <%= counter %> </p>
```

Now, our page on displays a Refresh button along with the counter's value:

![screenshot showing bottom of the rendered cards, with refresh button and counter highlighted](https://thepracticaldev.s3.amazonaws.com/i/mx8j3i50vogv8zzjg9ym.png)

And hitting refresh will update our cards (and increment the counter by one)!

![screenshot showing bottom of the rendered cards with different cards than before, with highlighted refresh counter showing number 1](https://thepracticaldev.s3.amazonaws.com/i/cwo7jl0je9xlnkz2nji1.png)


# Conclusion

We've covered how to:
* create partial views in Rails
* render them with `render partial:`
* pass variables to them with `locals:`
* iterate through collections to repeatedly render a partial with `collection:`. 

*(Hopefully, you've seen some good Magic: the Gathering art along the way too!)*

Here's the GitHub repo if you're interested in seeing the code or playing around with it yourself: [https://github.com/isalevine/devto-rails-partial-view-demo](https://github.com/isalevine/devto-rails-partial-view-demo)

### Got any tips or tricks for using Rails partial views? Please feel free to comment and share below! :)