This project is archived and is in readonly mode.

#312 ✓wontfix
Adam Keys

Database config in Ruby

Reported by Adam Keys | June 3rd, 2008 @ 06:53 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 set 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

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

    Closing till there is any further progress.

Create your profile

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

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Pages