Class: HerokuExternalDb::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/heroku_external_db.rb

Overview

Represents an ActiveRecord database configuration to be used in a Heroku app. This configuration consists of at least one environment variable, and possibly several:

  • [PREFIX]_DATABASE_URL - the Heroku-style DATABASE_URL to connect to. Required.

  • [PREFIX]_DATABASE_CA - the filename of a SSL certificate file which will be configured as the :sslca for the database. Useful for encrypted MySQL, e.g.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env_prefix, configuration_name) ⇒ Configuration

Returns a new instance of Configuration.



43
44
45
46
# File 'lib/heroku_external_db.rb', line 43

def initialize(env_prefix, configuration_name)
  @env_prefix = env_prefix
  @configuration_name = configuration_name
end

Instance Attribute Details

#ca_pathObject

The path in which your SSL CA certificates are stored. By default, this is [Rails app root]/config/ca.



50
51
52
# File 'lib/heroku_external_db.rb', line 50

def ca_path
  @ca_path ||= ca_path_from_rails_root
end

#configuration_nameObject (readonly)

The ActiveRecord configuration that will be set up. Typically this should be the same as your RAILS_ENV environment variable, but some apps may have multiple database connections with different names.



40
41
42
# File 'lib/heroku_external_db.rb', line 40

def configuration_name
  @configuration_name
end

#env_prefixObject (readonly)

Each HerokuExternalDb must have a unique prefix for its environment variables. For example, if the prefix was set to CRAZY, then the database URL would be taken from CRAZY_DATABASE_URL, and the CA filename would be taken from CRAZY_DATABASE_CA, if it existed.

In typical usage, the prefix will be EXTERNAL by default.



35
36
37
# File 'lib/heroku_external_db.rb', line 35

def env_prefix
  @env_prefix
end

Instance Method Details

#db_configObject

Returns an ActiveRecord configuration hash based on the environment variables.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/heroku_external_db.rb', line 103

def db_config
  @db_config ||= begin 
    raise "ENV['#{env_prefix}_DATABASE_URL'] expected but not found!" unless ENV["#{env_prefix}_DATABASE_URL"]
    config = parse_db_uri(ENV["#{env_prefix}_DATABASE_URL"])

    if ENV["#{env_prefix}_DATABASE_CA"]
      config.merge!(db_configuration({
        :sslca => ENV["#{env_prefix}_DATABASE_CA"],
        :sslcert => ENV["#{env_prefix}_DATABASE_CERT"],
        :sslkey => ENV["#{env_prefix}_DATABASE_KEY"],
      }))
    end
  
    config
  end
end

#db_configuration(opts) ⇒ Object

Returns a partial ActiveRecord configuration hash for the given SSL CA certificate. Checks to make sure the given filename actually exists, and raises an error if it does not.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/heroku_external_db.rb', line 79

def db_configuration(opts)
  return {} unless opts
  raise "ca_path for #{opts.inspect} cannot be determined from Rails root; please set it explicitly" unless ca_path

  config = {}

  [
    :sslca,

    # Needed when using X.509
    :sslcert,
    :sslkey,
  ].each do |k|
    if value = opts[k]
      filepath = File.join(ca_path, value)
      raise "File #{filepath.inspect} does not exist!" unless File.exists?(filepath)
      config[k] = filepath
    end
  end

  return config
end

#parse_db_uri(db_uri) ⇒ Object

Parse a Heroku-style database URI and return an ActiveRecord configuration hash based on it. Format is as follows:

<adapter>://[<username>[:<password>]]@<host>[:<port>]/<database>


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/heroku_external_db.rb', line 58

def parse_db_uri(db_uri)
  uri = URI.parse(db_uri)

  db_config = {
    :adapter => uri.scheme,
    :database => uri.path[1..-1],
    :host => uri.host
  }

  if uri.user
    db_config[:username] = uri.user
    db_config[:password] = uri.password if uri.password
  end
  db_config[:port] = uri.port if uri.port

  db_config
end

#setup!Object

Installs an ActiveRecord configuration based on the environment variables, and makes an initial connection to the database. (This flushes out the connection pool if a different connection has already been established, and tests to make sure we can actually connect.)



124
125
126
127
128
# File 'lib/heroku_external_db.rb', line 124

def setup!
  ActiveRecord::Base.configurations[configuration_name] = db_config
  ActiveRecord::Base.establish_connection(configuration_name).connection
  self
end