Dynamically Loading Ruby Configuration Tokens with Metaprogramming

Learn how to load API tokens dynamically in Ruby using metaprogramming, plus modern security best practices for Rails apps.

This was originally posted by me on Coderwall a while ago. I’m reposting it here for archival purposes.

The Problem with Hardcoded Tokens

So, let’s say you need to use tokens in your app to access a third-party API (like Twitter, Instagram, or Facebook). In Ruby gems tutorials it’s very common to see the following example code to load these tokens:

module Facebook
  CONFIG = YAML.load_file(Rails.root.join("config/facebook.yml"))[Rails.env]
  APP_ID = CONFIG['app_id']
  SECRET = CONFIG['secret_key']
end

This was extracted from the Koala’s gem wiki on GitHub. Or the following one, which was extracted from the README.md file of the Instagram gem GitHub repo.

Instagram.configure do |config|
  config.client_id = "YOUR_CLIENT_ID"
  config.client_secret = "YOUR_CLIENT_SECRET"
end

It’s a insecure practice to hardcode things like this.

Now, the most recommended and used practice in the wild is to have these API access tokens in a YAML file, so they can be loaded into our app, like the Koala’s example code from above. The format is the one that follows:

twitter:
  consumer_key: YOUR_CONSUMER_KEY
  consumer_secret: YOUR_CONSUMER_SECRET
  oauth_token: YOUR_OAUTH_TOKEN
  oauth_token_secret: YOUR_OAUTH_TOKEN_SECRET

As you may know, this file is loaded as a Hash. So we can access each key and value.

Dynamic Loading with Metaprogramming

Now, using Ruby’s metaprogramming magic we can load each key of the file and use it in the configuration section of the gem. How? Using the .send() method.

CONFIG = YAML.load_file(Rails.root.join("config", "settings.yml"))[Rails.env]

Twitter.configure do |twitter|
  CONFIG['twitter'].each_key do |key|
    twitter.send("#{key}=", CONFIG['twitter'][key])
  end
end

The secret is to name each key in the YAML file section exactly as the name of the configuration key of the gem, so there won’t be any method_missing error.

You can find more information about the .send() method on the Ruby Documentation page.

Next up

The Sausage Principle

If you love something, never find out how it’s made. Continue →