<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Daniel Azuma</title>
    <description>The latest articles on DEV Community by Daniel Azuma (@dazuma).</description>
    <link>https://dev.to/dazuma</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F177548%2F0ab70b99-4cd3-4e48-808f-722b37ba8e97.png</url>
      <title>DEV Community: Daniel Azuma</title>
      <link>https://dev.to/dazuma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dazuma"/>
    <language>en</language>
    <item>
      <title>Beware Twitter/X</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Mon, 04 Aug 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/dazuma/beware-twitterx-1na4</link>
      <guid>https://dev.to/dazuma/beware-twitterx-1na4</guid>
      <description>&lt;p&gt;So the TL;DR here is simply: please &lt;em&gt;unfollow&lt;/em&gt; and disregard any account on Twitter/X that claims to be me. I am not posting on that disgraced platform, and never will in the future. Earlier this year, my old username was taken over by a bot— one that I suspect was actually installed and run by Twitter itself— that spewed, with my name and profile, a deluge of apparently AI-generated nonsense that I would never say myself. I’ve been able to get that content removed for now, but with Musk at the helm, who knows what the platform might do in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fall of Twitter
&lt;/h2&gt;

&lt;p&gt;An eon ago, I gave a talk at RailsConf 2013 on philosophical issues around technology, and stirred the pot a bit with my &lt;a href="https://speakerdeck.com/dazuma/humanity-on-rails?slide=39" rel="noopener noreferrer"&gt;opinion of Twitter&lt;/a&gt;. It was, I thought, a platform with a net negative effect on culture due to how it intentionally filled public discourse with noise, and at the same time, an example of how technology controls us since I felt professionally obligated to maintain a presence on it anyway.&lt;/p&gt;

&lt;p&gt;That was, of course, many years ago, at the height of Twitter’s popularity and influence, long before Elon Musk’s takeover and the subsequent business, technical, and political issues now surrounding it. You can imagine what I think about it now.&lt;/p&gt;

&lt;p&gt;(And yes, I’m intentionally deadnaming Twitter. Its new identity is what is dead to me: a zombification of what was once, if a societal net-negative, at least an interesting and influential techno-cultural phenomenon.)&lt;/p&gt;

&lt;p&gt;So it should come as no surprise that I was one of perhaps many who chose to delete their accounts after the recent election and regime change in the US.&lt;/p&gt;

&lt;p&gt;My subsequent experience should serve as a bit of a cautionary tale for anyone else considering that step in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  The consequences of deleting your Twitter account
&lt;/h2&gt;

&lt;p&gt;In hindsight, I probably should have read the terms of service more closely and thought about the potential consequences. Among other things, Twitter’s terms of service state:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once your account is deleted after the 30-day deactivation window, your username will be available for registration by other X accounts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(From &lt;a href="https://help.x.com/en/managing-your-account/how-to-deactivate-x-account" rel="noopener noreferrer"&gt;How to deactivate your account&lt;/a&gt;, retrieved Aug 4, 2025)&lt;/p&gt;

&lt;p&gt;This should be an instant red flag, as it implies that any entity, including a hostile one, can usurp your old username and impersonate you.&lt;/p&gt;

&lt;p&gt;And sure enough, a bit over a month after I deleted my account, I suddenly noticed a deluge of new content on it, from puzzling announcements of the imminent commercial release of random open source projects, to shills of nonexistent memecoins, to retweets of accounts that real me might find disreputable or even offensive. Worse, the account still had all its previous followers, &lt;em&gt;and&lt;/em&gt; the description and mug photo remained as I had originally set it up. In all, it was nearly impossible to tell the account had been deleted, unless you know me and know what actually to expect of my posting habits.&lt;/p&gt;

&lt;p&gt;In other words, there was now a Twitter account masquerading as me, with content that was at best embarrassing, and at worst reputation-damaging. I’m also not alone in this; a quick Reddit search reveals a broad swath of similar experiences.&lt;/p&gt;

&lt;p&gt;Now, I do not know for certain what actually took place with the account. It is possible a real user took it over, although for what purpose I can’t fathom. However, I suspect Twitter itself simply decided to spew AI-generated garbage from deleted but well-followed accounts in an attempt to maintain the illusion of activity and relevance. Which, if true, would be an interesting instance of Twitter violating their own terms of use, by creating what is effectively an &lt;a href="https://help.x.com/en/safety-and-security/report-x-impersonation" rel="noopener noreferrer"&gt;impersonation account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I eventually complained to Twitter support. To their credit, they disabled the account within a few days. The account page now says the account is “suspended” for “rule violation” which I, uhh, guess is better than before. But now I have zero trust in what they might do next. What is to prevent Musk’s Twitter-zombie from unsuspending the account in the future and proceeding to impersonate me again?&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;If you still for some reason have a Twitter account, and are still following my old username, &lt;em&gt;please unfollow it&lt;/em&gt;. It is not me, and if there was any remote possibility of my ever rejoining the platform before, there isn’t anymore.&lt;/p&gt;

&lt;p&gt;And if you do still have an account and are considering deleting it, consider this a bit of a cautionary tale. It might be a good idea to take steps to strip the account of any ties to yourself and your profile before taking that action.&lt;/p&gt;

&lt;p&gt;Recent political, social, and tech industry developments have been difficult to say the least. I can consider myself lucky that I haven’t (yet) been impacted as deeply as some. The Musk-ification of Twitter and subsequent impact on people like myself who once felt obligated to use it for work, has been annoying, but at least not life- or livelihood-threatening. I pray for those not so lucky, and hope that, sometime soon, we’ll all come back to our senses and turn away from the insanity that seems to have gripped us.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Discord Command in Ruby on Google Cloud Functions: Part 4</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Tue, 04 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-4-3ch1</link>
      <guid>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-4-3ch1</guid>
      <description>&lt;p&gt;This is the last of a four-part series on writing a &lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;Discord “slash” command&lt;/a&gt; in &lt;a href="https://www.ruby-lang.org" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt; using &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Google Cloud Functions&lt;/a&gt;. In previous parts, we created a Discord command that displays a short Scripture passage, and implemented it in a webhook. In this part, we show how to split a longer passage across multiple messages, by triggering a second Function that makes Discord API calls. It will illustrate how to use Google Cloud Pub/Sub to run background jobs in Cloud Functions, combining an event architecture with the Discord API to implement a more complex application.&lt;/p&gt;

&lt;p&gt;Previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-g25"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l"&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-3-2e2l"&gt;Part 3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The challenge: size and timing
&lt;/h2&gt;

&lt;p&gt;So far in this series, we’ve accomplished quite a bit. We’ve written a Discord app in Ruby. We’ve integrated with the Discord API and the Bible API. We’ve added a Discord command to a server, and implemented it in our app. And we’ve followed best practices for handling secrets such as API keys.&lt;/p&gt;

&lt;p&gt;However, &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-3-2e2l"&gt;Part 3&lt;/a&gt; left off with two significant issues that are impairing the usability of our Discord command.&lt;/p&gt;

&lt;p&gt;First, Discord imposes a &lt;a href="https://discord.com/developers/docs/resources/webhook#execute-webhook" rel="noopener noreferrer"&gt;2000-character limit&lt;/a&gt; on message size. This means our command response cannot display a Scripture passage longer than 2000 characters. It can display John 1:1-5, for example, but not the first chapter of John in its entirety.&lt;/p&gt;

&lt;p&gt;Second, if the webhook has been idle for a while, the first call to it sometimes fails because it takes too long. This happens because Cloud Functions may need to spin up a new instance of our webhook to handle the request, and that involves not only starting up the Ruby process, but also making calls to Secret Manager to obtain API credentials, in addition to the latency of the Bible API itself. Together, this “cold start” latency sometimes takes longer than the &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction" rel="noopener noreferrer"&gt;3 seconds&lt;/a&gt; allowed by Discord.&lt;/p&gt;

&lt;p&gt;One way to address these problems is to &lt;em&gt;defer&lt;/em&gt; processing of the command. &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#interaction-response" rel="noopener noreferrer"&gt;Discord’s documentation&lt;/a&gt; describes this technique, which involves sending back an initial response quickly, but then continuing processing in the background and posting further updates to the response later. For our command, we could use a background process to retrieve a long passage, break it up into sections that are under the 2000-character limit, and issue Discord API calls to post each section in a separate message to the channel.&lt;/p&gt;

&lt;p&gt;To implement this kind of “deferred” processing in a serverless environment, we’ll have our app post an event to itself, triggering the background task in a separate Function. We could do so using an event broker such as Apache Kafka, or we can remain within Google’s ecosystem by using a service like &lt;a href="https://cloud.google.com/pubsub" rel="noopener noreferrer"&gt;Cloud Pub/Sub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following diagrams illustrate how this works. Previously we had a single responder that makes lengthy calls and can return only a single message response.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bf6tcj12f3lcmtqje1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8bf6tcj12f3lcmtqje1z.png" alt="Flow diagram for the current webhook" width="535" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we’ll modify the responder to instead just post an event to Cloud Pub/Sub, and return a message saying “I’m working on it…” Then, the Pub/Sub event will trigger another Cloud Function that performs the actual work. This Pub/Sub initiated function is not subject to the 3 second time limit, and can post an arbitrary number of messages to the channel by calling the Discord API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69gpgripjvlyxajsw6b7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69gpgripjvlyxajsw6b7.png" alt="Flow diagram for the revised webhook" width="654" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s our goal for part 4! Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deferred processing using pub/sub
&lt;/h2&gt;

&lt;p&gt;These days, the excitement around serverless often has to do with DevOps—or more specifically, the potential to reduce or simplify DevOps. It’s true that DevOps is way too hard, and we’re making progress on improving it, but I think the long-term impact of functions-as-a-service will actually be on &lt;em&gt;software architecture&lt;/em&gt;. FaaS has a close affinity with evented systems. As applications get more distributed, and we see more interest in evented control flow to manage that complexity, Functions has the potential to make that really easy.&lt;/p&gt;

&lt;p&gt;Acknowledging that trend, Google Cloud Functions already has tight integration with Google’s Pub/Sub service. You can deploy functions that are &lt;a href="https://cloud.google.com/functions/docs/calling/pubsub" rel="noopener noreferrer"&gt;triggered on Pub/Sub messages&lt;/a&gt;, and we’ll use that strategy to implement deferred processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying a pub/sub subscriber
&lt;/h3&gt;

&lt;p&gt;Before we can handle events coming from Pub/Sub, we need to create a topic to publish to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud pubsub topics create discord-bot-topic --project=$MY_PROJECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll write and deploy a &lt;em&gt;second&lt;/em&gt; function for the deferred task.&lt;/p&gt;

&lt;p&gt;First we’ll add the new function to &lt;code&gt;app.rb&lt;/code&gt;. Because it handles events coming from Pub/Sub, it takes a &lt;a href="https://github.com/cloudevents/sdk-ruby" rel="noopener noreferrer"&gt;CloudEvent&lt;/a&gt; (instead of a Rack HTTP request) as the argument. For now we’ll log the event so that we can see when events are triggered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.rb

# ...

# Existing webhook function (http)
FunctionsFramework.http "discord_webhook" do |request|
  global(:responder).respond(request)
end

# New subscriber function (cloud event)
FunctionsFramework.cloud_event "discord_subscriber" do |event|
  logger.info("Received event: #{event.data.inspect}")
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that our app now defines multiple functions. When you deploy to Cloud Functions, you deploy one function at a time, and you must specify which one to deploy by name. So to deploy this new Function, we identify it by its name &lt;code&gt;"discord_subscriber"&lt;/code&gt; and indicate that it should be triggered from the Pub/Sub topic we created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud functions deploy discord_subscriber \
    --project=$MY_PROJECT --region=us-central1 \
    --trigger-topic=discord-bot-topic --entry-point=discord_subscriber \
    --runtime=ruby27
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, instead of triggering on http requests, we configure this function to trigger when a message is published to our pubsub topic. We can test this by publishing to the topic manually, and then looking at the Cloud Functions logs to see the log entry written by our function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud pubsub topics publish discord-bot-topic \
    --project=$MY_PROJECT --message=hello
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Publishing an event
&lt;/h3&gt;

&lt;p&gt;But what we actually want is for our &lt;em&gt;webhook&lt;/em&gt; to publish the event. So we’ll start by adding the client library for the Pub/Sub API to our Gemfile.&lt;/p&gt;

&lt;p&gt;Now, normally, I’d recommend using the main &lt;a href="https://rubygems.org/gems/google-cloud-pubsub" rel="noopener noreferrer"&gt;google-cloud-pubsub&lt;/a&gt; gem for interacting with Pub/Sub, but for this app, we’re actually going to use the lower-level &lt;a href="https://rubygems.org/gems/google-cloud-pubsub-v1" rel="noopener noreferrer"&gt;google-cloud-pubsub-v1&lt;/a&gt; library. This is to improve cold start time. The higher-level library takes measurably longer to load in the Cloud Functions environment, and with a three-second budget, any milliseconds we can shave off will be useful. Addintionally, for our purposes, we only need to publish a single message, and don’t need the advanced subscription management code in the higher-level library.&lt;/p&gt;

&lt;p&gt;So let’s add the library to our Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile

source "https://rubygems.org"

gem "ed25519", "~&amp;gt; 1.2"
gem "faraday", "~&amp;gt; 1.4"
gem "functions_framework", "~&amp;gt; 0.9"
gem "google-cloud-pubsub-v1", "~&amp;gt; 0.4"
gem "google-cloud-secret_manager", "~&amp;gt; 1.1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After bundle installing, we’ll update our Responder class to publish a message to trigger our job. First, we construct a pubsub client in the initialize method. Again, we’re using the low-level publisher client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

require "google/cloud/pubsub/v1/publisher"

class Responder

  # ...

  def initialize(api_key:)
    # Create a verification key (from part 1)
    public_key = DISCORD_PUBLIC_KEY
    public_key_binary = [public_key].pack("H*")
    @verification_key = Ed25519::VerifyKey.new(public_key_binary)

    # Create a Bible API client (from part 3)
    @bible_api = BibleApi.new(api_key: api_key)

    # Create a Pub/Sub client
    @pubsub = Google::Cloud::PubSub::V1::Publisher::Client.new
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can enhance the &lt;code&gt;handle_command&lt;/code&gt; method to publish an event. For now we’ll keep the existing functionality (i.e. calling the Bible API and returning its content), and we’ll just provide additional code to publish to Pub/Sub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

class Responder

  PROJECT_ID = "my-project-id"
  TOPIC_NAME = "discord-bot-topic"

  # ...

  def handle_command(interaction)
    reference = reference_from_interaction(interaction)

    # Publish a simple pubsub event
    topic = "projects/#{PROJECT_ID}/topics/#{TOPIC_NAME}"
    attributes = {message: "Looked up #{reference}"}
    @pubsub.publish(topic: topic, messages: [{attributes: attributes}])

    # We'll leave this here for now.
    content = @bible_api.lookup_passage(reference)
    {
      type: 4,
      data: {
        content: "#{reference}\n#{content}"
      }
    }
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s redeploy the webhook function to put this change into production. Recall the command to deploy the webhook function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud functions deploy discord_webhook \
    --project=$MY_PROJECT --region=us-central1 \
    --trigger-http --entry-point=discord_webhook \
    --runtime=ruby27 --allow-unauthenticated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After redploying, we can now invoke the comamnd in Discord, and now in addition to seeing the response in Discord, we can look at the Cloud Function logs and see it trigger the subscriber function.&lt;/p&gt;

&lt;p&gt;So far so good—we now know how to trigger background tasks in Cloud Functions using Pub/Sub. Next, we’ll use this mechanism to support splitting a long passage across multiple chat postings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a multi-part response
&lt;/h2&gt;

&lt;p&gt;For commands that require a longer time to process, or that need to post mulitple responses to the channel, Discord &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#interaction-response" rel="noopener noreferrer"&gt;recommends&lt;/a&gt; returning an initial “deferred” response (which displays a “thinking” message in the channel) and following it up with additional responses sent via the Discord API. We’ll now change our response to a deferred response, and move the Scripture lookup logic into the Pub/Sub subscriber.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending a deferred response
&lt;/h3&gt;

&lt;p&gt;A deferred response is simply a JSON message with the type set to 5. We’ll delete our code that calls the Bible API, and just return a static response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

class Responder

  # ...

  def handle_command(interaction)
    reference = reference_from_interaction(interaction)

    # Publish a simple pubsub event
    topic = "projects/#{PROJECT_ID}/topics/#{TOPIC_NAME}"
    attributes = {message: "Looked up #{reference}"}
    @pubsub.publish(topic: topic, messages: [{attributes: attributes}])

    # Return a Type 5 response
    { type: 5 }
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, in order to perform the logic, the deferred task needs two pieces of information. First is, of course, the scripture reference to look up. And second is the &lt;em&gt;interaction token&lt;/em&gt; which lets us associate additional responses we send, to the original request. So we’ll update the Pub/Sub message to include these two pieces of information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

class Responder

  # ...

  def handle_command(interaction)
    reference = reference_from_interaction(interaction)

    # Publish a pubsub event including the reference and interaction token
    topic = "projects/#{PROJECT_ID}/topics/#{TOPIC_NAME}"
    attributes = {reference: reference, token: interaction["token"]}
    @pubsub.publish(topic: topic, messages: [{attributes: attributes}])

    # Return a Type 5 response
    { type: 5 }
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, since we no longer use the BibleApi class here in the Responder, we no longer need to pass in the &lt;code&gt;api_key&lt;/code&gt;. So we can remove that code from the Responder’s initialize method. And we can remove the &lt;code&gt;on_startup&lt;/code&gt; code that loads the API key from secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

require "google/cloud/pubsub/v1/publisher"

class Responder

  # ...

  def initialize
    # Create a verification key (from part 1)
    public_key = DISCORD_PUBLIC_KEY
    public_key_binary = [public_key].pack("H*")
    @verification_key = Ed25519::VerifyKey.new(public_key_binary)

    # We can now delete this code
    # @bible_api = BibleApi.new(api_key: api_key)

    # Create a Pub/Sub client
    @pubsub = Google::Cloud::PubSub::V1::Publisher::Client.new
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploying this webhook update, we can see the effect on the command. It now displays a “thinking” message in response to the “type 5” response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8f075o852pve67jtvic.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8f075o852pve67jtvic.png" alt="Displaying a thinking message" width="432" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a deferred task handler
&lt;/h3&gt;

&lt;p&gt;Now that we’re going to write some non-trivial code for the Pub/Sub-triggered Function, let’s create a class for it, like we did previously for the Responder class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# deferred_task.rb

class DeferredTask
  def initialize
    # TODO
  end

  def handle_event(event)
    # TODO
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to create a DeferredTask object on cold start, we can add code to the &lt;code&gt;on_startup&lt;/code&gt; block, just as we did for the Responder class. But hang on… we now have two separate functions—the webhook uses only the Responder class but not DeferredTask, and the subscriber uses only the DeferredTask class but not Responder. It would be nice to for each function to construct only the objects that it needs. This will be especially important in the next section because the DeferredTask will require a round-trip to the Secret Manager, and we’d like to avoid needlessly incurring that latency in the webhook Function.&lt;/p&gt;

&lt;p&gt;To ensure each Function constructs only the objects that it needs, we can use &lt;a href="https://cloud.google.com/functions/docs/bestpractices/tips#do_lazy_initialization_of_global_variables" rel="noopener noreferrer"&gt;lazy initialization of globals&lt;/a&gt;. The Ruby Functions Framework supports this feature by passing a block to &lt;code&gt;set_global&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.rb

FunctionsFramework.on_startup do
  # Define how to construct a Responder
  set_global(:responder) do
    # This does not actually run until the discord_webhook
    # function actually accesses it.
    require_relative "responder"
    Responder.new
  end

  # Define how to construct a DeferredTask
  set_global(:deferred_task) do
    # This does not actually run until the discord_subscriber
    # function actually accesses it.
    require_relative "deferred_task"
    DeferredTask.new
  end
end

# Call the Responder from the webhook function
FunctionsFramework.http "discord_webhook" do |request|
  global(:responder).respond(request)
end

# Call the DeferredTask from the pubsub subscriber function
FunctionsFramework.cloud_event "discord_subscriber" do |event|
  global(:deferred_task).handle_event(event)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how even the &lt;code&gt;require&lt;/code&gt; instructions are executed lazily, ensuring the libraries are loaded only if they’re actually going to be used. This can often make a significant difference in a serverless or container-based environment where file system access may be relatively slow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Passing secrets to the deferred task
&lt;/h3&gt;

&lt;p&gt;Now for the functionality of DeferredTask. We’ll need to call both the Bible API and the Discord API, so we’ll need both the Bible API key and the Discord Bot Token. In Part 3, we set up the Bible API key in a local file for local testing, and uploaded it to the Google Secret Manager for production. Now let’s enhance that Secrets class to add support for the Discord Bot Token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# secrets.rb

# ...

class Secrets

  # ...

  attr_reader :bible_api_key
  attr_reader :discord_bot_token

  # ..

  def load_from_hash(hash)
    @bible_api_key = hash["bible_api_key"]
    @discord_bot_token = hash["discord_bot_token"]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add &lt;code&gt;discord_bot_token&lt;/code&gt; to the secrets.yaml file. (Once again, you can find the token in your bot’s page in the Discord developer console.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# secrets.yaml

bible_api_key: "mybibleapikey12345"
discord_bot_token: "mydiscordbottoken12345"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And upload the updated file to Secret Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud secrets versions add discord-bot-secrets \
    --project=$MY_PROJECT --data-file=secrets.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s update our startup code to access these secrets and pass them into the DeferredTask constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.rb

# ...

FunctionsFramework.on_startup do

  # ...

  # Define how to construct a DeferredTask
  set_global(:deferred_task) do
    # This does not actually run until the discord_subscriber
    # function actually accesses it.
    require_relative "secrets"
    require_relative "deferred_task"
    secrets = Secrets.new
    DeferredTask.new(api_key: secrets.bible_api_key,
                     bot_token: secrets.discord_bot_token)
  end
end

# ...

# deferred_task.rb

require_relative "bible_api"
require_relative "discord_api"

class DeferredTask

  def initialize(api_key:, bot_token:)
    @bible_api_client = BibleApi.new(api_key: api_key)
    @discord_api_client = DiscordApi.new(bot_token: bot_token)
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we’ve accessed the secrets and constructed the needed clients, we’re ready to implement the task.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sending multiple ressages
&lt;/h3&gt;

&lt;p&gt;Our DeferredTask will make two types of calls to the Discord API. First, we’ll call &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#edit-original-interaction-response" rel="noopener noreferrer"&gt;Edit Original Interaction Response&lt;/a&gt; to update the original response (which was a Type 5 “thinking” message) to indicate that the bot is done “thinking” and is ready to display content. Then we’ll call &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#create-followup-message" rel="noopener noreferrer"&gt;Create Followup Message&lt;/a&gt; potentially multiple times, to post messages with Scripture content.&lt;/p&gt;

&lt;p&gt;First, let’s implement those calls in our Discord API client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# discord_api.rb

# ,,,

class DiscordApi

  # ...

  def edit_interaction_response(interaction_token, content)
    data_json = JSON.dump({content: content})
    headers = {"Content-Type" =&amp;gt; "application/json"}
    call_api("/webhooks/#{@client_id}/#{interaction_token}/messages/@original",
             method: :patch, body: data_json, headers: headers)
  end

  def create_followup(interaction_token, content)
    data_json = JSON.dump({content: content})
    headers = {"Content-Type" =&amp;gt; "application/json"}
    call_api("/webhooks/#{@client_id}/#{interaction_token}",
             method: :post, body: data_json, headers: headers)
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, implement the logic in DeferredTask. For simplicity, this code will naively split the content into 2000-character chunks. For a better experience, we’d probably want to split on word boundaries, or even better, verse boundaries.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# deferred_task.rb

class DeferredTask

  # ...

  def handle_event(event)
    # Get data from the Pub/Sub message
    attributes = event.data["message"]["attributes"]
    reference = attributes["reference"]
    interaction_token = attributes["token"]

    # Get the content from the Bible API
    full_content = @bible_api_client.lookup_passage(reference)

    # Edit the original interaction response to signal we're done "thinking"
    @discord_api_client.edit_interaction_response(
      interaction_token, "Looked up #{reference}")

    # Split the content into 2000-character sections and send Discord meessages
    full_content.scan(/.{1,2000}/m) do |section|
      # Space API calls out a bit, to avoid Discord's rate limiting.
      sleep 0.5
      @discord_api_client.create_followup(interaction_token, section)
    end
  end

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redeploy both the webhook and the subscriber, and our final app is ready!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndhjapbma69itr29tuon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fndhjapbma69itr29tuon.png" alt="The final output" width="797" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;We now have a working, nontrivial Discord command, running in Google Cloud Functions! We’ve also covered some of the key techniques in a robust serverless app, including handling secrets in production, and using a pub/sub system to schedule tasks.&lt;/p&gt;

&lt;p&gt;We do still have a few TODOs, which I will leave as “exercises for the reader”. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;It would make for a better experience to parse “normal” Scripture reference syntax such as “Matthew 1:2-3” rather than forcing users to use the Bible API’s reference format. When I wrote this app for my church, I also used a string distance metric to detect book name abbreviations such as “Matt” or “Mt”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Currently, if the Bible API returns an error (e.g. because the Scripture reference was invalid), our BibleApi client raises an exception. This causes the deferred task function to terminate, but the user doesn’t see any indication of this in the discord channel. It would be a good idea to catch any exceptions and post an error message to the channel.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It would be nice to split content into sections on verse boundaries rather than just taking 2000-character chunks. When I wrote this for my church, I actually passed an option to the Bible API call that causes it to return the passage in semantic structured JSON rather than a simple string. I then had to parse that data structure to construct a displayable string, but because it was structured data, I could extract the exact verse boundaries, and customize the output format.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, there are alternative approaches that could be explored. One might consider, for example, using a background task manager such as &lt;a href="https://cloud.google.com/tasks" rel="noopener noreferrer"&gt;Google Cloud Tasks&lt;/a&gt; instead of Pub/Sub to schedule the deferred task, and there are pros and cons to that approach. Discord also supports receiving events &lt;a href="https://discord.com/developers/docs/topics/gateway" rel="noopener noreferrer"&gt;via WebSocket&lt;/a&gt; rather than a webhook. This option could provide much better performance, but would require reworking much of the design and deployment of the app, and may increase the cost of running it.&lt;/p&gt;

&lt;p&gt;But overall, the techniques that we’ve covered should provide a good foundation for writing a variety of serverless applications using Google Cloud Platform. It should also provide a good set of getting-started information on writing Discord apps. I hope it was helpful!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>discord</category>
    </item>
    <item>
      <title>Building a Discord Command in Ruby on Google Cloud Functions: Part 3</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Mon, 03 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-3-2e2l</link>
      <guid>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-3-2e2l</guid>
      <description>&lt;p&gt;This is the third of a four-part series on writing a &lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;Discord “slash” command&lt;/a&gt; in &lt;a href="https://www.ruby-lang.org" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt; using &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Google Cloud Functions&lt;/a&gt;. In previous parts, we deployed a Discord app to Google Cloud Functions, and added a command to a server using the Discord API. In this part, we actually implement the command, calling an external API and displaying results in the channel. It will illustrate how to respond to commands, and how to handle secrets such as API keys in production.&lt;/p&gt;

&lt;p&gt;Previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-g25"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l"&gt;Part 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Responding to commands
&lt;/h2&gt;

&lt;p&gt;When we left off in &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l"&gt;part 2&lt;/a&gt;, we had created a command in a Discord server, but hadn’t yet implemented the back end. We’ll do so now.&lt;/p&gt;

&lt;p&gt;When someone invokes a command, an &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#interaction" rel="noopener noreferrer"&gt;interaction request&lt;/a&gt; of type 2 gets sent to our webhook. The JSON data sent with this request will include the command that was sent, and any options that were provided. For our command, that includes a Scripture reference.&lt;/p&gt;

&lt;p&gt;To start off, let’s update the &lt;code&gt;Responder#respond&lt;/code&gt; method to recognize type 2, and call a new method &lt;code&gt;handle_command&lt;/code&gt; that we will implement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

class Responder

  # ...

  def respond(rack_request)
    raw_body = rack_request.body.read
    unless verify_request(raw_body, rack_request.env)
      # Discord expects a 401 response if the verification failed
      return [401,
        {"Content-Type" =&amp;gt; "text/plain"},
        ["invalid request signature"]]
    end
    interaction = JSON.parse(raw_body)
    case interaction["type"]
    when 1
      # Ping
      {type: 1}
    when 2
      # Command
      handle_command(interaction)
    else
      [400,
        {"Content-Type" =&amp;gt; "text/plain"},
        ["Unrecognized interaction type"]]
    end
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the &lt;code&gt;handle_command&lt;/code&gt; method. Discord expects an &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#interaction-response" rel="noopener noreferrer"&gt;interaction response&lt;/a&gt; JSON message in the HTTP response. Typically, the response includes a message that the command will then display in the Discord channel. We signal such a response by setting the “type” field to 4, and returning the text to display in a “content” field.&lt;/p&gt;

&lt;p&gt;To start off, let’s construct a response by echoing back the Scripture reference that we were asked to look up. First we’ll write code to parse the interaction and get the option with name “reference”. For simplicity, we’ll just raise an exception (which Cloud Functions will translate to a 500 response) if the command data doesn’t have the expected format.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

class Responder

  # ...

  def reference_from_interaction(interaction)
    command_data = interaction["data"]
    unless command_data["name"] == "bible"
      raise "Unexpected command: #{command_data['name']}"
    end
    command_data["options"].each do |option|
      if option["name"] == "reference"
        return option["value"]
      end
    end
    raise "No reference option found"
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll use that to implement &lt;code&gt;handle_command&lt;/code&gt; to return a message to display. Remember that if we return a hash from a Cloud Function, it is automatically rendered as JSON in the HTTP response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

class Responder

  # ...

  def handle_command(interaction)
    reference = reference_from_interaction(interaction)
    {
      type: 4,
      data: {
        content: "You looked up #{reference}"
      }
    }
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s redeploy the function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install
$ gcloud functions deploy discord_webhook \
    --project=$MY_PROJECT --region=us-central1 \
    --trigger-http --entry-point=discord_webhook \
    --runtime=ruby27 --allow-unauthenticated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when we go back to our Discord channel and invoke the command… success!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnlyiw40eirqj5gbjnu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnlyiw40eirqj5gbjnu2.png" alt="Invoking a command" width="564" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbezwka48e6kl9fp4u6hx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbezwka48e6kl9fp4u6hx.png" alt="Simple output" width="651" height="79"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling an external API
&lt;/h2&gt;

&lt;p&gt;Now it’s time to implement the main functionality of our command, that of actually returning Scripture content from an API. We’ll access a suitable API, show how to manage its API key in production using &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Google Secret Manager&lt;/a&gt;, and write code to make API calls and include the results in the command response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Signing up with API.Bible
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://scripture.api.bible" rel="noopener noreferrer"&gt;API.Bible&lt;/a&gt; is a basic Scripture lookup API that boasts thousands of versions and translations, and allows reference lookup and keyword search. Their free tier includes access to public domain translations like the World English Bible, and a modest number of calls per day that will be more than adequate for my church’s needs.&lt;/p&gt;

&lt;p&gt;I created an account and registered an application. The site claims that new accounts are subject to approval, but it looks like, since mine was for non-commercial use by a small church, it pretty much went through immediately. As part of my account, they gave me an API key. This is effectively a password to my account, and I need to handle it like any secret, keeping it out of code, source control, and any unencrypted communication.&lt;/p&gt;

&lt;p&gt;The API website also includes “live” &lt;a href="https://scripture.api.bible/livedocs" rel="noopener noreferrer"&gt;reference documentation&lt;/a&gt; that includes the ability to make API calls directly from the web page. From here, I identified the “passages” API call as the likely call I’ll need for my Discord command. The resource path for the request includes the ID of the Bible version, and the reference in a specific format. Then the response is a JSON payload including the passage content. Looks good, let’s experiment with it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Calling the API
&lt;/h3&gt;

&lt;p&gt;As we did in part 2 with the Discord API, we’ll create a simple client class for the Bible API. We’ll start with a helper method that makes HTTP calls and handles results. This helper will also set the needed API key header. As we did with the Discord client, we’ll pass in the API key as a constructor argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# bible_api.rb

require "faraday"
require "json"

class BibleApi
  def initialize(api_key:)
    @api_key = api_key
  end

  private

  def call_api(path, params: nil)
    faraday = Faraday.new(
      url: "https://api.scripture.api.bible",
      headers: {
        "api-key" =&amp;gt; @api_key
      }
    )
    response = faraday.get(path) do |req|
      req.params = params if params
    end
    body = JSON.parse(response.body)
    unless response.status == 200 &amp;amp;&amp;amp; body["data"]
      raise body["message"] || "Unknown error"
    end
    body["data"]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’ll implement a method for the “passages” call. It will take the reference as an argument. For simplicity, we’ll just hard-code the Bible ID for a reasonable translation, the World English Bible, Ecumenical version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# bible_api.rb

# ...

class BibleApi

  WEB_BIBLE_ID = "9879dbb7cfe39e4d-01"

  # ...

  def lookup_passage(reference)
    params = {
      "content-type" =&amp;gt; "text",
      "include-titles" =&amp;gt; "false"
    }
    resource = "/v1/bibles/#{WEB_BIBLE_ID}/passages/#{reference}"
    response_object = call_api(resource, params: params)
    response_object["content"]
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now test this by creating a simple command-line script using Toys. As before, we’ll pass the API key in on the command line, to keep it out of our code. We’ll also pass the Scripture reference on the command line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .toys.rb

tool "lookup" do
  flag :api_key, "--api-key TOKEN"
  required_arg :reference

  def run
    require_relative "bible_api"
    client = BibleApi.new(api_key: api_key)
    result = client.lookup_passage(reference)
    puts result
  end
end

# ... and other scripts from before
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the Bible API uses a particular format for Scripture references. We need to use that format when we test our API client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ toys lookup --api-key=$MY_API_KEY JHN.1.1
     [1] In the beginning was the Word, and the Word was with God, and the Word was God.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Calling the API from the webhook
&lt;/h3&gt;

&lt;p&gt;Now that we have the Bible API working, we can write the rest of the code to integrate it into our webhook. We’ll construct a Bible API client in our Responder class, and rewrite our &lt;code&gt;Responder#handle_command&lt;/code&gt; method to call it to get the passage content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

# ...

require_relative "bible_api"

class Responder

  # ...

  def initialize(api_key:)
    # Create a verification key (from part 1)
    public_key = DISCORD_PUBLIC_KEY
    public_key_binary = [public_key].pack("H*")
    @verification_key = Ed25519::VerifyKey.new(public_key_binary)

    # Create a Bible API client
    @bible_api = BibleApi.new(api_key: api_key)
  end

  # ...

  def handle_command(interaction)
    # Call the Bible API to get the content to return
    reference = reference_from_interaction(interaction)
    content = @bible_api.lookup_passage(reference)
    {
      type: 4,
      data: {
        content: "#{reference}\n#{content}"
      }
    }
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, we’re almost there. We have a working Bible API client, and our webhook command handler calls it to get content, and returns it to Discord for display. There’s just one problem. The Bible API client needs an API key. We’ve added an &lt;code&gt;api_key&lt;/code&gt; argument to the Responder constructor so we can pass it down, but where does the Responder get the API key from? Remember from part 1 that a Responder is created when the function starts up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.rb

require "functions_framework"
require_relative "responder"

FunctionsFramework.on_startup do
  set_global(:responder, Responder.new(api_key: "???"))
end

FunctionsFramework.http "discord_webhook" do |request|
  global(:responder).respond(request)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Somehow we need to pass a secret into this app, and do so safely and securely. Let’s talk about how to do that.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling secrets
&lt;/h3&gt;

&lt;p&gt;There are many ways to handle secrets without exposing them in our code. In the past, it has been common to set secrets in environment variables, or store them in special source files that we exclude from source control. These techniques work, but each has its flaws. Environment variables can be logged by accident or read by malicious code. Files can be accessed by anyone with access to the deployment image.&lt;/p&gt;

&lt;p&gt;The technique we’ll implement here uses a local file for local testing, but uses a secret handling service, &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Google Cloud Secret Manager&lt;/a&gt;, for production. When we test locally, the secrets will be present in a local file that is excluded from source control, letting us test without having to make calls to a cloud service. When we deploy, however, we &lt;em&gt;exclude&lt;/em&gt; this file, avoiding the security risk of having the file present in our deployment image. Instead, in production, we load the secret directly into memory from the Secret Manager service.&lt;/p&gt;

&lt;p&gt;So the basic logic is: first check for a file, and if that’s not present, invoke Secret Manager. We’ll implement this logic in a new class, Secrets.&lt;/p&gt;

&lt;p&gt;First, we’ll add the client library for Secret Manager to our Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile

source "https://rubygems.org"

gem "ed25519", "~&amp;gt; 1.2"
gem "faraday", "~&amp;gt; 1.4"
gem "functions_framework", "~&amp;gt; 0.9"
gem "google-cloud-secret_manager", "~&amp;gt; 1.1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t forget to &lt;code&gt;bundle install&lt;/code&gt; to ensure your &lt;code&gt;Gemfile.lock&lt;/code&gt; gets updated.&lt;/p&gt;

&lt;p&gt;Now let’s implement the Secrets class. In the code below, make sure you substitute the name of your Google Cloud project as the value of the&lt;code&gt;PROJECT_ID&lt;/code&gt; constant. I’ve also chosen a name for the secret in the &lt;code&gt;SECRET_NAME&lt;/code&gt; constant, but you can use a different name if you want.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# secrets.rb

require "psych"
require "google/cloud/secret_manager"

class Secrets
  SECRETS_FILE = File.join( __dir__ , "secrets.yaml")
  SECRET_NAME = "discord-bot-secrets"
  PROJECT_ID = "my-project-id"

  def initialize
    if File.file?(SECRETS_FILE)
      load_from_file
    else
      load_from_secret_manager
    end
  end

  attr_reader :bible_api_key

  def load_from_file
    secret_data = Psych.load_file(SECRETS_FILE)
    load_from_hash(secret_data)
  end

  def load_from_secret_manager
    secret_manager = Google::Cloud::SecretManager.secret_manager_service
    version_name = secret_manager.secret_version_path(
      project: PROJECT_ID, secret: SECRET_NAME, secret_version: "latest"
    )
    version_data = secret_manager.access_secret_version(name: version_name)
    secret_data = Psych.load(version_data.payload.data)
    load_from_hash(secret_data)
  end

  def load_from_hash(hash)
    @bible_api_key = hash["bible_api_key"]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can invoke this class from our function and use it to retrieve the API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# app.rb

require "functions_framework"
require_relative "responder"
require_relative "secrets"

FunctionsFramework.on_startup do
  secrets = Secrets.new
  set_global(:responder, Responder.new(api_key: secrets.bible_api_key))
end

FunctionsFramework.http "discord_webhook" do |request|
  global(:responder).respond(request)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get this to work for local testing, we just need to create the “secrets.yaml” file, substituting your actual API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# secrets.yaml

bible_api_key: "mybibleapikey12345"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure it stays out of source control. I use git, so I’ll add a &lt;code&gt;.gitignore&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .gitignore

secrets.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also want to avoid &lt;em&gt;deploying&lt;/em&gt; this file. By default, Cloud Functions honors &lt;code&gt;.gitignore&lt;/code&gt; when deploying, so it will do this by default. But if you don’t use git, or you have more complex requirements, you can configure which files are omitted from deployment by providing a &lt;a href="https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore" rel="noopener noreferrer"&gt;gcloudignore file&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In production, we want to read this data from Secret Manager, so we’ll need to upload it to Secret Manager first, using the Google Cloud command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud secrets create discord-bot-secrets \
    --project=$MY_PROJECT --data-file=secrets.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s one more thing we need to do. We’ve written code that loads the secrets from Secret Manager in production, but we need to grant that code &lt;em&gt;access&lt;/em&gt; to Secret Manager. To do so, we note that when an app runs in Cloud Functions, by default it uses a particular service account called &lt;code&gt;$MY_PROJECT@appspot.gserviceaccount.com&lt;/code&gt; to talk to Google Cloud APIs. So we need to grant that service account access to the secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud secrets add-iam-policy-binding discord-bot-secrets \
    --project=$MY_PROJECT --role=roles/secretmanager.secretAccessor \
    --member=serviceAccount:$MY_PROJECT@appspot.gserviceaccount.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For both the above commands, make sure the project name and the secret name &lt;code&gt;discord-bot-secrets&lt;/code&gt; matches the corresponding names you used in the constants in the Secrets class.&lt;/p&gt;

&lt;p&gt;Now we can redeploy our Function, and try it out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnlyiw40eirqj5gbjnu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnlyiw40eirqj5gbjnu2.png" alt="Invoking a command" width="564" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr3kx4y440psqkc1u7p01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr3kx4y440psqkc1u7p01.png" alt="Displaying a short passage" width="788" height="147"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Woot!&lt;/p&gt;

&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;When I got to this point, things were mostly working, but I encountered a few issues.&lt;/p&gt;

&lt;p&gt;The bot wasn’t very stable. Sometimes, the first time I tried running a command, I’d get a failure message, but then the next time it would work. Oddly, when I looked at the logs in Cloud Functions, everything seemed to be fine, even for the supposedly failed request.&lt;/p&gt;

&lt;p&gt;The cause, it turns out, was that Discord will wait for a &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#responding-to-an-interaction" rel="noopener noreferrer"&gt;maximum of 3 seconds&lt;/a&gt; for a response, and if it doesn’t receive one in a timely manner, it will give up and display an error. This can sometimes be a challenge for a webhook that makes API requests to external services, if those calls may incur significant latency. It’s also a particular issue for serverless hosting. An environment like Cloud Functions might shut down your app if it’s not in use, in order to conserve resources. Then, the next time it receives a request, it might take some time to boot back up. This is called a “cold start” in serverless lingo, and it can be a challenge for app developers to keep it low. In addition to calling the Bible API, our app makes a call out to the Secret Manager during cold start, and sometimes the latency of that additional call is enough to push the entire cold start over the 3 second threshold.&lt;/p&gt;

&lt;p&gt;Additionally, the syntax that the Bible API requires to specify the scripture reference, wasn’t very nice. Culturally, we’re used to a syntax like “John 1:1-5” rather than “JHN.1.1-JHN.1.5”. So I had to write a parser for “human” references, and convert them to the format needed by the Bible API.&lt;/p&gt;

&lt;p&gt;Finally, displaying longer passages would also fail. There was no way, for example, to display the entire first chapter of John. Even though the Bible API would return the passage content, it turns out that Discord imposes a &lt;a href="https://discord.com/developers/docs/resources/webhook#execute-webhook" rel="noopener noreferrer"&gt;2000-character limit&lt;/a&gt; on message length, and it refused to post a message that is any longer.&lt;/p&gt;

&lt;p&gt;So I had a working command, but I wasn’t really satisfied with the results. Parsing reference syntax is just programming, and I’ll leave improving it as an exercise for the reader. But the latency and the longer passages were a real usability hindrance. The good news is, there are solutions, and we’ll explore them in &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-4-3ch1"&gt;part 4&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>discord</category>
    </item>
    <item>
      <title>Building a Discord Command in Ruby on Google Cloud Functions: Part 2</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Sun, 02 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l</link>
      <guid>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l</guid>
      <description>&lt;p&gt;This is the second of a four-part series on writing a &lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;Discord “slash” command&lt;/a&gt; in &lt;a href="https://www.ruby-lang.org" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt; using &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Google Cloud Functions&lt;/a&gt;. In the previous part, we set up a Discord application and deployed its webhook to Google Cloud Functions. In this part, we investigate how to add a command to a Discord channel. It will illustrate how to authenticate and interact with the Discord API, how to use it to add a command to a Discord server, and demonstrate some nice tools for invoking these tasks from the command line.&lt;/p&gt;

&lt;p&gt;Previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-g25"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20"&gt;Part 1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing on a Discord server
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20"&gt;part 1&lt;/a&gt;, we set up a Discord application. But to make commands available in a Discord server, we’ll need to add our application to the server.&lt;/p&gt;

&lt;p&gt;First, we’ll create a &lt;em&gt;Bot user&lt;/em&gt; for the application. According to the &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#slash-commands-interactions-and-bot-users" rel="noopener noreferrer"&gt;Discord documentation&lt;/a&gt;, this is not always necessary for slash commands, but in our case we’ll need it later to make additional API calls. Back in the application page in the Discord Developer console, under the Bot tab, click “Add Bot.”&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoo15x7cbf1eqev353qs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffoo15x7cbf1eqev353qs.png" alt="Adding a new bot" width="800" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to add the bot to a Discord server (also known as a “guild” in the Discord API). More precisely, we’re granting it permissions in the guild to behave as a bot user and to respond to commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.com/developers/docs/topics/oauth2#bots" rel="noopener noreferrer"&gt;Discord’s documentation&lt;/a&gt; shows how you can authorize a bot via an OAuth2 flow. If you have “manage server” permissions on the guild, you can go to a particular page and grant guild permissions to the bot. The URL for that page looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://discord.com/api/oauth2/authorize?client_id=MY_APPLICATION_ID&amp;amp;scope=bot%20applications.commands

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

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;MY_APPLICATION_ID&lt;/code&gt; with the application ID from the Discord application’s page in the Discord developer console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygdkuuxr8c7k86ipbce3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygdkuuxr8c7k86ipbce3.png" alt="Authorizing a bot with a server" width="452" height="788"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the authorization page, choose the server from the dropdown, and authorize the application. Once this is done, the bot will show up as a user in the server. Behind the scenes, it will have the &lt;code&gt;bot&lt;/code&gt; and &lt;code&gt;applications.commands&lt;/code&gt; permissions that it will need to implement commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a command
&lt;/h2&gt;

&lt;p&gt;Next we’ll use the bot to create a command on the Discord server. This requires making calls to the Discord API. In this section, I’ll demonstrate how to call the API, provide credentials with those calls, and perform them from the command line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Toys Scripts
&lt;/h3&gt;

&lt;p&gt;I kind of wish Discord had a UI in its console for registering and managing commands, or at least an official CLI that will do so. As it is, creating a command is pretty involved and you pretty much have to write code to make an API call. To make it a bit easier to invoke this code, we’ll use the &lt;a href="https://rubygems.org/gems/toys" rel="noopener noreferrer"&gt;Toys&lt;/a&gt; gem to write quick command line scripts. Toys works similar to the familiar tool Rake, but is designed specifically for writing command line tools rather than make-style dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gem install toys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing a hello-world script is simple. Create a file called &lt;code&gt;.toys.rb&lt;/code&gt; (the leading period is important.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .toys.rb

tool "hello" do
  def run
    puts "Hello, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script can be run using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ toys hello
Hello, world!
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below, we’ll use scripts like this to run code that talks to the API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing an API client
&lt;/h3&gt;

&lt;p&gt;There are a few gems out there for Discord—I briefly looked into &lt;a href="https://github.com/shardlab/discordrb" rel="noopener noreferrer"&gt;discordrb&lt;/a&gt;—but the needs of this bot are simple, and it’s instructive to roll your own client. Start by adding the HTTP client library &lt;a href="https://lostisland.github.io/faraday/" rel="noopener noreferrer"&gt;Faraday&lt;/a&gt; to your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile

source "https://rubygems.org"

gem "ed25519", "~&amp;gt; 1.2"
gem "faraday", "~&amp;gt; 1.4"
gem "functions_framework", "~&amp;gt; 0.9"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start a basic API client class. First, a helper method that makes different kinds of API calls and handles results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# discord_api.rb

require "faraday"
require "json"

class DiscordApi

  private

  def call_api(path,
               method: :get,
               body: nil,
               params: nil,
               headers: nil)
    faraday = Faraday.new(url: "https://discord.com")
    response = faraday.run_request(method, "/api/v9#{path}", body, headers) do |req|
      req.params = params if params
    end
    unless (200...300).include?(response.status)
      raise "Discord API failure: #{response.status} #{response.body.inspect}"
    end
    return nil if response.body.nil? || response.body.empty?
    JSON.parse(response.body)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s write a method that &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#get-guild-application-commands" rel="noopener noreferrer"&gt;lists the commands&lt;/a&gt; defined in a server by a Discord application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# discord_api.rb

require "faraday"
require "json"

class DiscordApi
  DISCORD_APPLICATION_ID = "838132693479850004"
  DISCORD_GUILD_ID = "828125771288805436"

  def initialize
    @client_id = DISCORD_APPLICATION_ID
    @guild_id = DISCORD_GUILD_ID
  end

  def list_commands
    call_api("/applications/#{@client_id}/guilds/#{@guild_id}/commands")
  end

  private

  # def call_api...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;DISCORD_APPLICATION_ID&lt;/code&gt; is the Application ID of the Discord app (from the application’s general information page in the Discord developer console). The &lt;code&gt;DISCORD_GUILD_ID&lt;/code&gt; is the ID of the Discord server, which is part of the server URL. Replace these with the values for your app. These are not secret values, and are safe to have in code, although normally you might want to read them from environment variables or a config file.&lt;/p&gt;

&lt;p&gt;Now it should be easy to write a quick Toys script to call this method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .toys.rb

tool "list-commands" do
  def run
    require_relative "discord_api"
    result = DiscordApi.new.list_commands
    puts JSON.pretty_generate(result)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And try running it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ toys list-commands
RuntimeError: Discord API failure: 401 "{\"message\": \"401: Unauthorized\", \"code\": 0}"
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that didn’t quite work. We got a 401 Unauthorized result from the API call. And of course that makes sense: we need to provide credentials, otherwise anyone can access our commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authorizing Discord API calls
&lt;/h3&gt;

&lt;p&gt;Discord uses a variety of &lt;a href="https://discord.com/developers/docs/topics/oauth2" rel="noopener noreferrer"&gt;OAuth2 flows&lt;/a&gt; for authorization, and these can of course be complicated, especially for applications that need to act on behalf of other users. For our case, however, a simple flow will work: &lt;a href="https://discord.com/developers/docs/topics/oauth2#bots" rel="noopener noreferrer"&gt;authorizing as the bot user&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each bot is assigned a &lt;em&gt;bot token&lt;/em&gt; that it can use to authenticate to the Discord API. The bot token is a &lt;em&gt;secret value&lt;/em&gt;; it’s essentialy the bot’s password. If you own a bot, you can find it on the bot’s page in the Discord developer console. Find your Discord application, navigate to the “Bot” tab, and click the “Copy” button for the Token, to copy the token to your clipboard. It will be a series of around 60 characters.&lt;/p&gt;

&lt;p&gt;Because a token is sensitive information, it should not live in your code or source control. For now, we’ll alter our command line tool to read the token from a command line argument. In later articles, we’ll discuss strategies for accessing such secrets in production using Google Cloud Secret Manager.&lt;/p&gt;

&lt;p&gt;First, modify the DiscordApi constructor to take the bot token as an argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# discord_api.rb

# ...

class DiscordApi
  def initialize(bot_token:)
    @client_id = DISCORD_APPLICATION_ID
    @guild_id = DISCORD_GUILD_ID
    @bot_token = bot_token
  end

  # ...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token needs to be set in an authorization header in API requests. So modify the &lt;code&gt;call_api&lt;/code&gt; helper method to set this header in the Faraday object’s constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# discord_api.rb

# ...

class DiscordApi

  # ...

  private

  def call_api(path,
               method: :get,
               body: nil,
               params: nil,
               headers: nil)
    faraday = Faraday.new(url: "https://discord.com") do |conn|
      # Set the authorization header to include a token of type "Bot"
      conn.authorization(:Bot, @bot_token)
    end
    # make the request ...

  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, add a command line flag to set the token in the Toys script, and pass it into the DiscordApi constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .toys.rb

tool "list-commands" do
  flag :token, "--token TOKEN"

  def run
    require_relative "discord_api"
    client = DiscordApi.new(bot_token: token)
    result = client.list_commands
    puts JSON.pretty_generate(result)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can run the command line tool again, substituting in the actual token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ toys list-commands --token=$MY_BOT_TOKEN
[

]
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If all went well, this should now display an empty array, indicating that the application has not yet installed any commands in this server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Command
&lt;/h3&gt;

&lt;p&gt;Now we finally have all the parts in place to make an API call to create a command in a Discord server. First, we’ll add a method to the client class that calls the &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#create-guild-application-command" rel="noopener noreferrer"&gt;Create Guild Application Command API&lt;/a&gt;. This API adds a command to a specific guild. (You can also create “global commands” that apply to multiple guilds, but they’re a bit more complicated to manage, so we’ll use guild-specific commands for now.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# discord_api.rb

# ,,,

class DiscordApi

  # ...

  def create_command(command_definition)
    definition_json = JSON.dump(command_definition)
    headers = {"Content-Type" =&amp;gt; "application/json"}
    call_api("/applications/#{@client_id}/guilds/#{@guild_id}/commands",
            method: :post,
            body: definition_json,
            headers: headers)
  end

  private

  # def call_api...

end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That method takes a hash object that describes the command to create. For this project, this is a Scripture lookup command called &lt;code&gt;/bible&lt;/code&gt;, which takes one required option, the Scripture reference to look up. Here’s the definition of this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  name: "bible",
  description: "Simple Scripture lookup",
  options: [
    {
      type: 3,
      name: "reference",
      description: "Scripture reference (e.g. `JHN.1.1-JHN.1.5`)",
      required: true
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full format is specified &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#applicationcommand" rel="noopener noreferrer"&gt;in the Discord documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So I wrote a quick Toys script that takes the above description and feeds it into the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .toys.rb

tool "create-command" do
  flag :token, "--token TOKEN"

  def run
    require_relative "discord_api"
    client = DiscordApi.new(bot_token: token)
    definition = {
      name: "bible",
      description: "Simple Scripture lookup",
      options: [
        {
          type: 3,
          name: "reference",
          description: "Scripture reference (e.g. `JHN.1.1-JHN.1.5`)",
          required: true
        }
      ]
    }
    result = client.create_command(definition)
    puts JSON.pretty_generate(result)
  end
end

# The "list" command from before is still here ...
tool "list-commands" do
  flag :token, "--token TOKEN"

  def run
    require_relative "discord_api"
    client = DiscordApi.new(bot_token: token)
    result = client.list_commands
    puts JSON.pretty_generate(result)
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I was able to call the new script, again substituting in the bot token. The script creates the command and displays the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ toys create-command --token=$MY_BOT_TOKEN
{
  "id": "838195819437228113",
  "application_id": "838132693479850004",
  "name": "bible",
  "description": "Simple Scripture lookup",
  "version": "838195819437228114",
  "default_permission": true,
  "guild_id": "828125771288805436",
  "options": [
    {
      "type": 3,
      "name": "reference",
      "description": "Scripture reference (e.g. `JHN.1.1-JHN.1.5`)",
      "required": true
    }
  ]
}
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now calling list again, shows the new command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ toys list-commands --token=$MY_BOT_TOKEN
[
  {
    "id": "838195819437228113",
    "application_id": "838132693479850004",
    "name": "bible",
    "description": "Simple Scripture lookup",
    "version": "838195819437228114",
    "default_permission": true,
    "guild_id": "828125771288805436",
    "options": [
      {
        "type": 3,
        "name": "reference",
        "description": "Scripture reference (e.g. `JHN.1.1-JHN.1.5`)",
        "required": true
      }
    ]
  }
]
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;Great, we’e created a command in our Discord server! The Discord UI updates immediately, so now it should be possible to go to a chat room in the Discord server, and type &lt;code&gt;/bible&lt;/code&gt;, and see the autocomplete kick in.&lt;/p&gt;

&lt;p&gt;But of course, if you’re following along, and try to invoke an entire command:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnlyiw40eirqj5gbjnu2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwnlyiw40eirqj5gbjnu2.png" alt="Invoking a command" width="564" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…you’ll get this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgu0tnhwaabok3ex3cn82.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgu0tnhwaabok3ex3cn82.png" alt="Interaction failed" width="434" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s because we haven’t actually implemented the command in our webhook yet. That will be the topic of &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-3-2e2l"&gt;part 3&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>discord</category>
    </item>
    <item>
      <title>Building a Discord Command in Ruby on Google Cloud Functions: Part 1</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Sat, 01 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20</link>
      <guid>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20</guid>
      <description>&lt;p&gt;This is the first of a four-part series on writing a &lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;Discord “slash” command&lt;/a&gt; in &lt;a href="https://www.ruby-lang.org" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt; using &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Google Cloud Functions&lt;/a&gt;. In this part, we cover setting up a Discord bot, deploying a webhook to Cloud Functions, and validating webhook requests from Discord using the &lt;a href="https://github.com/RubyCrypto/ed25519" rel="noopener noreferrer"&gt;Ruby ed25519&lt;/a&gt; library. At the end of this part, we’ll have a working webhook that Discord recognizes, but that doesn’t yet actually implement a command.&lt;/p&gt;

&lt;p&gt;Previous articles in this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-g25"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Discord application
&lt;/h2&gt;

&lt;p&gt;Discord is a big system. Often used for gaming and streaming, but also increasingly for online community interaction, it includes a wide variety of features involving chat, voice, video, and content. Bots are an integral part of the ecosystem, and all bots start in the same place: with a Discord application.&lt;/p&gt;

&lt;p&gt;Discord’s developer portal can be accessed at &lt;a href="https://discord.com/developers" rel="noopener noreferrer"&gt;https://discord.com/developers&lt;/a&gt;. Once you’ve registered and logged in, it shows you a list of your applications. I created a new application here called &lt;code&gt;scripture-bot&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hrl2j21ijhhos0m3ufb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2hrl2j21ijhhos0m3ufb.png" alt="Screenshot of a new Discord app" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each application comes with a number of properties. These appear in the “general information” tab of the application, and include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;application ID&lt;/em&gt; is a unique number identifying your application. It’s kind of like your application’s “username”, and will be important later when we call the Discord API to register with servers and create commands.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;public key&lt;/em&gt; will be used by your bot to authenticate requests—that is, to verify that HTTP requests you receive actually came from Discord and not from someone else trying to spoof Discord.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;interactions endpoint URL&lt;/em&gt; is the URL of the webhook that will be called when someone invokes your command. It starts off empty because &lt;em&gt;you&lt;/em&gt; need to fill it in. And that’s what we’ll be doing next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also a tab labeled “Bot” that includes information about the “bot user”. It also starts off empty, but we will create a bot user later when we install our command into a Discord server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing a webhook
&lt;/h2&gt;

&lt;p&gt;A Discord application can respond to commands in two ways: via the Gateway or by implementing a webhook. The Gateway communicates over a websocket, which is flexible and low-latency, but complicated to implement, and requires running a permanent process. For this project, we’ll opt for the simpler approach of providing a webhook that Discord will call whenever a command is invoked. The webhook option has limitations, but a key advantage: it can be deployed as a serverless web app, and thus likely to be inexpensive to run if it’s not heavily used.&lt;/p&gt;

&lt;p&gt;Writing and deploying webhooks is quite easy with functions-as-a-service, or “FaaS”, a serverless architecture that models your app as a simple function that handles events. Many major cloud providers offer a FaaS environment, for example &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; from AWS, or &lt;a href="https://cloud.google.com/functions/" rel="noopener noreferrer"&gt;Cloud Functions&lt;/a&gt; from Google. For this article, we’ll use Cloud Functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hello Functions
&lt;/h3&gt;

&lt;p&gt;Deploying a hello-world app to Cloud Functions is quite simple even if you haven’t done it before. Create a project in the &lt;a href="https://console.cloud.google.com" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;, and install the &lt;a href="https://cloud.google.com/sdk" rel="noopener noreferrer"&gt;Google Cloud SDK&lt;/a&gt;, Google Cloud’s command-line tool. Then you can write a quick function called “&lt;code&gt;discord_webook&lt;/code&gt;” using the &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-ruby" rel="noopener noreferrer"&gt;Functions Framework&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile

source "https://rubygems.org"

gem "functions_framework", "~&amp;gt; 0.9"

# app.rb

require "functions_framework"

FunctionsFramework.http "discord_webhook" do |request|
  "Hello, world!\n"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can then deploy the function from the command line. Cloud Functions requires that an up-to-date Gemfile.lock file is present in order to deploy, so that means installing the bundle, then running the gcloud command to deploy a function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle install
$ gcloud functions deploy discord_webhook \
    --project=$MY_PROJECT --region=us-central1 \
    --trigger-http --entry-point=discord_webhook \
    --runtime=ruby27 --allow-unauthenticated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Substitute your own project ID (or use &lt;code&gt;gcloud config set project&lt;/code&gt; to set it globally.) The command above deploys to the &lt;code&gt;us-central1&lt;/code&gt; availability region, and specifies a function that responds to HTTP requests using a Ruby 2.7 runtime. Note that it also disables Google’s default authentication. Instead, we will implement Discord’s authentication mechansim below.&lt;/p&gt;

&lt;p&gt;If successful, the output of the gcloud deployment command will display the URL for the function. At this point you can use &lt;code&gt;curl&lt;/code&gt; to send http requests to the function and see the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl https://us-central1-$MY_PROJECT.cloudfunctions.net/discord_webhook
Hello, world!
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even though it’s very simple to get started, Google Cloud Functions has a long and growing list of features to make it easy to write and test your functions. You can run your function locally with a single command, and there’s a useful set of tools for running functions in isolation so you can write unit tests in Minitest or Rspec. I won’t cover the details here, but a lot of imformation is available in the &lt;a href="https://googlecloudplatform.github.io/functions-framework-ruby/latest" rel="noopener noreferrer"&gt;Functions Framework documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Responding to pings
&lt;/h3&gt;

&lt;p&gt;Now that we have a working function, it’s time to configure it as the webhook endpoint for our Discord application. This is set in the “General Information” tab on your application’s page in the Discord console. However, if you just attempt to set the field now, Discord gives an error:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms0sg88bqnm390xrc1xn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms0sg88bqnm390xrc1xn.png" alt="Endpoint verification failure message" width="738" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is because &lt;em&gt;verification&lt;/em&gt; failed. When you set up a webhook for an application, Discord will verify it is running correctly by sending it a &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#receiving-an-interaction" rel="noopener noreferrer"&gt;ping message&lt;/a&gt; and expecting the proper reply. So we first need to update our function to handle pings.&lt;/p&gt;

&lt;p&gt;Since we’re about to implement some real logic, let’s break it out into a separate class. The Functions Framework lets you define a function as a block, and you can put all the logic there. But for maintainability sake, it’s often a good idea to write separate Ruby classes encapsulating your application logic. So we’ll start by creating a Responder class to respond to HTTP requests sent by Discord, and refactoring our function to call it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

class Responder
  def respond(rack_request)
    "Hello, world!\n"
  end
end

# app.rb

require "functions_framework"
require_relative "responder"

FunctionsFramework.on_startup do
  set_global(:responder, Responder.new)
end

FunctionsFramework.http "discord_webhook" do |request|
  global(:responder).respond(request)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The above code uses a &lt;em&gt;startup block&lt;/em&gt; to instantiate our Responder and set it in a “global” that can be accessed by our function. The startup block and global storage are features of the &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-ruby" rel="noopener noreferrer"&gt;Ruby Functions Framework&lt;/a&gt;. You could also use a Ruby global variable, or even a local variable scoped to the file, but the globals mechanism provided by the Functions Framework makes it easier to isolate runs when you write unit tests.&lt;/p&gt;

&lt;p&gt;At this point, you can redeploy the function and verify that it still works. It should still just respond with the “Hello, world!” message. But we’ll change that now.&lt;/p&gt;

&lt;p&gt;Discord’s messages, known in the Discord API as &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#interaction" rel="noopener noreferrer"&gt;“interactions”&lt;/a&gt;, are sent as JSON and have a “type” field indicating the interaction type. Ping interactions have a type of 1, and when Discord sends you a ping, it expects you to respond with a similar JSON object, also with the “type” field set to 1. Let’s implement this in our Responder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

require "json"

class Responder
  def respond(rack_request)
    raw_body = rack_request.body.read
    interaction = JSON.parse(raw_body)
    if interaction["type"] == 1
      {type: 1}
    else
      [400,
        {"Content-Type" =&amp;gt; "text/plain"},
        ["Unrecognized interaction type"]]
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the return types: we return a hash if we receive a ping, or a standard &lt;a href="https://github.com/rack/rack/blob/master/SPEC.rdoc" rel="noopener noreferrer"&gt;Rack response array&lt;/a&gt; to report a 400 Bad Request if we receive anything else. The Functions Framework recognizes a variety of return types: a string will be encoded as plain text, a hash will be encoded as JSON, and Rack response types are also recognized.&lt;/p&gt;

&lt;p&gt;Redeploy to Cloud Functions, and you can test it there by posting a JSON request using curl and seeing the expected response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl https://us-central1-$MY_PROJECT.cloudfunctions.net/discord_webhook \
  --data '{"type":1}'
{"type":1}
$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can go back to the Discord developer site, and fill in the &lt;em&gt;interactions endpoint url&lt;/em&gt; field with the URL of your function. And…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms0sg88bqnm390xrc1xn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fms0sg88bqnm390xrc1xn.png" alt="Endpoint verification failure message" width="738" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’re still getting a verification failure. It turns out, even though we’re returning the correct response to a ping, Discord &lt;em&gt;also&lt;/em&gt; requires that we &lt;em&gt;verify request signatures&lt;/em&gt; correctly before it will let us set the endpoint. So we’ll turn our attention there next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validating Discord requests
&lt;/h3&gt;

&lt;p&gt;When you write a web service, it’s always good practice to validate that any requests you receive are actually from whom you think they’re from. Before it lets you set your endpoint URL, Discord will enforce this practice by checking that you’ve implemented validation correctly. It does this by sending send test requests to your endpoint with both correct and incorrect credentials, and making sure you respond appropriately&lt;/p&gt;

&lt;p&gt;So let’s implement this verification, following the &lt;a href="https://discord.com/developers/docs/interactions/slash-commands#security-and-authorization" rel="noopener noreferrer"&gt;instructions from Discord&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, we’ll need a library that can validate ED25519 signatures. There are several to choose from, but we’ll use the &lt;a href="https://github.com/RubyCrypto/ed25519" rel="noopener noreferrer"&gt;ed25519&lt;/a&gt; gem because it doesn’t depend on outside C libraries, making it easier to deploy it to serverless runtimes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Gemfile

source "https://rubygems.org"

gem "ed25519", "~&amp;gt; 1.2"
gem "functions_framework", "~&amp;gt; 0.9"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;bundle install&lt;/code&gt; to install the gem and ensure that your &lt;code&gt;Gemfile.lock&lt;/code&gt; is updated. If you forget to do this when you update your bundle, Cloud Functions will fail to deploy your app, and will report an error that your lockfile is out of date.&lt;/p&gt;

&lt;p&gt;Then it’s time to write the signature verification code. First, create a verification key from the app’s &lt;em&gt;public key&lt;/em&gt; (which is available from the General Information tab on Discord.) Set this in the constructor for the Responder class because it’s the same for all requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

require "json"
require "ed25519"

class Responder
  # Substitute your Discord app's public key here
  DISCORD_PUBLIC_KEY = "1904a4821ccb7f5212ad0ce8cfd32a385dee845d9f7dc5113b35066e3b05db78"

  def initialize
    public_key = DISCORD_PUBLIC_KEY
    public_key_binary = [public_key].pack("H*")
    @verification_key = Ed25519::VerifyKey.new(public_key_binary)
  end

  # ...
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example code, substitute your app’s public key for mine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We’ve hard-coded the public key for now. This is not great practice, but it’s generally safe because a public key is not secret. In a real application you’ll likely want to load it from an environment variable or configuration file instead.&lt;/p&gt;

&lt;p&gt;Once you have a verification key, you can verify a request by checking the contents of the request against the signature sent by Discord, using your key. The signature will match only if it was created using the corresponding private key, which only Discord should have. Additionally, the request content will include a timestamp, and you should check that it is close to the current time, in order to prevent replay attacks. Here’s the final code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# responder.rb

require "json"
require "ed25519"

class Responder
  # Substitute your Discord app's public key here
  DISCORD_PUBLIC_KEY = "1904a4821ccb7f5212ad0ce8cfd32a385dee845d9f7dc5113b35066e3b05db78"

  # Allowed difference in seconds betwen the current time and
  # the timestamp sent by Discord
  ALLOWED_CLOCK_SKEW = 10

  def initialize
    public_key = DISCORD_PUBLIC_KEY
    public_key_binary = [public_key].pack("H*")
    @verification_key = Ed25519::VerifyKey.new(public_key_binary)
  end

  def respond(rack_request)
    raw_body = rack_request.body.read
    unless verify_request(raw_body, rack_request.env)
      # Discord expects a 401 response if the verification failed
      return [401,
        {"Content-Type" =&amp;gt; "text/plain"},
        ["invalid request signature"]]
    end
    interaction = JSON.parse(raw_body)
    if interaction["type"] == 1
      {type: 1}
    else
      [400,
        {"Content-Type" =&amp;gt; "text/plain"},
        ["Unrecognized interaction type"]]
    end
  end

  private

  # Verify a request by checking the timestamp and signature
  def verify_request(raw_body, rack_env)
    # Get the timestamp and check for replay attacks
    timestamp = rack_env["HTTP_X_SIGNATURE_TIMESTAMP"].to_s
    current_time = Process.clock_gettime(Process::CLOCK_REALTIME)
    clock_skew = (current_time - timestamp.to_i).abs
    return false if clock_skew &amp;gt; ALLOWED_CLOCK_SKEW

    # Get the signature and verify it against the content and timestamp
    signature_hex = rack_env["HTTP_X_SIGNATURE_ED25519"].to_s
    signature = [signature_hex].pack("H*")
    begin
      @verification_key.verify(signature, timestamp + raw_body)
      true
    rescue Ed25519::VerifyError
      false
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick redeploy, and now at last we can set our Discord application’s endpoint URL. If you go look at the Cloud Functions logs in the Google Cloud Console, you’ll be able to see the test requests that Discord sends you. Typically it will send two requests when you attempt to set the webhook URL: one with a correct signature and one with an incorrect signature, just to make sure you have pings and verification implemented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It can take a few seconds, even after Cloud Functions finishes deploying your function, for the backend to “switch over” to the new deployment. So if you’re following along, and you believe you’ve implemented the verification, but Discord is still reporting a verification error, wait about a minute and then try setting the endpoint field in Discord again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now what?
&lt;/h2&gt;

&lt;p&gt;So far so good. We have a working Discord application, deployed to Google Cloud Functions, and responding correctly to requests sent by Discord. Next we actually have to create a command. We’ll cover that in &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l"&gt;part 2&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>discord</category>
    </item>
    <item>
      <title>Building a Discord Command in Ruby on Google Cloud Functions</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Fri, 30 Apr 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-g25</link>
      <guid>https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-g25</guid>
      <description>&lt;p&gt;I recently spent a weekend experimenting with &lt;a href="https://discord.com/developers" rel="noopener noreferrer"&gt;Discord integration&lt;/a&gt;. My church had decided to move our online services from Zoom to Discord, and, being a software geek, I thought it would be fun to try to build some things for the community.&lt;/p&gt;

&lt;p&gt;So far I’ve built a simple command that does Scripture lookups. You can type a command in one of our Discord channels:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyetm3qsitmteazemlyj4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyetm3qsitmteazemlyj4.png" alt="Invoking the command: /bible John 1:1-5" width="486" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…and the bot will look up the passage using an API, and display it in the channel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F572s6l46rh5djc1a6y1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F572s6l46rh5djc1a6y1y.png" alt="The command output, displaying a passage" width="582" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;“Slash” commands&lt;/a&gt; such as this, are a relatively recent adition to the Discord API. They’re easy for users to interact with, and convenient to deploy as a webhook.&lt;/p&gt;

&lt;p&gt;Following will be a short series of articles describing how to write a Discord command such as this “scripture-bot” in Ruby, along with how to deploy it to the cloud. Along the way, we’ll cover a lot of the issues you may encounter with a real-world application, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploying to a “serverless” environment.&lt;/strong&gt; Specifically, we’ll use &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Cloud Functions&lt;/a&gt;, a functions-as-a-service offering from Google.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifying request signatures&lt;/strong&gt; using Ruby libraries, in this case using the &lt;a href="https://github.com/RubyCrypto/ed25519" rel="noopener noreferrer"&gt;ed25519&lt;/a&gt; gem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good practices for handling secrets&lt;/strong&gt; , such as API keys, in production. For this project, we’ll demonstrate the use of Google &lt;a href="https://cloud.google.com/secret-manager" rel="noopener noreferrer"&gt;Secret Manager&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Techniques for reducing cold start time&lt;/strong&gt; , including lazy initialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using an event-driven architecture&lt;/strong&gt; to schedule and run background tasks. In this project we’ll use &lt;a href="https://cloud.google.com/pubsub" rel="noopener noreferrer"&gt;Google Pub/Sub&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;p&gt;Because there’s a lot to cover, I’ll be splitting this article into four parts:&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20"&gt;part 1&lt;/a&gt;, we’ll cover setting up a Discord bot, deploying a webhook to Google Cloud Functions, and validating webhook requests from Discord using the Ruby ed25519 library. At the end of this part, we’ll have a working webhook that Discord recognizes, but that doesn’t yet actually implement a command.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-2-570l"&gt;part 2&lt;/a&gt;, we’ll add our bot to a Discord server, and use the Discord API to register a command with the server. At the end of this part, we’ll know how to authenticate with the Discord API, and we’ll have a command set up, but it won’t yet be implemented.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-3-2e2l"&gt;part 3&lt;/a&gt;, we’ll implement the command, calling an external API to get the actual Scripture text to display on the Discord channel. We’ll also learn how to use Google’s Secret Manager to access the API key securely in production. At the end of this part, our command will be working, with a few caveats.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-4-3ch1"&gt;part 4&lt;/a&gt;, we’ll deal with one of the main issues with our implementation so far, which is that Discord doesn’t let you display more than 2000 characters in a chat message. To display a longer Scripture passage, we’ll need to send follow-up messages using the Discord API, and we’ll do that by scheduling a follow-up task using Google Pub/Sub.&lt;/p&gt;

&lt;p&gt;All the code will be included, so at the end, if you follow along, you’ll have your own fully-functional Discord bot.&lt;/p&gt;

&lt;p&gt;Ready? On to &lt;a href="https://dev.to/googlecloud/building-a-discord-command-in-ruby-on-google-cloud-functions-part-1-a20"&gt;part 1&lt;/a&gt;…&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>serverless</category>
      <category>googlecloud</category>
      <category>discord</category>
    </item>
    <item>
      <title>Should I use instance_eval or class_eval?</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Tue, 02 Feb 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/dazuma/should-i-use-instanceeval-or-classeval-106b</link>
      <guid>https://dev.to/dazuma/should-i-use-instanceeval-or-classeval-106b</guid>
      <description>&lt;p&gt;Ruby objects have methods called &lt;code&gt;instance_eval&lt;/code&gt; and &lt;code&gt;class_eval&lt;/code&gt;. They both execute a block with &lt;code&gt;self&lt;/code&gt; referencing the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class C; end

C.instance_eval { puts "instance_eval: self = #{self}" }
# prints "instance_eval: self = C"

C.class_eval { puts "class_eval: self = #{self}" }
# prints "class_eval: self = C"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a long time I thought these methods were basically synonymous, that for some reason I was simply supposed to use &lt;code&gt;class_eval&lt;/code&gt; for classes (and &lt;code&gt;module_eval&lt;/code&gt; for modules) and &lt;code&gt;instance_eval&lt;/code&gt; for everything else. Just accept it and don’t ask questions, I told myself.&lt;/p&gt;

&lt;p&gt;But there &lt;em&gt;is&lt;/em&gt; a difference, one that points to an important but seldom understood aspect of Ruby. In this article, we’ll explore the distinction and what it means for our Ruby code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The “current object”
&lt;/h2&gt;

&lt;p&gt;Let’s start with what many Ruby programmers already know.&lt;/p&gt;

&lt;p&gt;Like many object-oriented languages, Ruby has the notion of a “current object”. This is the object that receives method calls by default, and it is the object that owns any instance variables you reference. Ruby sets the current object inside every method call, so the method can access the object’s instance variables and easily call other methods of the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  def initialize(name)
    @name = name
  end

  def greeting
    # Reference "@name" from the current object
    "Hello, #{@name}!"
  end

  def print_greeting
    # Call the "greeting" method in the current object
    puts greeting
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get a reference to the current object using the &lt;code&gt;self&lt;/code&gt; keyword, but for the most part, it’s used implicitly when resolving methods or instance variables.&lt;/p&gt;

&lt;p&gt;There is always a current object, even when you’re not in a method. Within a class definition, the current object is the class. And Ruby even provides a “main” object that is the current object at the top level of a Ruby script.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  puts self
  # Prints "Greeter"
end

puts self
# Prints "main"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Changing the current object with instance_eval
&lt;/h2&gt;

&lt;p&gt;You can also &lt;em&gt;change&lt;/em&gt; the current object using the &lt;code&gt;instance_eval&lt;/code&gt; method (or the closely related &lt;code&gt;instance_exec&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;greet = Greeter.new("world")

# Change the object context within the given block.
greet.instance_eval do
  # Self now references the greeter object
  assert self == greet

  # You can call its methods without a receiver
  print_greeting

  # You can even access its instance variables
  puts @name
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;instance_eval&lt;/code&gt; method is commonly used for building domain-specific-languages (DSLs) because it lets us control how methods are looked up. When you write &lt;a href="https://guides.rubyonrails.org/routing.html" rel="noopener noreferrer"&gt;Rails routes&lt;/a&gt;, for example, Rails is using &lt;code&gt;instance_eval&lt;/code&gt; to give you a simple syntax for declaring paths and resources.&lt;/p&gt;

&lt;p&gt;The current object has a strong effect on looking up method names, but what about &lt;em&gt;defining&lt;/em&gt; a method? This is where the story gets a bit more complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining methods
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;def&lt;/code&gt; keyword is typically used to define a new method. Normally, &lt;code&gt;def&lt;/code&gt; appears within a class or module definition, so it’s clear where the method is defined. But Ruby is very flexible. You can put a &lt;code&gt;def&lt;/code&gt; almost anywhere: outside any class, in a block, even within another method.&lt;/p&gt;

&lt;p&gt;What do you think happens when a method is defined inside another method?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  def greeting
    def dismissal
      "Bye, world!"
    end
    "Hello, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No, Ruby doesn’t have any weird notion of “nested” methods. All that’s happening here is that defining the &lt;code&gt;dismissal&lt;/code&gt; method is part of the &lt;em&gt;functionality&lt;/em&gt; of the &lt;code&gt;greeting&lt;/code&gt; method. &lt;code&gt;dismissal&lt;/code&gt; is defined when you &lt;em&gt;call&lt;/em&gt; &lt;code&gt;greeting&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So what class is &lt;code&gt;dismissal&lt;/code&gt; defined on? One might guess that it’s also defined on the &lt;code&gt;Greeter&lt;/code&gt; class, and in this case you’d be right. But &lt;em&gt;why&lt;/em&gt; is that the case? It may seem “obvious” or “intuitive,” but it’s very important to understand what’s actually going on, because things won’t always be obvious. Take this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  def greeting
    "Hello, world!"
  end
end

Greeter.instance_eval do
  def dismissal
    "Bye, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re using &lt;code&gt;instance_eval&lt;/code&gt; to set the object context to the &lt;code&gt;Greeter&lt;/code&gt; class when we define the method. So where is &lt;code&gt;dismissal&lt;/code&gt; defined? You might guess, on the &lt;code&gt;Greeter&lt;/code&gt; class. But you’d be wrong. It gets defined on the &lt;code&gt;Object&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;So what’s really going on? What actually governs on which class a method is defined?&lt;/p&gt;

&lt;h2&gt;
  
  
  The “current class”
&lt;/h2&gt;

&lt;p&gt;The answer is that “self” isn’t the only piece of context that Ruby maintains. The “current object” governs lookup of method names (and instance variables), but method &lt;em&gt;definitions&lt;/em&gt; are governed by a separate piece of context that I’ll call the “current class”.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: other terms have been used elsewhere for the “current class”. For example, Yugui used the term “default definee” when she wrote about the Ruby contexts in an &lt;a href="https://blog.yugui.jp/entry/846" rel="noopener noreferrer"&gt;earlier article&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you define a class using the &lt;code&gt;class&lt;/code&gt; keyword, it creates a class and sets the current class context so that methods are attached to it. Similarly, when a method is called, the current class context is set to &lt;code&gt;self&lt;/code&gt;’s class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  # Current class is set to Greeter.
  # This ensures the greeting method is defined on Greeter.
  def greeting
    # Current class is set to self's class, which is Greeter.
    # This ensures the dismissal method is also defind on Greeter.
    def dismissal
      "Bye, world!"
    end
    "Hello, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, importantly, &lt;code&gt;instance_eval&lt;/code&gt; sets the current object but &lt;em&gt;not&lt;/em&gt; the current class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Current class is Object at the top level of a Ruby file

Greeter.instance_eval do
  # The instance_eval method sets self but not the current class,
  # so the current class is still Object here.
  def dismissal
    "Bye, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this is where &lt;code&gt;class_eval&lt;/code&gt; is different. Whereas &lt;code&gt;instance_eval&lt;/code&gt; sets &lt;em&gt;only&lt;/em&gt; the current object, &lt;code&gt;class_eval&lt;/code&gt; sets &lt;em&gt;both&lt;/em&gt; the current object and current class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Greeter.class_eval do
  # The current class is now Greeter.
  def dismissal
    "Bye, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we’ve seen that Ruby maintains &lt;em&gt;separate, independent&lt;/em&gt; class and object contexts. And that brings up an interesting question: Why?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are “current class” and “current object” distinct?
&lt;/h2&gt;

&lt;p&gt;Why maintain two separate pieces of state? Isn’t that needlessly complicated?&lt;/p&gt;

&lt;p&gt;It turns out there’s a good reason for it, and it has to do with Ruby’s goal of making programming intuitive. Let’s look again at the two places we’ve seen method definitions.&lt;/p&gt;

&lt;p&gt;When you use a &lt;code&gt;class&lt;/code&gt; declaration, Ruby sets both the current object and the current class to the same thing, the class. This lets you both define methods and call class methods such as &lt;code&gt;attr_reader&lt;/code&gt;, &lt;code&gt;include&lt;/code&gt;, and even &lt;code&gt;private&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  # current class == self
  # This means you can define methods on the class
  def greeting
    "Hello, world!"
  end

  # And you can also call class methods
  attr_reader :language
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But when you define a method in another method, the current object and the current class are &lt;em&gt;not&lt;/em&gt; the same. An arbitrary object doesn’t have methods; only classes do. So the current class is set to the class of the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  def greeting
    # current class != self
    # current class == self.class
    def dismissal
      "Bye, world!"
    end
    "Hello, world!"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to support reasonable behavior in both of these two cases, Ruby needs the flexibility to be able to set up the two values, current object and current class, differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does this matter?
&lt;/h2&gt;

&lt;p&gt;So Ruby has more context than just &lt;code&gt;self&lt;/code&gt;. Why does it matter?&lt;/p&gt;

&lt;p&gt;Well, it might help you avoid some bugs, and it’s always useful to understand the details of the language you are using. But it’s particularly important when you are designing interfaces for other developers to use, especially if you’re designing a domain-specific language.&lt;/p&gt;

&lt;p&gt;Ruby is a flexible language, and Ruby programmers expect to be able to use that flexibility, calling code, writing helper methods, and generally doing things you might not expect. If the Rails router had set the current class to some strange value and made it difficult for users to write helper methods, Rails would have been much more brittle and difficult to use, and ultimately less successful.&lt;/p&gt;

&lt;p&gt;So it’s important for Ruby library writers to make sure you choose the correct method when using &lt;code&gt;class_eval&lt;/code&gt; or &lt;code&gt;instance_eval&lt;/code&gt;. And in general, library designers need to pay attention to the current class in order to avoid unexpected behavior.&lt;/p&gt;

&lt;p&gt;If you’re interested in learning some tips for designing interfaces that pay attention to these issues, see my talk &lt;a href="https://www.youtube.com/watch?v=Ov-tMtOkKS4" rel="noopener noreferrer"&gt;“Ruby Ate My DSL!”&lt;/a&gt; from RubyConf 2019.&lt;/p&gt;

&lt;h2&gt;
  
  
  Investigating further
&lt;/h2&gt;

&lt;p&gt;It turns out, the rabbit hole goes even deeper. A third independent piece of Ruby context governs constants, where they are defined and how they are looked up. This piece of context, known internally as the &lt;code&gt;cref&lt;/code&gt;, represents the lexical nesting of classes and modules, basically what you get from calling &lt;code&gt;Module.nesting&lt;/code&gt;. However, unlike the current object and current class, the cref can’t be changed programmatically, at least not without dropping into C.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Greeter
  # The cref points to Greeter, so GREETING is defined there
  GREETING = "hello"
end

puts Greeter::GREETING
# prints "hello"

Greeter.class_eval do
  # class_eval doesn't affect cref, so SALUTATION is defined on Object
  SALUTATION = "hi"
end

puts Object::SALUTATION
# prints "hi"

puts Greeter::SALUTATION
# Raises NameError
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because cref can’t be modified from Ruby, it’s difficult to control how constants are defined and managed in DSLs. This is generally why many DSLs eschew constants or provide alternatives.&lt;/p&gt;

&lt;p&gt;And indeed, there are several other elements to the Ruby “context”, controlling such functionality as what &lt;code&gt;super&lt;/code&gt; calls, how iteration keywords like &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;break&lt;/code&gt; behave, and so forth. If you’re interested in exploring this further, the old Ruby hacking guide has &lt;a href="https://ruby-hacking-guide.github.io/module.html" rel="noopener noreferrer"&gt;a chapter&lt;/a&gt; dedicated to the context, but it’s from the Ruby 1.7 era and might be out of date. Pat Shaughnessy’s excellent book &lt;a href="http://patshaughnessy.net/ruby-under-a-microscope" rel="noopener noreferrer"&gt;&lt;em&gt;Ruby Under a Microsocope&lt;/em&gt;&lt;/a&gt; is somewhat newer and also covers some of these topics. (If anyone knows of other resources, leave a note in the comments!)&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Designing a Ruby Serverless Runtime</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Wed, 20 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/googlecloud/designing-a-ruby-serverless-runtime-575</link>
      <guid>https://dev.to/googlecloud/designing-a-ruby-serverless-runtime-575</guid>
      <description>&lt;p&gt;Last week, Google &lt;a href="https://cloud.google.com/blog/products/application-development/ruby-comes-to-cloud-functions" rel="noopener noreferrer"&gt;announced&lt;/a&gt; the public beta of the Ruby runtime for &lt;a href="https://cloud.google.com/functions" rel="noopener noreferrer"&gt;Cloud Functions&lt;/a&gt;, Google’s functions-as-a-service (FaaS) hosting platform. Ruby support has lagged a bit behind other languages over the past year or so, but now that we’ve caught up, I thought I’d share some of the design process behind the product.&lt;/p&gt;

&lt;p&gt;This article is not a traditional design document. I won’t go through the design itself step-by-step. Instead, I want to discuss some of the design issues we faced, the decisions we made, and why we made them, because it was an interesting exercise in figuring out how to fuse Ruby conventions with those of the public cloud. Some of the trade-offs we made are, I think, emblematic of the challenges the Ruby community as a whole is facing as the industry evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Ruby way of doing serverless
&lt;/h2&gt;

&lt;p&gt;Bringing Ruby support to a serverless product is a lot more involved than you might expect. At the most basic level, a language runtime is just a Ruby installation, and sure, it’s not hard to configure a Ruby image and install it on a VM. But things become more complex when you bring “serverless” into the mix. Severless is much more than just automatic maintenance and scaling. It’s an entirely different way of thinking about compute resources, one that goes contrary to much of we’ve been taught about deploying Ruby apps for the past fifteen years. When the Ruby team at Google Cloud took on the task of designing the Ruby runtime for Cloud Functions, we were also taking on the daunting task of proposing a &lt;em&gt;Ruby way of doing serverless&lt;/em&gt;. While remaining true to the Ruby idioms, practices, and tools familiar to our community, we also had to rethink how we approach web application development at almost every level, from code, to dependencies, persistence, testing, everything.&lt;/p&gt;

&lt;p&gt;This article will examine our approach to five different aspects of the design: function syntax, concurrency and lifecycle, testing, dependencies, and standards. In each case, we’ll see a balance between the importance of remaining true to our Ruby roots, and the desire to embrace the new serverless paradigms. We tried very hard to maintain continuity with the traditional Ruby way of doing things, and we also took cues from other Google Cloud Functions language runtimes, as well as precedents set by serverless products from other cloud providers. However, in a few cases, we chose to blaze a different trail. We did so when we felt that current approaches either abused a language feature, or were misleading and encouraged the wrong ideas about serverless app development.&lt;/p&gt;

&lt;p&gt;It’s possible, even likely, that some of these decisions will eventually prove to have been wrong. That’s why I’m offering this article now, to discuss what we’ve done and to start the conversation about how we as the Ruby community practice serverless app development. The good news is that Ruby is a very flexible language, and we will have plenty of opportunity to adapt as we learn and as our needs evolve.&lt;/p&gt;

&lt;p&gt;So let’s take a look at some of the initial design decisions and trade-offs we made and why we made them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Functional Ruby
&lt;/h2&gt;

&lt;p&gt;“Functions-as-a-Service” (FaaS) is currently one of the more popular serverless paradigms. Google’s Cloud Functions is just one implementation. Many other major cloud providers have their own FaaS product, and there are &lt;a href="https://www.openfaas.com/" rel="noopener noreferrer"&gt;open source implementations&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;The idea, of course, is to use a programming model centered not around web servers, but around &lt;em&gt;functions&lt;/em&gt;: stateless pieces of code that take input arguments and return results. It seems like a simple, almost obvious, change in terminology, but it actually has profound implications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv18q9cvzyqtzdwfgmjm7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv18q9cvzyqtzdwfgmjm7.png" alt="Diagram of a cloud function" width="512" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first challenge for Ruby is that, unlike many other programming languages, Ruby actually &lt;em&gt;doesn’t have&lt;/em&gt; first-class functions. Ruby is first and foremost an object-oriented language. When we write code and wrap it in a &lt;code&gt;def&lt;/code&gt;, we are writing a &lt;em&gt;method&lt;/em&gt;, code that runs in response to a &lt;em&gt;message&lt;/em&gt; sent to an &lt;em&gt;object&lt;/em&gt;. This is an important distinction, because the objects and classes that form the context of a method call are not part of the serverless abstraction. So their presence can complicate a serverless application, and even mislead us when we’re writing it.&lt;/p&gt;

&lt;p&gt;For example, some FaaS frameworks let you write a function with a &lt;code&gt;def&lt;/code&gt; at the top level of a Ruby file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def handler(event:, context:)
  "Hello, world!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this code appears straightforward, it’s important to remember what it actually does. It adds this “function” as a private method on the &lt;code&gt;Object&lt;/code&gt; class, the base class of the Ruby class hierarchy. In other words, the “function” has been added to &lt;em&gt;nearly every object in the Ruby virtual machine&lt;/em&gt;. (Unless, of course, the application changes the main object and class context when loading the file, a technique that carries other risks.) At best, this breaks encapsulation and single responsibility. At worst, it risks interfering with the functionality of your application, its dependencies, or even the Ruby standard library. This is why such “top level” methods, while common in simple single-file Ruby scripts and Rakefiles, are not recommended in larger Ruby applications.&lt;/p&gt;

&lt;p&gt;The Google Ruby team decided this issue was serious enough that we chose a different syntax, writing functions as blocks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "functions_framework"
FunctionsFramework.http("handler") do |request|
  "Hello, world!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides a Ruby-like way to define functions without modifying the &lt;code&gt;Object&lt;/code&gt; base class. It also has a few side benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The name (“handler” in this case) is just a string argument. It doesn’t need to be a legal Ruby method name, nor is there any concern of it colliding with a Ruby keyword.&lt;/li&gt;
&lt;li&gt;Blocks exhibit more traditional lexical scoping than do methods, so this will behave more similarly to functions in other languages.&lt;/li&gt;
&lt;li&gt;The block syntax makes it easier to manage function definitions. For example, it’s possible to “undefine” functions cleanly, which is important for testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, there are trade-offs. Among them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The syntax is slightly more verbose.&lt;/li&gt;
&lt;li&gt;It requires a library to provide the interface for defining functions as blocks. (Here, Ruby follows other language runtimes for Cloud Functions by utilizing a &lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-ruby" rel="noopener noreferrer"&gt;Functions Framework&lt;/a&gt; library.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We decided it was worth these trade-offs for the goal of properly distinguishing functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  To share or not to share
&lt;/h2&gt;

&lt;p&gt;Concurrency is hard. This is one of the key observations underlying the design of serverless in general, and functions-as-a-service in particular: that we live in a concurrent world and we need ways to cope. The functional paradigm addresses concurrency by insisting that functions not share state (except through an external persistence system such as a queue or database).&lt;/p&gt;

&lt;p&gt;This is in fact another reason we chose to use block syntax rather than method syntax. Methods imply objects, which carry state in the form of instance variables, state that might not work as expected in a stateless FaaS environment. Eschewing methods is a subtle but effective syntactic way to discourage practices we know to be problematic.&lt;/p&gt;

&lt;p&gt;That said, what if you need to share &lt;em&gt;resources&lt;/em&gt;, such as database connection pools? When would you initialize such resources, and how would you access them?&lt;/p&gt;

&lt;p&gt;For this purpose, the Ruby runtime supports &lt;em&gt;startup functions&lt;/em&gt; that can initialize resources and passes them into function calls. Importantly, while the startup function can create resources, normal functions can only read them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "functions_framework"

# Use an on_startup block to initialize a shared client and store it in
# the global shared data.
FunctionsFramework.on_startup do
  require "google/cloud/storage"
  set_global :storage_client, Google::Cloud::Storage.new
end

# The shared storage_client can be accessed by all function invocations
# via the global shared data.
FunctionsFramework.http "storage_example" do |request|
  bucket = global(:storage_client).bucket "my-bucket"
  file = bucket.file "path/to/my-file.txt"
  file.download.to_s
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we chose to define special methods &lt;code&gt;global&lt;/code&gt; and &lt;code&gt;set_global&lt;/code&gt; to interact with global resources. (By the way, these are not methods on Object, but methods on a specific class we use as the function context.) Again, we could have used more traditional idioms such as Ruby global variables, or even a constructor and instance variables, to pass information from startup code to function calls. However, those idioms would have communicated the wrong things. We’re not writing ordinary Ruby classes and methods where sharing data is normal, but &lt;em&gt;serverless functions&lt;/em&gt; where sharing data is hazardous if even possible, and we felt it was important for the &lt;em&gt;syntax&lt;/em&gt; to emphasize the distinction. The special methods were a deliberate design decision, to discourage practices could be dangerous in the presence of concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test first
&lt;/h2&gt;

&lt;p&gt;A strong testing culture is central to the Ruby community. Popular frameworks, such as Rails, acknowledge this and encourage active testing by providing testing tools and scaffolding as part of the framework, and the Ruby runtime for Google Cloud Functions follows suit by providing testing tools for serverless functions.&lt;/p&gt;

&lt;p&gt;The FaaS paradigm actually fits very well with tests. Functions are by nature easily testable; simply pass in arguments and assert against results. In particular, you don’t need to spin up a web server to run tests, because web servers are not part of the abstraction. The Ruby runtime provides a module of helper methods for creating HTTP request and Cloud Event objects to use as inputs, and otherwise most tests are very straightforward to write.&lt;/p&gt;

&lt;p&gt;One of the main testing challenges we encountered, though, had to do with testing &lt;em&gt;initialization code&lt;/em&gt;. Indeed, this is an issue that some of Google’s Ruby team members have had with other frameworks, including Rails: it is difficult to test an app’s initialization process, because framework initialization typically happens outside the tests, before they run. We therefore designed a way for tests to isolate the entire lifecycle of a function, including initialization. This allows us to run initialization within a test, and even repeat it multiple times allowing tests of different aspects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "minitest/autorun"
require "functions_framework/testing"

class MyTest &amp;lt; Minitest::Test
  # Include testing helper methods
  include FunctionsFramework::Testing

  def test_startup_tasks
    # Run the lifecycle, and test the startup tasks in isolation.
    load_temporary "app.rb" do
      globals = run_startup_tasks "storage_example"
      assert_kind_of Google::Cloud::Storage, globals[:storage_client]
    end
  end

  def test_storage_request
    # Rerun the entire lifecycle, including the startup tasks, and
    # test a function call.
    load_temporary "app.rb" do
      request = make_get_request "https://example.com/foo"
      response = call_http "storage_example", request
      assert_equal 200, response.status
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;load_temporary&lt;/code&gt; method loads function definitions in a sandbox, isolating them and their initialization from other test runs. That and other helper methods are defined in the &lt;code&gt;FunctionsFramework::Testing&lt;/code&gt; module, which can be included in minitest or rspec tests.&lt;/p&gt;

&lt;p&gt;So far we’ve really provided only basic testing tools for the Ruby runtime, and I expect we’ll add significantly to the toolset as our users develop more apps and we identify more of the common testing patterns. But I strongly believe testing tools are an important part of any library, especially one that purports to be a framework or runtime, and so it was a core part of our design from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dependable runtime
&lt;/h2&gt;

&lt;p&gt;Most nontrivial Ruby apps require third-party gems. For Ruby apps that use Google Cloud Functions, we require at least one gem, the &lt;code&gt;functions_framework&lt;/code&gt; that provides the Ruby interfaces for writing functions. You may also need other gems for handling data, authenticating and integrating with other services, and so forth. Dependency management is a crucial part of any runtime framework.&lt;/p&gt;

&lt;p&gt;We made several design decisions around dependency management. And the first and most important was to embrace &lt;a href="https://bundler.io" rel="noopener noreferrer"&gt;Bundler&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I know that sounds a bit frivolous. Most Ruby apps these days use Bundler anyway, and there are very few alternatives, hardly any in widespread use. But we actually took it a step further and built Bundler deep into our infrastructure, &lt;em&gt;requiring&lt;/em&gt; that apps use it in order to work with Cloud Functions. We did this because, knowing exactly how an app will manage its dependencies would allow us to implement some important optimizations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjewn6ilme39bzceqwm8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdjewn6ilme39bzceqwm8.png" alt="Deployed cloud function" width="648" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vital to a good FaaS system is the speed of deployments and cold starts. In a serverless world, your code might be updated, deployed, and torn down many times in rapid succession, so it’s crucial to eliminate bottlenecks such as resolving and installing dependencies. Because we standardize on one system for dependency management, we are able to &lt;em&gt;cache&lt;/em&gt; dependencies aggressively. We judged that the performance gains of implementing such caching, as well as the reduced load on the Rubygems.org infrastructure, far outweighed the reduced flexibility of not being able to use an alternative to Bundler.&lt;/p&gt;

&lt;p&gt;Another feature, or maybe quirk, of the Ruby runtime for Google Cloud Functions, is that it will fail deployments if the gem lockfile is missing or inconsistent. We require that &lt;code&gt;Gemfile.lock&lt;/code&gt; is present when you deploy. This was another decision made to enforce a best practice. If the lockfile gets reresolved during deployment, your builds may not be repeatable, and you may not be running against the same dependencies you tested with. We avoid this by requiring an up-to-date &lt;code&gt;Gemfile.lock&lt;/code&gt; file, and again, we are able to enforce this because we require the use of Bundler.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standards old and new
&lt;/h2&gt;

&lt;p&gt;Finally, good designs lean on standards and prior art. We had to innovate a bit to define robust functions in Ruby, but when it comes to representing the function arguments, there were already existing libraries or emerging standards to follow.&lt;/p&gt;

&lt;p&gt;For example, in the near term, many functions will respond to &lt;em&gt;web hooks&lt;/em&gt;, and will need information about the incoming HTTP request. It would not be difficult to design a class that represents an HTTP request, but the Ruby community already has a standard API for this sort of thing: &lt;a href="https://github.com/rack/rack" rel="noopener noreferrer"&gt;Rack&lt;/a&gt;. We adopted the Rack request class for our event parameters, and we support standard Rack responses for return values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "functions_framework"

FunctionsFramework.http "http_example" do |request|
  # request is a Rack::Request object.
  logger.info "I received #{request.request_method} from #{request.url}!"
  # You can return a standard Rack response array, or use one of
  # several convenience formats.
  [200, {}, "ok"]
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not only does this provide a familiar API, but it also makes it easy to integrate with other Rack-based libraries. For example, it is easy to layer a &lt;a href="http://sinatrarb.com" rel="noopener noreferrer"&gt;Sinatra&lt;/a&gt; app atop Cloud Functions because they both speak Rack.&lt;/p&gt;

&lt;p&gt;In the longer term, we increasingly expect functions-as-a-service to fit as a component in &lt;em&gt;evented&lt;/em&gt; systems. Event-based architectures are rapidly growing in popularity, often surrounding event queues such as &lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;Apache Kafka&lt;/a&gt;. And a crucial element of an event architecture is a standard way to describe the events themselves, a standard understood by event senders, brokers, transport, and consumers.&lt;/p&gt;

&lt;p&gt;Google Cloud Functions has thrown its support behind &lt;a href="https://cloudevents.io" rel="noopener noreferrer"&gt;CNCF CloudEvents&lt;/a&gt;, an emerging standard for describing and delivering events. In addition to HTTP requests, Cloud Functions can also receive data in the form of a CloudEvent, and the runtime will even convert some legacy event types to CloudEvents when calling your function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require "functions_framework"

FunctionsFramework.cloud_event "my_handler" do |event|
  # event is a CloudEvent object defined by the cloud_events gem
  logger.info "I received a CloudEvent of type #{event.type}!"
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To support CloudEvents in Ruby, the Google Ruby team worked closely with the CNCF Serverless Working Group, and even volunteered to take over development of the &lt;a href="https://github.com/cloudevents/sdk-ruby" rel="noopener noreferrer"&gt;Ruby SDK&lt;/a&gt; for CloudEvents. This turned out to be a lot of work, but we considered it crucial to be able to use the official, standard Ruby interfaces, even if we had to implement it ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The serverless future
&lt;/h2&gt;

&lt;p&gt;“Serverless” and “functions-as-a-service” hosting has garnered a lot of interest over the past few years. I think the jury is still out on how useful it will be for most workloads, but the possibilities are intriguing. “Zero”-devops, automatic maintenance and scaling, no servers to maintain, and pay only for the compute resources you actually use. I recently &lt;a href="https://dev.to/dazuma/deploying-my-blog-to-google-cloud-run-kkl"&gt;moved this very blog&lt;/a&gt; from a personal &lt;a href="https://kubernetes.io" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; cluster to Google’s managed &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt; service, and slashed my monthly bill from dozens of dollars down to pennies.&lt;/p&gt;

&lt;p&gt;That said, serverless is a fundamentally different way of thinking about compute resources, and as an industry we are still very early in our understanding of the implications. As my team designed the Ruby runtime for Google Cloud Functions, we were mindful about the ways the serverless paradigm interacts with our normal Ruby practices. In some cases, as with testing, it encourages us to double down on the good parts of Ruby culture. In others, as with how to express and notate a function in a language that strictly speaking doesn’t have them, it challenges our ideas of how to present code and communicate its intent.&lt;/p&gt;

&lt;p&gt;But in all cases, the experience of designing the runtime reminded me that we’re in a industry of constant change. Serverless is just the latest in a string of disruptions that have included the public cloud in general, and even Rails, and Ruby itself. It’s not yet clear how much serverless will stick, but it is here today, and it’s up to us to respond with curiosity, creativity, and a willingness not to take what we know for granted.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>googlecloud</category>
      <category>serverless</category>
      <category>faas</category>
    </item>
    <item>
      <title>Is it time to replace Rake?</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Wed, 06 Nov 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/dazuma/is-it-time-to-replace-rake-1jh5</link>
      <guid>https://dev.to/dazuma/is-it-time-to-replace-rake-1jh5</guid>
      <description>&lt;p&gt;Do you have really heavy Rakefiles?&lt;/p&gt;

&lt;p&gt;I do. My day job includes working in a Ruby-based &lt;a href="https://github.com/googleapis/google-cloud-ruby" rel="noopener noreferrer"&gt;repository&lt;/a&gt; with a lot of test, build, and release tooling, living in an &lt;em&gt;enormous&lt;/em&gt; Rakefile. Actually, an enormous main Rakefile, plus lots of additional Rakefiles in dozens of subdirectories. Combined, it’s well over seven thousand lines of Rakefile—and that’s actually quite a bit smaller than it has been in the past.&lt;/p&gt;

&lt;p&gt;Rakefiles are the ugly junk closets of our Ruby projects. Our app might have nicely factored classes, short methods, and excellent test coverage for our application code, but that seldom extends to our Rakefiles. Is your Rakefile covered by your code health tools? Do you have tests for your Rake tasks? Didn’t think so.&lt;/p&gt;

&lt;p&gt;And running Rake tasks is another adventure. Simple cases such as &lt;code&gt;rake test&lt;/code&gt; work great. But what if you need to pass in flags or arguments to a task? Yes, you can do it with Rake. There are several ways in fact. But it’s not pretty.&lt;/p&gt;

&lt;p&gt;Rake is an incredibly useful tool that has served the community really well for many years. But I’ve always felt like it was not quite right, that its design didn’t quite match up. And that’s not surprising, because… it doesn’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rake and Make
&lt;/h2&gt;

&lt;p&gt;Rake was written as a replacement for the unix &lt;em&gt;make&lt;/em&gt; tool. As a &lt;em&gt;make&lt;/em&gt; tool, it excels, covering all the essential aspects of file-based dependencies and builds. And the Rakefile format is, to a Rubyist, &lt;em&gt;much&lt;/em&gt; nicer to work with than Makefile.&lt;/p&gt;

&lt;p&gt;But it is still a &lt;em&gt;make&lt;/em&gt; replacement, designed for building projects in languages like C with file-based compilation dependencies. Where you have to recompile object files because source and header files changed.&lt;/p&gt;

&lt;p&gt;We generally don’t do that sort of thing in Ruby.&lt;/p&gt;

&lt;p&gt;Indeed I think the only time Ruby devs compile C regularly is when we install a gem with C extensions. And for that, most gems use &lt;a href="https://ruby-doc.org/stdlib/libdoc/mkmf/rdoc/MakeMakefile.html" rel="noopener noreferrer"&gt;mkmf&lt;/a&gt; to create, yes, a Makefile. That’s right, we generally don’t even use Rake to build our C extensions. Maybe we should. But I digress.&lt;/p&gt;

&lt;p&gt;When was the last time you wrote a Rakefile with a FileTask? Maybe you have, but most of us seldom if ever use Rake’s primary features. &lt;em&gt;We don’t use Rake for what it’s designed for.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead, we mostly use Rake to write &lt;em&gt;scripts&lt;/em&gt;. Scripts to run our tests, build assets, initiate deployments, and so forth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing project scripts
&lt;/h2&gt;

&lt;p&gt;Now when it comes to writing and using scripts, there are a few capabilities I find important.&lt;/p&gt;

&lt;p&gt;I might need to pass &lt;strong&gt;arguments or flags&lt;/strong&gt; to the script. For example, a test script might use arguments to configure the test environment or select which tests to run. Rake does have a syntax for arguments, but I’d like to use normal unix arguments and flags. It would be much nicer to say&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;run-test --integration --env=production

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

&lt;/div&gt;



&lt;p&gt;rather than&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rake 'run-test[integration,production]'

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Online help&lt;/strong&gt; for my scripts is also very important. I’m not going to remember the usage for all my scripts, and I don’t want to have to open the source every time. Rake provides a way to set a short description for each task, but it’s not well suited for long or complex content, or especially for documenting arguments.&lt;/p&gt;

&lt;p&gt;I want my implementation to be &lt;strong&gt;organized, testable, and maintainable&lt;/strong&gt; , just like application code. Large scripts should be broken into smaller methods. There should be principled ways to share code and data, and to break big projects into multiple files. Yes, all this is technically possible with Rake. It’s just Ruby after all. But, let’s face it: the Rakefile format doeesn’t exactly encourage good software practice.&lt;/p&gt;

&lt;p&gt;Support for typical &lt;strong&gt;script-y features&lt;/strong&gt; would be nice. Terminal features such as input, progress meters, and styled text. Shell completion. Rich file system manipulation and process control features. Not a lot of this is provided by Rake, and while you can bring in some additional gems, it’s not common practice.&lt;/p&gt;

&lt;p&gt;But there’s one thing I like about Rake that I want to keep: the &lt;strong&gt;Rakefile&lt;/strong&gt;. Or something like it. The ability to define scripts in a file in the current directory, with a simple DSL. That’s why I’ve been using Rake all this time, despite its limitations. I suspect that’s why we all still use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives to Rake
&lt;/h2&gt;

&lt;p&gt;So is there an alternative?&lt;/p&gt;

&lt;p&gt;Actually, there are a few. &lt;a href="https://github.com/erikhuda/thor" rel="noopener noreferrer"&gt;Thor&lt;/a&gt; is probably best known as a framework for building command line applications in your own gems. But it also can read “Thorfiles” in the current directory, and run them as tasks using the &lt;code&gt;thor&lt;/code&gt; executable. Your tasks then have access to all the features of Thor, including normal unix-style command line options and flags, and automatic help.&lt;/p&gt;

&lt;p&gt;I’ve also been experimenting with a similar tool myself, called &lt;a href="https://github.com/dazuma/toys" rel="noopener noreferrer"&gt;Toys&lt;/a&gt;. Like Thor, Toys reads its scripts from files in the current directory, and it supports unix-style command line options and flags, and automatically generates help screens. But it goes further, providing features that I’ve found helpful to manage the complex projects I work on. It provides better code organization, integrates with tab completion and the &lt;code&gt;did_you_mean&lt;/code&gt; gem, includes templates for generating common tasks, provides helpers for building terminal apps and controlling subprocesses, has built-in logging, and a whole lot more. Toys can also read an existing Rakefile, making it easy to migrate.&lt;/p&gt;

&lt;p&gt;I’ve been using Toys to manage workflow scripts at my day job for several years already. And recently I’ve started replacing the Rakefiles for my open-source Ruby projects. Both usability and maintainability have improved significantly.&lt;/p&gt;

&lt;p&gt;The Rails team also decided to move on from Rake for Rails apps. Several versions ago, the Rake tasks in new Rails apps were deprecated in favor of &lt;code&gt;bin/rails&lt;/code&gt;, a custom command line tool.&lt;/p&gt;

&lt;p&gt;So we already have some good alternatives. I think it’s just a matter of the Ruby community at large trying them out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it time to replace Rake?
&lt;/h2&gt;

&lt;p&gt;Rake has served the community well for many years, and for some uses it’s still very appropriate. Do you need to orchestrate building files in your project—whether that’s C source, assets, codegen, or other tasks with file-based dependencies? Then Rake will continue to be your friend.&lt;/p&gt;

&lt;p&gt;But for many, maybe most, of your project tasks—running tests, deploying code, performing admin tasks, etc.—it may make more sense to use a tool that is optimized for &lt;em&gt;writing scripts&lt;/em&gt;. For these cases, maybe it is indeed time to replace Rake.&lt;/p&gt;

&lt;p&gt;If you agree, consider checking out &lt;a href="https://github.com/dazuma/toys" rel="noopener noreferrer"&gt;Toys&lt;/a&gt;. I’d love to hear whether it works for you, and how it could be better. And if you think Rake should continue to be the tool of choice, I’d also love to hear your thoughts.&lt;/p&gt;

</description>
      <category>ruby</category>
    </item>
    <item>
      <title>Deploying My Blog to Google Cloud Run</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Mon, 01 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/dazuma/deploying-my-blog-to-google-cloud-run-kkl</link>
      <guid>https://dev.to/dazuma/deploying-my-blog-to-google-cloud-run-kkl</guid>
      <description>&lt;p&gt;For the past few years, my blog (the canonical copy at &lt;a href="https://daniel-azuma.com" rel="noopener noreferrer"&gt;https://daniel-azuma.com/blog&lt;/a&gt;) has been hosted on a personal &lt;a href="https://kubernetes.io" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt; cluster. In addition to my blog, I used the cluster to host a few other websites, as well as run occasional compute jobs. Kubernetes is a great, very flexible technology, and since (full disclosure) I work at Google, throwing container images at a server infrastructure feels very natural to me. But maintaining a Kubernetes cluster means paying for the VMs, and lately I’ve been wondering if I can avoid those costs.&lt;/p&gt;

&lt;p&gt;So I started to move my sites and my jobs off Kubernetes, and finally, a month ago, I was able to turn down the cluster completely. I migrated this blog to &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;, a container-based serverless environment that Google introduced in April. It’s been working perfectly so far, and since it fits easily into the free tier, my costs have gone down to effectively zero. And I know that if I ever get slashdotted… this is Google: they’ll scale me up as needed.&lt;/p&gt;

&lt;p&gt;In this article, I’ll discuss whether a serverless platform might be a good fit for a static site. Then I’ll provide a tutorial for deploying a &lt;a href="https://jekyllrb.com" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;-based static site to Cloud Run. Along the way you’ll also learn some good practices for containerizing static sites. It shouldn’t be too difficult to adapt these instructions for other static site generators such as &lt;a href="https://gohugo.io/" rel="noopener noreferrer"&gt;Hugo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why serverless for static sites?
&lt;/h2&gt;

&lt;p&gt;These days we have a number of choices for hosting static sites. One of the easiest is to use a cloud storage service such as &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteHosting.html" rel="noopener noreferrer"&gt;Amazon S3&lt;/a&gt; or &lt;a href="https://cloud.google.com/storage/docs/hosting-static-website" rel="noopener noreferrer"&gt;Google Cloud Storage&lt;/a&gt;. Just upload your static content to a storage bucket, configure a few knobs, and you have a website. Source control services such as &lt;a href="https://pages.github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://confluence.atlassian.com/bitbucket/publishing-a-website-on-bitbucket-cloud-221449776.html" rel="noopener noreferrer"&gt;Bitbucket&lt;/a&gt; also often provide web hosting for content pushed to their repositories. For simple static sites, options such as these are generally straightforward and inexpensive.&lt;/p&gt;

&lt;p&gt;With the advent of the serverless cloud, we now have a third inexpensive option. With many serverless platforms, you don’t pay for containers or VMs or storage—just the compute resources that you actually use. And for a static site, you don’t use very much.&lt;/p&gt;

&lt;p&gt;But still, why choose a general-purpose serverless platform over storage-based or source control hosting?&lt;/p&gt;

&lt;p&gt;Different deployment options always have pros and cons. One of the chief benefits of general-purpose serverless, however, is &lt;em&gt;flexibility&lt;/em&gt;. Static sites are almost never “purely” static. Maybe you need custom error handling and error documents. Maybe you need more sophisticated redirects. Maybe you need to hide some content behind authentication. Maybe the majority of your site is static, but you still need server-side scripts for a few cases. Simple content-based hosting generally provides some configuration knobs to help with these extras, but they can’t always handle every case.&lt;/p&gt;

&lt;p&gt;In my blog, I have a large number of redirects, and a few special cases handled by specially crafted Nginx configuration. So for me, one of the big draws of Google Cloud Run was the ability to provide my own container with my own Nginx config files.&lt;/p&gt;

&lt;p&gt;There’s of course no one size that fits all. But if you’re having trouble getting S3 to handle your site the way you need, or if you’re currently running on VMs or on your own servers and want to cut costs, a serverless platform might be your sweet spot as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Jekyll on Cloud Run
&lt;/h2&gt;

&lt;p&gt;The rest of this article is a tutorial for deploying a &lt;a href="https://jekyllrb.com" rel="noopener noreferrer"&gt;Jekyll&lt;/a&gt;-based site to &lt;a href="https://cloud.google.com/run" rel="noopener noreferrer"&gt;Google Cloud Run&lt;/a&gt;. Cloud Run is particularly effective for static sites because it uses containers, letting you configure your own web server for static content. (In contrast, “function” or “lambda” based platforms are typically tailored for specific dynamic web languages or frameworks, and might not support static sites at all.)&lt;/p&gt;

&lt;p&gt;Note that there are two “flavors” of Cloud Run: a fully-managed flavor that runs directly in Google’s infrastructure, and a flavor that runs on your own &lt;a href="https://cloud.google.com/kubernetes-engine/" rel="noopener noreferrer"&gt;Kubernetes Engine&lt;/a&gt; cluster. For static sites, you will probably prefer the former, because it’s the one that gives you the very cheap pay-per-use model. (And in my case, part of the whole purpose was to get rid of my Kubernetes cluster.) However, it’s still easy to adapt these instructions to deploy to Cloud Run on GKE if you want more control over the infrastructure. &lt;a href="https://www.youtube.com/watch?v=RVdhyprptTQ" rel="noopener noreferrer"&gt;This video&lt;/a&gt; has additional info on the differences between the two Cloud Run flavors.&lt;/p&gt;

&lt;p&gt;Jekyll is written in Ruby. I’ll assume you already have Ruby and Jekyll installed, but if you need help, the &lt;a href="https://jekyllrb.com/docs/installation/" rel="noopener noreferrer"&gt;Jekyll documentation&lt;/a&gt; has detailed instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating and testing a Jekyll project
&lt;/h3&gt;

&lt;p&gt;We’ll start by creating a new Jekyll project. (If you have an existing project, feel free to use it instead.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ jekyll new mysite
$ cd mysite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to run your site locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ bundle exec jekyll serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jekyll will build your site locally and serve it on port 4000. Point your web browser to localhost:4000 to see.&lt;/p&gt;

&lt;p&gt;You can edit your site by editing posts in the &lt;code&gt;_posts&lt;/code&gt; directory, or by making changes to the configuration and layouts. As you make changes, &lt;code&gt;jekyll serve&lt;/code&gt; will notice the file edits and rebuild your site automatically. We won’t go through all the details of how to use Jekyll here, but there’s a good &lt;a href="https://jekyllrb.com/docs/step-by-step/01-setup/" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; in the Jekyll documentation.&lt;/p&gt;

&lt;p&gt;Type &lt;code&gt;CTRL&lt;/code&gt;-&lt;code&gt;C&lt;/code&gt; to stop the server.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;jekyll serve&lt;/code&gt; is a convenient way to view your site during development, but it’s not how you should run it in production. In fact, as we shall see, you don’t need Jekyll, or even Ruby, installed at all in your final production container. So let’s explore how to create an efficient production image of your site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a production Docker image
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; has become the &lt;em&gt;de facto&lt;/em&gt; standard way to package applications for deployment, and Cloud Run conveniently uses Docker images as its input format. Here we’ll create a Docker image to deploy your site.&lt;/p&gt;

&lt;p&gt;Our desired image will serve our static site directly from &lt;a href="https://www.nginx.com/" rel="noopener noreferrer"&gt;Nginx&lt;/a&gt;, a high-performance web server. It’s a static site, so we don’t need Jekyll or even Ruby at runtime. However, we do need Jekyll to &lt;em&gt;build&lt;/em&gt; the static site. That is, &lt;em&gt;building&lt;/em&gt; and &lt;em&gt;running&lt;/em&gt; have different requirements, so we’re going to separate them into different phases, as diagrammed below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jv4rckp2q19295x2y89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4jv4rckp2q19295x2y89.png" alt="Diagram of the build process" width="692" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will write the items in the blue boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Site content and Jekyll configs as inputs to Jekyll&lt;/li&gt;
&lt;li&gt;Nginx config files and startup scripts that launch Nginx with the correct configuration.&lt;/li&gt;
&lt;li&gt;A Dockerfile that describes the build process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then when we perform a &lt;code&gt;docker build&lt;/code&gt;, it will proceed in two phases. First, the build phase runs, taking our blog posts and Jekyll configs, and running Jekyll to produce HTML output. Second, the built HTML, along with the Nginx configs and startup scripts, are installed into the runtime image.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuring Nginx
&lt;/h4&gt;

&lt;p&gt;Inside our &lt;code&gt;mysite&lt;/code&gt; directory, create a subdirectory called &lt;code&gt;_app&lt;/code&gt;. This directory will contain our Nginx configuration files and startup script. Because it begins with an underscore, Jekyll won’t try to build it as part of your site html. It will just pass directly through into your final runtime image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir _app
$ cd _app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s write the Nginx configuration. Create a file called &lt;code&gt;_app/nginx.conf.in&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;worker_processes 1;
working_directory /app;
daemon off;

events {
  worker_connections 80;
}

http {
  include /etc/nginx/mime.types;

  server {
    listen $PORT;
    root /app/site;

    location / {
      try_files $uri $uri.html $uri/ /404.html;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn’t an Nginx tutorial, so I won’t go over everything here in detail. However, I’ll point out a couple of things.&lt;/p&gt;

&lt;p&gt;First, we intentionally set &lt;code&gt;worker_connections&lt;/code&gt; to 80. Cloud Run currently has a maximum concurrency of 80 (meaning it will allow up to 80 simultaneous connections to each instance). Nginx can often handle more, but because Cloud Run currently has this limit, we’ll pass that info on to Nginx so it can optimize itself.&lt;/p&gt;

&lt;p&gt;Second, notice that we’re listening to port &lt;code&gt;$PORT&lt;/code&gt;. This is actually not “valid” Nginx config syntax by itself. Instead, we’re going to write a startup script that treats this Nginx config as a &lt;em&gt;template&lt;/em&gt;, and substitutes the actual port number here at runtime. This is because Cloud Run’s &lt;a href="https://cloud.google.com/run/docs/reference/container-contract" rel="noopener noreferrer"&gt;runtime specification&lt;/a&gt; states that the port isn’t actually known until runtime: it will tell you what port to listen to via the &lt;code&gt;PORT&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;So our next task is to write a script that reads the environment variable and substitutes the correct value into the config template. Create a file called &lt;code&gt;_app/start.sh&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

[[-z "$PORT"]] &amp;amp;&amp;amp; export PORT=8080
envsubst '$PORT' &amp;lt; /app/nginx.conf.in &amp;gt; /app/nginx.conf

exec nginx -c /app/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s pick apart what this is doing. First, it checks that the &lt;code&gt;PORT&lt;/code&gt; variable is set, and if not, it sets it to a default value of &lt;code&gt;8080&lt;/code&gt;. Cloud Run always sets the variable, but we’re also going to test this image locally outside Cloud Run, so we’ll make sure it has a value in that case.&lt;/p&gt;

&lt;p&gt;Next, we substitute the value of the &lt;code&gt;PORT&lt;/code&gt; environment variable in the &lt;code&gt;nginx.conf.in&lt;/code&gt; file, and write the result to the final &lt;code&gt;nginx.conf&lt;/code&gt; that we’ll use. Finally, we start Nginx.&lt;/p&gt;

&lt;p&gt;It’s important to tell &lt;code&gt;envsubst&lt;/code&gt; to substitute only the &lt;code&gt;PORT&lt;/code&gt; environment variable (because our Nginx configuration file also includes syntax like &lt;code&gt;$uri&lt;/code&gt; that we want &lt;code&gt;envsubst&lt;/code&gt; to leave alone.)&lt;/p&gt;

&lt;p&gt;It’s also important to prefix our Nginx command with &lt;code&gt;exec&lt;/code&gt;. This causes Nginx to &lt;em&gt;replace&lt;/em&gt; the script process so that it receives signals. This is important for Cloud Run to be able to control the container effectively.&lt;/p&gt;

&lt;p&gt;Save the two files, and set the execute bit on your start script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ chmod a+x start.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now have the two files &lt;code&gt;nginx.conf.in&lt;/code&gt; and &lt;code&gt;start.sh&lt;/code&gt; inside the &lt;code&gt;_app&lt;/code&gt; directory in your &lt;code&gt;mysite&lt;/code&gt; project.&lt;/p&gt;

&lt;h4&gt;
  
  
  Writing the Dockerfile
&lt;/h4&gt;

&lt;p&gt;Next we’re going to write a Dockerfile to build a Docker image of your site.&lt;/p&gt;

&lt;p&gt;Go back to your &lt;code&gt;mysite&lt;/code&gt; project directory, and create another subdirectory called &lt;code&gt;_build&lt;/code&gt;. Again, because this name begins with an underscore, Jekyll won’t try to treat any of its files as site content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd /path/to/mysite
$ mkdir _build
$ cd _build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file called &lt;code&gt;_build/Dockerfile&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM ruby:2 AS build

RUN gem install bundler

WORKDIR /workspace
COPY Gemfile* /workspace/
RUN bundle install

COPY . /workspace
ENV JEKYLL_ENV=production
RUN bundle exec jekyll build

FROM nginx:1

WORKDIR /app
COPY _app /app
COPY --from=build /workspace/_site /app/site

CMD ["/app/start.sh"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may look a bit more complex than other “getting started” Dockerfiles you may have seen, so let’s take a closer look at what it’s doing.&lt;/p&gt;

&lt;p&gt;Remember that our build process was going to proceed in two stages. You can see that now in this multi-stage Dockerfile. The first stage stage starts with the standard &lt;a href="https://hub.docker.com/_/ruby" rel="noopener noreferrer"&gt;Ruby-Debian base image&lt;/a&gt;, installs your bundle (which should include Jekyll), and performs a production Jekyll build. The results are left in the &lt;code&gt;/workspace/_site&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Then the second stage creates the final image that we will deploy to Cloud Run. It starts with the standard &lt;a href="https://hub.docker.com/_/nginx" rel="noopener noreferrer"&gt;Nginx base image&lt;/a&gt;, copies in our Nginx config file and startup script, and also copies in the built html files from the first stage. Note that the final image includes only what is needed to serve your site: Nginx, but not Ruby or Jekyll. This &lt;a href="https://docs.docker.com/develop/develop-images/multistage-build/" rel="noopener noreferrer"&gt;two-stage&lt;/a&gt; strategy is a common best practice when building Docker images.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing your image locally
&lt;/h4&gt;

&lt;p&gt;Now that we have a Dockerfile, we can build the image locally. Return to your &lt;code&gt;mysite&lt;/code&gt; directory, and perform a docker build, pointing at our Dockerfile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd /path/to/mysite
$ docker build -t mysite -f _build/Dockerfile .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build your site as a Docker image and tag it with the name &lt;code&gt;mysite&lt;/code&gt;. We can now try running it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker run --rm -it -p 8080:8080 mysite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run your image with your static site, and expose port 8080. Now you can test it by pointing your web browser to localhost:8080.&lt;/p&gt;

&lt;p&gt;Hit &lt;code&gt;CTRL&lt;/code&gt;-&lt;code&gt;C&lt;/code&gt; to stop your Docker image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploying to Cloud Run
&lt;/h3&gt;

&lt;p&gt;Now that we have a working Docker image, it’s time to deploy to Cloud Run.&lt;/p&gt;

&lt;h4&gt;
  
  
  Setting up Google Cloud
&lt;/h4&gt;

&lt;p&gt;If you do not yet have a Google Cloud project, go to the &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;console&lt;/a&gt; and create one. You’ll need to enable billing in order to use Cloud Run. But don’t worry—unless your site has a truly massive amount of traffic, you’ll easily fit into the free tier for this tutorial.&lt;/p&gt;

&lt;p&gt;Enable Cloud Build and Cloud Run in your project, if you haven’t already:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to &lt;a href="https://console.cloud.google.com/cloud-build" rel="noopener noreferrer"&gt;Cloud Build&lt;/a&gt; in the console and click “Enable Cloud Build API”.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;a href="https://console.cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt; in the console and click “Start Using Cloud Run”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll also need the &lt;a href="https://cloud.google.com/sdk/" rel="noopener noreferrer"&gt;Google Cloud SDK&lt;/a&gt; installed. Set the default project as follows (substituting your project ID for &lt;code&gt;$MY_PROJECT_ID&lt;/code&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud config set project $MY_PROJECT_ID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Building in the cloud
&lt;/h4&gt;

&lt;p&gt;You could push a local image to the cloud in preparation to deploy to Cloud Run, but it is easier and safer to build in the cloud. We’ll create a simple configuration to build your site image in &lt;a href="https://cloud.google.com/cloud-build/" rel="noopener noreferrer"&gt;Google Cloud Build&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;_build/cloudbuild.yaml&lt;/code&gt;. Copy the following into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;steps:
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '--no-cache', '--pull',
      '--file', '_build/Dockerfile',
      '--tag', '$_IMAGE',
      '.']

images:
  - '$_IMAGE'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration performs a Docker build using the &lt;code&gt;_build/Dockerfile&lt;/code&gt; you created, and tags it with an image name that you need to specify when you invoke the build.&lt;/p&gt;

&lt;p&gt;Move back into the root directory for your Jekyll site, and build using this command (substituting your project ID for &lt;code&gt;$MY_PROJECT_ID&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd /path/to/mysite
$ gcloud builds submit --config _build/cloudbuild.yaml \
    --substitutions _IMAGE=gcr.io/$MY_PROJECT_ID/mysite:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will build your site’s Docker image, and upload it to &lt;a href="https://cloud.google.com/container-registry/" rel="noopener noreferrer"&gt;Google Container Registry&lt;/a&gt;. If you like, you can view your images by visiting your &lt;a href="https://console.cloud.google.com/gcr" rel="noopener noreferrer"&gt;container registry&lt;/a&gt; in the cloud console. You can also view your &lt;a href="https://console.cloud.google.com/cloud-build/builds" rel="noopener noreferrer"&gt;build results and logs&lt;/a&gt; in the console.&lt;/p&gt;

&lt;p&gt;The image name you provide as the value of &lt;code&gt;_IMAGE&lt;/code&gt; should be of the form &lt;code&gt;gcr.io/$MY_PROJECT_ID/$SITE_NAME:$TAG&lt;/code&gt; in order to upload to Google Container Registry. In that name, &lt;code&gt;$MY_PROJECT_ID&lt;/code&gt; must be your project ID. &lt;code&gt;$SITE_NAME&lt;/code&gt; should be some identifying name for this image. (We used &lt;code&gt;mysite&lt;/code&gt; in this case.) &lt;code&gt;$TAG&lt;/code&gt; should be a name for this build. (We used &lt;code&gt;v1&lt;/code&gt; here, but you might consider using a timestamp, git hash, or other system of generating build IDs.)&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploying to Cloud Run
&lt;/h4&gt;

&lt;p&gt;Now, to deploy to Cloud run, type this command (substituting your project ID for &lt;code&gt;$MY_PROJECT_ID&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud beta run deploy mysite \
    --platform managed --region us-central1 \
    --image gcr.io/$MY_PROJECT_ID/mysite:v1 \
    --allow-unauthenticated --concurrency 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unless you have a very large site, this should take only a few seconds. When deployment is done, the command will output your site’s URL. It will look something like &lt;code&gt;https://mysite-somecode.a.run.app&lt;/code&gt;. Open that URL in your web browser to view your site.&lt;/p&gt;

&lt;p&gt;If this is a real site, you’ll probably want to point your own domain at it. You can do this in the console. Go to the &lt;a href="https://console.cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run section&lt;/a&gt; and click “Manage custom domains”.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploying updates
&lt;/h4&gt;

&lt;p&gt;When you have updates, you should repeat the build and run steps above. I recommend using a different &lt;code&gt;$TAG&lt;/code&gt; for each update. This will let you identify each build uniquely, making it easy to roll your site forward and back as needed.&lt;/p&gt;

&lt;p&gt;For example, our initial deployment used a tag of &lt;code&gt;v1&lt;/code&gt;. Make some edits to your site (maybe add or edit a post), and test it out locally using &lt;code&gt;jekyll serve&lt;/code&gt;. Then, let’s build &lt;code&gt;v2&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud builds submit --config _build/cloudbuild.yaml \
    --substitutions _IMAGE=gcr.io/$MY_PROJECT_ID/mysite:v2
$ gcloud beta run deploy mysite \
    --platform managed --region us-central1 \
    --image gcr.io/$MY_PROJECT_ID/mysite:v2 \
    --allow-unauthenticated --concurrency 80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you work with your site, you’ll probably want to create a rake task or similar script to generate new tags and automate those commands. I use &lt;a href="https://github.com/dazuma/toys" rel="noopener noreferrer"&gt;Toys&lt;/a&gt; for this purpose, and my &lt;code&gt;.toys.rb&lt;/code&gt; file looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LOCAL_IMAGE = "mysite"
PROJECT = "my-project-id"
SERVICE = "mysite"

tool "run-local" do
  flag :no_cache
  include :exec, exit_on_nonzero_status: true
  def run
    cache_args = no_cache ? ["--pull", "--no-cache"] : []
    exec ["docker", "build"] + cache_args +
         ["-t", LOCAL_IMAGE, "-f", "_build/Dockerfile", "."]
    puts "Running on http://localhost:8080"
    exec ["docker", "run", "--rm", "-it", "-p", "8080:8080", LOCAL_IMAGE]
  end
end

tool "deploy" do
  flag :tag, default: Time.new.strftime("%Y-%m-%d-%H%M%S")
  include :exec, exit_on_nonzero_status: true
  def run
    image = "gcr.io/#{PROJECT}/#{SERVICE}:#{tag}"
    exec ["gcloud", "builds", "submit", "--project", PROJECT,
          "--config", "_build/cloudbuild.yaml",
          "--substitutions", "_IMAGE=#{image}"]
    exec ["gcloud", "beta", "run", "deploy", SERVICE,
          "--project", PROJECT, "--platform", "managed",
          "--region", "us-central1", "--allow-unauthenticated",
          "--image", image, "--concurrency", "80"]
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cleaning up
&lt;/h3&gt;

&lt;p&gt;When you’re done with this tutorial, if you do not want to continue serving your site, you can delete the project so you do not incur any expenses related to it.&lt;/p&gt;

&lt;p&gt;Note that deleting the project will delete all resources related to it, including any VMs, databases, storage, and networking resources. If you are still using some of the project’s resources and do not want to delete the entire project, you can clean up after this tutorial by doing this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://console.cloud.google.com/run" rel="noopener noreferrer"&gt;Cloud Run console&lt;/a&gt; and delete the service you deployed for your site. (It might be called “mysite”.)&lt;/li&gt;
&lt;li&gt;Go to your &lt;a href="https://console.cloud.google.com/gcr" rel="noopener noreferrer"&gt;container registry&lt;/a&gt; in the console and delete the images for your site.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to go from here
&lt;/h2&gt;

&lt;p&gt;The Cloud Run documentation includes a lot of &lt;a href="https://cloud.google.com/run/docs/how-to" rel="noopener noreferrer"&gt;additional information&lt;/a&gt; about deploying and running your site. Of particular interest are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/mapping-custom-domains" rel="noopener noreferrer"&gt;How to map a custom domain&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/run/docs/continuous-deployment" rel="noopener noreferrer"&gt;Setting up continuous deployment from Cloud Build&lt;/a&gt; and &lt;a href="https://cloud.google.com/cloud-build/docs/running-builds/automate-builds" rel="noopener noreferrer"&gt;triggering on pushes to GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/monitoring" rel="noopener noreferrer"&gt;Monitoring your site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/run/pricing" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt; and &lt;a href="https://cloud.google.com/run/quotas" rel="noopener noreferrer"&gt;quotas&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more information about the runtime environment, or if you want to customize your container further, you should refer to the &lt;a href="https://cloud.google.com/run/docs/reference/container-contract" rel="noopener noreferrer"&gt;container runtime contract&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There’s a lot of hype around serverless these days, and not all of it is justified. For me, Cloud Run is the first product I’ve actually been somewhat excited about, because of how it successfully fuses serverless with containers, and because of how well it works for common use cases such as static sites.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>jekyll</category>
      <category>googlecloud</category>
      <category>cloudrun</category>
    </item>
    <item>
      <title>Yes, you should provide a client library for your API! (RubyConf 2018)</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Tue, 13 Nov 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/dazuma/yes-you-should-provide-a-client-library-for-your-api-rubyconf-2018-285h</link>
      <guid>https://dev.to/dazuma/yes-you-should-provide-a-client-library-for-your-api-rubyconf-2018-285h</guid>
      <description>&lt;p&gt;On Nov 13, 2018, I spoke on "Yes, you should provide a client library for your API!" at RubyConf in Los Angeles, CA. The talk discussed the benefits of providing client libraries for HTTP-based APIs, and some techniques for writing them.&lt;/p&gt;

&lt;p&gt;Here’s a small list of resources that I either mentioned in the talk or are closely related. Feel free to share more in the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  Talk resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://confreaks.tv/videos/rubyconf2018-yes-you-should-provide-a-client-library-for-your-api" rel="noopener noreferrer"&gt;Video on Confreaks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speakerdeck.com/dazuma/yes-you-should-provide-a-client-library-for-your-api" rel="noopener noreferrer"&gt;Slides on SpeakerDeck&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code generation frameworks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://openapis.org" rel="noopener noreferrer"&gt;OpenAPI&lt;/a&gt; is an open standard that works well for REST APIs.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://grpc.io" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; is a high-performance RPC framework originally developed by Google, that uses HTTP/2 and protocol buffers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://thrift.apache.org/" rel="noopener noreferrer"&gt;Apache Thrift&lt;/a&gt; is an RPC framework originally developed by Facebook.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources related to OpenAPI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://yos.io/2018/02/11/schema-first-api-design/" rel="noopener noreferrer"&gt;Schema-first API Design&lt;/a&gt; is a great article for getting oriented with OpenAPI.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.blazemeter.com/blog/how-to-generate-openapi-definitions-from-code" rel="noopener noreferrer"&gt;Generating OpenAPI From Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://swagger.io/tools/open-source/open-source-integrations/" rel="noopener noreferrer"&gt;Swagger integration libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://swagger.io/tools/swagger-editor/" rel="noopener noreferrer"&gt;Swagger editor&lt;/a&gt; for editing OpenAPI spec.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Resources related to gRPC
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developers.google.com/protocol-buffers/" rel="noopener noreferrer"&gt;Protocol buffers&lt;/a&gt; is the structured data serialization mechanism used by gRPC.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cncf.io/blog/2018/07/03/http-2-smarter-at-scale/" rel="noopener noreferrer"&gt;HTTP/2: Smarter at Scale&lt;/a&gt; is a useful article describing HTTP/2 which underlies gRPC.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cncf.io/blog/2018/08/31/grpc-on-http-2-engineering-a-robust-high-performance-protocol/" rel="noopener noreferrer"&gt;gRPC on HTTP/2: Engineering a Robust High Performance Protocol&lt;/a&gt; is another article useful for understanding gRPC.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Related resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://schd.ws/hosted_files/apistrat18/b2/APIStrat-presentation-joe-levy-david-justice.pdf" rel="noopener noreferrer"&gt;Slide deck discussing how Oracle and Azure manage APIs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://graphql.org/learn/" rel="noopener noreferrer"&gt;GraphQL&lt;/a&gt; is a data schema system, similar in some respects but more limited in scope. You might find it useful in conjunction with a full API specification framework.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://opencensus.io/" rel="noopener noreferrer"&gt;OpenCensus&lt;/a&gt; is an open framework for collecting and reporting instrumentation information.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other API design resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/apis/design/" rel="noopener noreferrer"&gt;Google’s API design guide&lt;/a&gt; is public, and describes Google’s API design principles, some of which are specific to Google, but most of which should apply broadly to APIs in general.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thanks to Jeff who did a bunch of preliminary research and presented an earlier version of this talk at CodeBEAM, and to Graham for reviewing and providing suggestions. Thanks to Google for sponsoring my appearance at RubyConf this year.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>api</category>
    </item>
    <item>
      <title>Docker and OTP: Friends or Foes? (ElixirConf 2018)</title>
      <dc:creator>Daniel Azuma</dc:creator>
      <pubDate>Fri, 07 Sep 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/dazuma/docker-and-otp-friends-or-foes-elixirconf-2018-3lf0</link>
      <guid>https://dev.to/dazuma/docker-and-otp-friends-or-foes-elixirconf-2018-3lf0</guid>
      <description>&lt;p&gt;On Sept 7, 2018, I spoke on "Docker and OTP: Friends or Foes?" at ElixirConf in Bellevue, WA. The talk discussed ideas for making OTP applications work together with container-based deployments.&lt;/p&gt;

&lt;p&gt;As a community, we sometimes believe that OTP and containers/cloud are incompatible, and we too often conclude that we must choose one over the other. I argued that we should instead look for creative ways to combine the strengths of the two platforms. As an example, I demonstrated an online real-time game, written using Elixir/Phoenix, that uses process migration to survive container-based updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Talk resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=nLApFANtkHs" rel="noopener noreferrer"&gt;Video on YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://speakerdeck.com/dazuma/docker-and-otp-friends-or-foes" rel="noopener noreferrer"&gt;Slides on SpeakerDeck&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code and libraries used in the talk
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The Elixir Tank game is on Github at &lt;a href="https://github.com/ElixirSeattle/tanx" rel="noopener noreferrer"&gt;https://github.com/ElixirSeattle/tanx&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The LibCluster library used to build an Erlang cluster from the Kubernetes API is on Github at &lt;a href="https://github.com/bitwalker/libcluster" rel="noopener noreferrer"&gt;https://github.com/bitwalker/libcluster&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The Horde library that provides “distributed” supervisor and registry implementations is on Github at &lt;a href="https://github.com/derekkraan/horde" rel="noopener noreferrer"&gt;https://github.com/derekkraan/horde&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The CRDT library underlying the handoff process is on Github at &lt;a href="https://github.com/derekkraan/delta_crdt_ex" rel="noopener noreferrer"&gt;https://github.com/derekkraan/delta_crdt_ex&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other libraries of interest
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;An alternate library that could be used for process migration is Swarm, which can be found on Github at &lt;a href="https://github.com/bitwalker/swarm" rel="noopener noreferrer"&gt;https://github.com/bitwalker/swarm&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lasp is a collection of libraries for building large distributed systems in Erlang/Elixir. It includes a replacement for distributed Erlang, as well as implementations for global registries, CRDTs, and more. Information is available at &lt;a href="https://lasp-lang.org" rel="noopener noreferrer"&gt;https://lasp-lang.org&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links and articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://gigalixir.com" rel="noopener noreferrer"&gt;Gigalixir&lt;/a&gt; is a hosting service that is optimized for Elixir applications, and is able to perform OTP hot upgrades in containers.&lt;/li&gt;
&lt;li&gt;Derek Kraan wrote a couple of articles useful for getting started with Horde: &lt;a href="https://medium.com/@derek.kraan2/introducing-horde-a-distributed-supervisor-in-elixir-4be3259cc142" rel="noopener noreferrer"&gt;Introducing Horde&lt;/a&gt; and &lt;a href="https://medium.com/@derek.kraan2/getting-started-with-hordes-distributed-supervisor-registry-f3017208e1ce" rel="noopener noreferrer"&gt;Getting Started with Horde&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Ellis Pritchard discusses the Kubernetes/OTP shutdown process in some depth in his article &lt;a href="https://medium.com/@ellispritchard/graceful-shutdown-on-kubernetes-with-signals-erlang-otp-20-a22325e8ae98" rel="noopener noreferrer"&gt;Graceful shutdown on Kubernetes with signals and Erlang OTP 20&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Chirag Singh Toor wrote up his experience &lt;a href="https://engineering.dollarshaveclub.com/elixir-otp-applications-on-kubernetes-9944636b8609" rel="noopener noreferrer"&gt;writing a similar system&lt;/a&gt;, including detailed code. A very useful read!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also intend to write a few posts myself covering some of this material in a bit more detail, so watch this space in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thanks
&lt;/h2&gt;

&lt;p&gt;Thanks to Ben, Kyle, and Greg for joining me in writing the original Tanx app… has it really been three years?? Thanks to Google for sponsoring my appearance at ElixirConf this year, and to the Elixir team at Google for helpful feedback on my talk and demo.&lt;/p&gt;

</description>
      <category>elixir</category>
      <category>docker</category>
      <category>kubernetes</category>
      <category>otp</category>
    </item>
  </channel>
</rss>
