A migration is needed for this patch.
The new table is named config_properties and contains individual
configuration elements. The goal is to replace all configuration
files, exception database.yml, with this entries in this table.
Each element has a name and a value. Values can be null or empty,
but the name must be non-null and unique.
Signed-off-by: Darryl L. Pierce <mcpierce(a)gmail.com>
---
.gitignore | 1 +
app/controllers/application.rb | 1 -
app/models/config_property.rb | 60 ++++++++++
db/migrate/024_create_config_properties.rb | 31 ++++++
db/schema.rb | 163 ----------------------------
test/fixtures/config_properties.yml | 3 +
test/unit/config_property_test.rb | 95 ++++++++++++++++
7 files changed, 190 insertions(+), 164 deletions(-)
create mode 100644 app/models/config_property.rb
create mode 100644 db/migrate/024_create_config_properties.rb
delete mode 100644 db/schema.rb
create mode 100644 test/fixtures/config_properties.yml
create mode 100644 test/unit/config_property_test.rb
diff --git a/.gitignore b/.gitignore
index 0dca223..2b1aabd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
config/database.yml
config/mailer.yml
config/schedules.yml
+db/schema.rb
diff --git a/app/controllers/application.rb b/app/controllers/application.rb
index d318bad..22c7661 100644
--- a/app/controllers/application.rb
+++ b/app/controllers/application.rb
@@ -48,7 +48,6 @@ class ApplicationController < ActionController::Base
end
def load_counts
- puts "LOADING COUNTS"
@project_count = Project.find(:all).size
@product_count = Product.find(:all).size
@user_count = User.find(:all).size
diff --git a/app/models/config_property.rb b/app/models/config_property.rb
new file mode 100644
index 0000000..85255e7
--- /dev/null
+++ b/app/models/config_property.rb
@@ -0,0 +1,60 @@
+# config_property.rb
+# Copyright (C) 2009, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+# A +ConfigProperty+ represents a single configuration element.
+#
+# The +name+ is the unique key for the element.
+#
+# The +value+ is the value for the element.
+#
+# The +type+ indicates the element type.
+#
+# Configuration values should not be retrieved directly. Instead, the developer
+# should fetch and store values by name. An instance of +ConfigProperty+ should
+# never be used directly.
+class ConfigProperty < ActiveRecord::Base
+ validates_presence_of :name,
+ :message => "A name is required."
+ validates_uniqueness_of :name,
+ :message => "The name must be unique."
+
+ # Fetches a configuration value by name.
+ # If not found, the default value is returned.
+ # If no default value is provided, then +nil+ is returned.
+ def self.fetch(name, default = nil)
+ raise Exception.new("Missing property name") unless name &&
!name.empty?
+
+ result = ConfigProperty.find_by_name(name)
+
+ return result.value if result
+ return default
+ end
+
+ # Saves a configuration value.
+ def self.store(name, value = nil)
+ raise Exception.new("Missing property name") unless name &&
!name.empty?
+
+ property = ConfigProperty.find_by_name(name)
+
+ if property
+ property.value = value
+ property.save
+ else
+ ConfigProperty.create(:name => name, :value => value)
+ end
+ end
+end
diff --git a/db/migrate/024_create_config_properties.rb
b/db/migrate/024_create_config_properties.rb
new file mode 100644
index 0000000..b0e8873
--- /dev/null
+++ b/db/migrate/024_create_config_properties.rb
@@ -0,0 +1,31 @@
+# 024_create_config_properties.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+class CreateConfigProperties < ActiveRecord::Migration
+ def self.up
+ create_table :config_properties do |t|
+ t.string :name, :null => false, :unique => true, :limit => 128
+ t.text :value, :null => true, :unique => false
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :config_properties
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
deleted file mode 100644
index dc8868f..0000000
--- a/db/schema.rb
+++ /dev/null
@@ -1,163 +0,0 @@
-# This file is auto-generated from the current state of the database. Instead of editing
this file,
-# please use the migrations feature of Active Record to incrementally modify your
database, and
-# then regenerate this schema definition.
-#
-# Note that this schema.rb definition is the authoritative source for your database
schema. If you need
-# to create the application database on another system, you should be using
db:schema:load, not running
-# all the migrations from scratch. The latter is a flawed and unsustainable approach (the
more migrations
-# you'll amass, the slower it'll run and the greater likelihood for issues).
-#
-# It's strongly recommended to check this file into your version control system.
-
-ActiveRecord::Schema.define(:version => 22) do
-
- create_table "backlog_items", :force => true do |t|
- t.integer "sprint_id",
:null => false
- t.integer "user_story_id",
:null => false
- t.integer "owner_id"
- t.decimal "estimated_hours", :precision => 5, :scale => 2, :default
=> 0.0, :null => false
- t.text "notes"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.integer "state", :default =>
0, :null => false
- end
-
- add_index "backlog_items", ["sprint_id",
"user_story_id"], :name =>
"index_backlog_items_on_sprint_id_and_user_story_id", :unique => true
-
- create_table "notifications", :force => true do |t|
- t.integer "user_id"
- t.boolean "task_reminders"
- t.boolean "daily_updates"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.boolean "updates_to_product_list", :default => false
- end
-
- create_table "product_roles", :force => true do |t|
- t.integer "user_id", :null => false
- t.integer "product_id", :null => false
- t.integer "role_id", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
- t.boolean "pending", :default => true
- t.boolean "is_approved", :default => false
- end
-
- add_index "product_roles", ["product_id", "user_id"],
:name => "index_product_roles_on_user_id_and_product_id", :unique => true
-
- create_table "products", :force => true do |t|
- t.integer "project_id"
- t.integer "owner_id"
- t.string "name", :limit => 128
- t.text "description"
- t.datetime "created_at"
- t.datetime "updated_at"
- t.string "mailing_list", :limit => 128
- end
-
- add_index "products", ["name"], :name =>
"index_products_on_name", :unique => true
-
- create_table "projects", :force => true do |t|
- t.integer "owner_id", :null => false
- t.string "name", :limit => 50, :null => false
- t.string "url"
- t.text "description"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- add_index "projects", ["name"], :name =>
"index_projects_on_name", :unique => true
-
- create_table "remaining_hours_estimates", :force => true do |t|
- t.integer "backlog_item_id", :null =>
false
- t.integer "user_id", :null =>
false
- t.decimal "hours", :precision => 5, :scale => 2, :null
=> false
- t.datetime "created_at"
- t.datetime "updated_at"
- t.datetime "estimated_on", :null =>
false
- end
-
- create_table "roles", :force => true do |t|
- t.string "name", :null =>
false
- t.boolean "can_manage_backlog_items", :default => false, :null =>
false
- t.boolean "can_close_user_story", :default => false, :null =>
false
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- add_index "roles", ["name"], :name =>
"index_roles_on_name", :unique => true
-
- create_table "sessions", :force => true do |t|
- t.string "session_id", :null => false
- t.text "data"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- add_index "sessions", ["session_id"], :name =>
"index_sessions_on_session_id"
- add_index "sessions", ["updated_at"], :name =>
"index_sessions_on_updated_at"
-
- create_table "sprints", :force => true do |t|
- t.integer "product_id", :null => false
- t.string "title", :limit => 100, :null =>
false
- t.date "start", :null => false
- t.integer "duration", :null => false
- t.string "goals", :limit => 1000, :null =>
false
- t.datetime "created_at"
- t.datetime "updated_at"
- t.integer "status", :default => 0, :null =>
false
- end
-
- create_table "tasks", :force => true do |t|
- t.integer "backlog_item_id",
:null => false
- t.integer "primary_id",
:null => false
- t.integer "backup_id"
- t.text "description",
:null => false
- t.decimal "hours", :precision => 4, :scale => 2, :default
=> 0.0, :null => false
- t.date "when_entered",
:null => false
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- create_table "user_privileges", :force => true do |t|
- t.integer "user_id", :null => false
- t.boolean "admin_projects", :default => false, :null => false
- t.boolean "admin_users", :default => false, :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- create_table "user_stories", :force => true do |t|
- t.integer "product_id"
- t.integer "priority"
- t.boolean "closed", :default => false
- t.string "title", :limit => 100
- t.text "description"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- create_table "user_verifications", :force => true do |t|
- t.integer "user_id", :null => false
- t.string "token", :limit => 16, :null => false
- t.datetime "sent", :null => false
- t.datetime "created_at"
- t.datetime "updated_at"
- t.integer "attempts", :default => 0, :null => false
- end
-
- add_index "user_verifications", ["user_id"], :name =>
"index_user_verifications_on_user_id", :unique => true
-
- create_table "users", :force => true do |t|
- t.string "email", :limit => 128, :null => false
- t.string "display_name", :limit => 128, :null => false
- t.string "hashed_password", :null => false
- t.string "salt", :null => false
- t.text "introduction"
- t.datetime "created_at"
- t.datetime "updated_at"
- end
-
- add_index "users", ["email"], :name =>
"index_users_on_email", :unique => true
-
-end
diff --git a/test/fixtures/config_properties.yml b/test/fixtures/config_properties.yml
new file mode 100644
index 0000000..05d87bf
--- /dev/null
+++ b/test/fixtures/config_properties.yml
@@ -0,0 +1,3 @@
+email_server_name:
+ name: email.server.name
+ value:
smtp.google.com
diff --git a/test/unit/config_property_test.rb b/test/unit/config_property_test.rb
new file mode 100644
index 0000000..86f61d2
--- /dev/null
+++ b/test/unit/config_property_test.rb
@@ -0,0 +1,95 @@
+# config_property_test.rb
+# Copyright (C) 2008, Darryl L. Pierce <mcpierce(a)gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class ConfigPropertyTest < ActiveSupport::TestCase
+ def setup
+ @newproperty = ConfigProperty.new(:name => 'name', :value =>
'value')
+ @oldproperty = config_properties(:email_server_name)
+ end
+
+ # Ensures that the name is required.
+ def test_name_required
+ @newproperty.name = nil
+
+ fail "A name is required." if @newproperty.valid?
+ end
+
+ # Ensures that the name is required to be unique.
+ def test_name_unique
+ @newproperty.name = @oldproperty.name
+
+ fail "The name must be unique." if @newproperty.valid?
+ end
+
+ # Ensures that a value is not required.
+ def test_value_not_required
+ @newproperty.value = nil
+
+ fails "The value is not required." unless @newproperty.valid?
+ end
+
+ # Ensures that a well-formed property is valid.
+ def test_valid
+ fail "There is a problem with validation." unless @newproperty.valid?
+ end
+
+ # Ensures that a name must be provided when fetching a property.
+ def test_fetch_without_name
+ assert_raises(Exception) { ConfigProperty.fetch(nil) }
+ end
+
+ # Ensures that retrieving an existing value works.
+ def test_fetch_for_existing
+ result = ConfigProperty.fetch((a)oldproperty.name, nil)
+
+ assert result
+ assert_equal @oldproperty.value, result, "returned the wrong value"
+ end
+
+ # Ensures that the default value's returned when no such property is found.
+ def test_fetch_with_default_value
+ result = ConfigProperty.fetch((a)oldproperty.name.reverse, "default value")
+
+ assert result
+ assert_equal "default value", result, "Did not return the default
value."
+ end
+
+ # Ensures that a name is required when saving a property.
+ def test_store_without_name
+ assert_raises(Exception) { ConfigProperty.store(nil) }
+ end
+
+ # Ensures that a new configuration property is saved property.
+ def test_store_new_property
+ assert_nil ConfigProperty.fetch((a)oldproperty.name.reverse), "Whoa! That
shouldn't exist!"
+
+ ConfigProperty.store((a)oldproperty.name.reverse, "farkle")
+ result = ConfigProperty.fetch((a)oldproperty.name.reverse)
+
+ assert_equal "farkle", result, "Property was not property
saved."
+ end
+
+ # Ensures that an existing property is overwritten.
+ def test_store
+ ConfigProperty.store((a)oldproperty.name, "farkle")
+
+ result = ConfigProperty.fetch((a)oldproperty.name)
+ assert_equal "farkle", result, "Property was not overwritten."
+ end
+end
--
1.6.0.2