#312 √ wontfix
Adam Keys

Database config in Ruby

Reported by Adam Keys | June 3rd, 2008 @ 06:22 AM

This patch allows a developer to configure their database via Ruby in addition to the existing database.yml. First, one specifies the path to the Ruby file:

config.database_configuration_file = 'config/database.rb'

Then, you set up your database in @database.rb@:

Rails::DatabaseSpecGenerator.map do |db|
  db.development :config => 
    {:adapter => 'sqlite3', 
     :database => 'db/development.sqlite3', 
     :timeout => 5000}
  db.test :config => 
    {:adapter => 'sqlite3', 
     :database => 'db/test.sqlite3', 
     :timeout => 5000}
  db.production :config => 
    {:adapter => 'sqlite3', 
     :database => 'db/production.sqlite3', 
     :timeout => 5000}
end

This patch does not yet generate config/database.rb by default, nor does it prevent you from using config/database.yml.

Comments and changes to this ticket

  • Adam Keys

    Adam Keys June 3rd, 2008 @ 05:58 PM

    Really attach the patch this time.

  • Pratik

    Pratik June 3rd, 2008 @ 11:17 PM

    • → Assigned user changed from “” to “Pratik”
  • Cheah Chu Yeow
  • Cheah Chu Yeow

    Cheah Chu Yeow June 4th, 2008 @ 04:11 PM

    I like this!

    I'm adding a patch based off Adam's one that would allow you to do something like this (I use defaults in YAML so this is needed for a Ruby version for me):

    Rails::DatabaseSpecGenerator.map do |db|
      db.defaults :config => {
        :adapter => 'mysql',
        :encoding => 'utf8'
      }
    
      db.development :config => {
        :database => 'ryokan_development'
      }.merge(db.defaults)
    end
    

    The syntax is not DSL-ish nor perfect but at least it's understandable ruby.

    Also some minor tweaks like removing the @@@returning@@@ block and using File.extname to check for file extensions.

  • Adam Keys

    Adam Keys June 4th, 2008 @ 04:14 PM

    I'm curious what advantages db.defaults has something like this:

    Rails::DatabaseSpecGenerator.map do |db|
      defaults = {:adapter => 'mysql', :encoding => 'utf8'}
    
      db.development :config => defaults.merge(:database => 'ryokan_development'}
    end
    

    That said, I'm working on a somewhat different approach to this. Will post it here once I'm done.

  • Pratik

    Pratik June 4th, 2008 @ 10:30 PM

    • → Assigned user cleared.
  • Cheah Chu Yeow

    Cheah Chu Yeow June 4th, 2008 @ 04:14 PM

    • → Assigned user cleared.

    Ack sorry Adam you got me there. I was thinking of not doing it with a local var to stick to the DSL but I guess that's not very convincing. Looking forward to your different approach :)

  • Adam Keys

    Adam Keys June 6th, 2008 @ 07:05 AM

    Just attached a reworked version. This time 'round, the database config hangs off of ActiveRecord itself. You can set it in your generic environment or a specific environment file. For example, in `config/environment.rb` :

      config.active_record.configure do |db|
        db.development do |setup|
          socket = %w{/tmp/mysql.sock /var/run/mysql.sock}.detect do |f|
            File.exists?(f)
          end
          setup.database = "test_app_#{Rails.env}"
          setup.adapter  = 'mysql'
          setup.socket = socket
          setup.credentials = "#{RAILS_ROOT}/config/credentials.rb"
        end
        
        db.production do |setup|
          socket = %w{/tmp/mysql.sock /var/run/mysql.sock}.detect do |f|
            File.exists?(f)
          end
          setup.database = "test_app_#{Rails.env}"
          setup.adapter  = 'mysql'
          setup.socket = socket
          # setup.credentials = "#{RAILS_ROOT}/config/credentials.rb"
        end
      end
    

    Or, in `config/environments/development.rb` :

    config.active_record.configure do |db|
      db.development do |setup|
        setup.database = 'test_app_development'
      end
    end
    

    Note you can set `credentials`. This loads a YAML or Ruby file with the database username/password information:

    username: root
    password: 
    
    username = 'root'
    password = ''
    

    You must use a credentials file in production, otherwise an exception is raised.

    If this version looks good, I'll start on app generator changes.

  • Jeremy Kemper

    Jeremy Kemper June 6th, 2008 @ 07:18 AM

    Hey, that's pretty nice.

    What's the advantage of a DSL here (config builder) over, say, assigning a hash? e.g.

      config.active_record.databases = {
        :development => {
          :adapter => 'mysql',
          :socket => find_socket,
          :database => "#{test_app_#{Rails.env}",
          :credentials => "#{Rails.root}/config/credentials.rb"
        }, ...
    

    The blocks let you delay execution, but I'm not sure that's needed.

  • Jeremy Kemper

    Jeremy Kemper June 6th, 2008 @ 07:19 AM

    yeah, pretend I wrote some valid Ruby too ^^^

  • Adam Keys

    Adam Keys June 6th, 2008 @ 01:01 PM

    Jeremy: well in this case, having real methods makes it cleaner, IMHO, to do things like yell at the developer if they set a username/password in production mode. But really, I like the look of the block version better than a hash-y version.

    As an aside, I would love to not delay execution, but AR isn't loaded when environments are evaluated.

  • Adam Keys

    Adam Keys June 6th, 2008 @ 02:52 PM

    I went ahead and did the generators. Here's what it looks like for a fresh app generated for MySQL:

      # Specify your database connections. You can partially configure your
      # your database here and then finish in an environment-specific file or
      # an initializer.
      config.active_record.configure do |db|
        
        # MySQL.  Versions 4.1 and 5.0 are recommended.
        #
        # Install the MySQL driver:
        #   gem install mysql
        # On Mac OS X:
        #   sudo gem install mysql -- --with-mysql-dir=/usr/local/mysql
        # On Mac OS X Leopard:
        #   sudo env ARCHFLAGS="-arch i386" gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
        #       This sets the ARCHFLAGS environment variable to your native architecture
        # On Windows:
        #   gem install mysql
        #       Choose the win32 build.
        #       Install MySQL and put its /bin directory on your path.
        #
        # And be sure to use new-style password hashing:
        #   http://dev.mysql.com/doc/refman/...
        db.development do |setup|
          setup.encoding = utf8
          setup.username = root
          setup.password = 
          setup.socket = /tmp/mysql.sock
          setup.adapter = mysql
          setup.database = foo_development
        end
        
        # Warning: The database defined as "test" will be erased and
        # re-generated from your development database when you run "rake".
        # Do not set this db to the same as development or production.
        db.test do |setup|
          setup.encoding = utf8
          setup.username = root
          setup.password = 
          setup.socket = /tmp/mysql.sock
          setup.adapter = mysql
          setup.database = foo_test
        end
        
        # You cannot specify the username and password for your database directly
        # here. You need to add those details to a credentials file that looks
        # like this:
        #
        #  username = 'root'
        #  password = ''
        #
        # Specify the path to your credentials file below. For more info see 
        # http://rubyonrails.org/awesome_c...
        db.production do |setup|
          setup.encoding = utf8
          setup.credentials = "/path/to/your/credentials.rb"
          setup.socket = /tmp/mysql.sock
          setup.adapter = mysql
          setup.database = foo_production
        end
      end
    

    Looking for feedback on the code and/or copy.

  • Adam Keys

    Adam Keys June 8th, 2008 @ 05:54 PM

    Updated patch to allow DRYer AR configuration. In environment.rb :

    config.active_record.configure do |db|
        
        db.encoding = utf8
        db.adapter = 'mysql'
        db.socket = /tmp/mysql.sock
        db.database = 'foo_development'
        
      end
    

    @development.rb@:

    config.active_record.connection.configure do |db|
      db.username = 'root'
      db.password = ''
      db.database = 'foo_development'
    end
    

    @production.rb@:

    config.active_record.connection.configure do |db|
      db.database = 'foo_production'
      db.credentials = '/path/to/your/credentials.rb'
    end
    

    environments.rb is loaded first. Environment-specific files overwrite anything set previously.

  • James Cox

    James Cox June 11th, 2008 @ 04:16 PM

    I have to say that the credentials in a file means that rather than saving another file from getting checked in, what you are doing is just drying up database.yml - which is fine if it's an itch you want scratching :)

    to be honest- it might be more interesting drying it up properly. Why not get opinionated and append _dev/_prod/_test to the name of your db- so in env, set db.name = "foo" and the name of each db is inferred, unless you override and explicitly set in each env?

  • Adam Keys

    Adam Keys June 11th, 2008 @ 04:31 PM

    @James yeah its completely possible to define the whole thing in environment.rb like so:

      config.active_record.connection.configure do |db|
        db.adapter  = 'mysql'
        db.encoding = 'utf8'
        db.database = "test_app_#{Rails.env}"
        db.socket = %w{/tmp/mysql.sock /var/run/mysql.sock}.detect do |f|
          File.exists?(f)
        end
        db.credentials = "#{RAILS_ROOT}/config/credentials.rb"
      end
    

    You can then override anything you want (adapter, credentials, etc.) in environment-specific files.

  • Trevor Turk

    Trevor Turk June 11th, 2008 @ 11:09 PM

    I think this is a great direction, but I just wanted to note that using a hash might be more consistent with how email configuration works (e.g. config.action_mailer.smtp_settings)

  • Adam Keys

    Adam Keys June 11th, 2008 @ 11:31 PM

    Trevor: indeed, I considered using a Hash. However, using a proper object makes it easier to yell at those who would explicitly set a username/password for their production database. ;)

  • Chad Woolley

    Chad Woolley June 12th, 2008 @ 02:22 AM

    -1

    See comments on mailing list thread (which contains another -1 from another poster) for explanation.

  • Steven Soroka
  • Carsten Nielsen
  • Wesley Moxam

    Wesley Moxam June 12th, 2008 @ 11:08 PM

    -1

    It might make for a nice plugin for those who desire such a feature.

  • Pratik

    Pratik August 21st, 2008 @ 11:53 AM

    • → Tag changed from “” to “activerecord enhancement patch railties”
    • → State changed from “new” to “wontfix”

    Closing till there is any further progress.

Please Login or create a free account to add a new comment.

You can update this ticket by sending an email to from your email client. (help)

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Source available from github

The Git repository resides at http://github.com/rails

Check out the current development trunk (Edge Rails) with:

git clone git://github.com/rails/rails.git

Creating or reviewing a patch

See the contributor guide.

Creating a feature request

Please don't. If you want a new feature in Rails, you'll have to pull up your sleeves and get busy yourself. Or convince someone else to do it. See the contributor guide on how to get going. But posting them here is just going to lead to ticket root.

Creating a bug report

When creating a bug report, be sure to include as much relevant information as possible. Post the code sample that causes the problem. Preferably, alter the unit tests and show through either changed or added tests how the expected behavior is not occuring.

Security vulnerabilities should be reported via an email to security@rubyonrails.org, do not use trac for reporting security vulnerabilities. All content in trac is publicly available as soon as it is posted.

Then don't get your hopes up. Unless you have a "Code Red, Mission Critical, The World is Coming to an End" kinda bug, you're creating this ticket in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the ticket automatically will see any activity or that others will jump to fix it. Creating a ticket like this is mostly to help yourself start on the path of fixing the problem and for others to sign on to with a "I'm having this problem too".

Shared Ticket Bins