Working on adding automated configuration support

This commit is contained in:
Sam Lown 2011-04-29 21:40:36 +02:00
parent 37c021a6b9
commit f3dd4ae06e
9 changed files with 299 additions and 26 deletions

View file

@ -4,6 +4,7 @@ module CouchRest
extend ActiveModel::Naming
include CouchRest::Model::Connection
include CouchRest::Model::Configuration
include CouchRest::Model::Persistence
include CouchRest::Model::Callbacks

View file

@ -11,13 +11,33 @@ module CouchRest
add_config :model_type_key
add_config :mass_assign_any_attribute
add_config :auto_update_design_doc
add_config :database_config_path
add_config :environment
add_config :connection
add_config :connection_config_file
configure do |config|
config.model_type_key = 'model' # was 'couchrest-type'
config.mass_assign_any_attribute = false
config.auto_update_design_doc = true
config.database_config_path = File.join(defined?(Rails) ? Rails.root : Dir.pwd, 'config', 'couchdb.yml')
config.environment = defined?(Rails) ? Rails.env : :development
config.connection_config_file =
File.join(
defined?(Rails) ? Rails.root : Dir.pwd,
'config', 'couchdb.yml'
)
app_name = defined?(Rails) ? Rails.application.class.to_s.underscore.gsub(/\/.*/, '') : 'couchrest'
config.connection = {
:protocol => 'http',
:host => 'localhost',
:port => '5984',
:prefix => app_name,
:suffix => nil,
:username => nil,
:password => nil
}
end
end

View file

@ -0,0 +1,74 @@
module CouchRest
module Model
module Connection
extend ActiveSupport::Concern
def database
self.class.database
end
def server
self.class.server
end
module ClassMethods
# Overwrite the normal use_database method so that a database
# name can be provided instead of a full connection.
def use_database(db)
@_database_name = db
end
# Replace CouchRest's database reader with a more advanced
# version that will make a best guess at the database you might
# want to use. Allows for a string to be provided instead of
# a database object.
def database
@database ||= prepare_database(@_database_name)
end
def server
@server ||= CouchRest::Server.new(prepare_server_uri)
end
def prepare_database(db = nil)
unless db.is_a?(CouchRest::Database)
conf = connection_configuration
db = [conf[:prefix], db.to_s, conf[:suffix]].compact.join('_')
self.server.database!(db)
else
db
end
end
protected
def prepare_server_uri
conf = connection_configuration
userinfo = [conf[:username], conf[:password]].compact.join(':')
userinfo += '@' unless userinfo.empty?
"#{conf[:protocol]}://#{userinfo}#{conf[:host]}:#{conf[:port]}"
end
def connection_configuration
@server_configuration ||=
self.connection.update(
(load_connection_config_file[environment] || {}).symbolize_keys
)
end
def load_connection_config_file
connection_config_cache[connection_config_file] ||=
File.exists?(connection_config_file) ?
YAML::load(ERB.new(IO.read(connection_config_file)).result) :
{ }
end
def connection_config_cache
Thread.current[:connection_config_cache] ||= {}
end
end
end
end
end

View file

@ -89,10 +89,6 @@ module CouchRest
self
end
def database
self.class.database(@database)
end
protected
def perform_validations(options = {})
@ -108,20 +104,6 @@ module CouchRest
module ClassMethods
# Replace CouchRest's database reader with a more advanced
# version that will make a best guess at the database you might
# want to use. Allows for a string to be provided instead of
# a database object.
def database(db = nil)
db ||= @database
if db.nil?
# try to grab from configuration files
else
db
end
end
# Creates a new instance, bypassing attribute protection
#
# ==== Returns

View file

@ -4,8 +4,14 @@ module CouchRest
module Proxyable
extend ActiveSupport::Concern
def proxy_database
raise StandardError, "Please set the #proxy_database_method" if self.class.proxy_database_method.nil?
@proxy_database ||= self.class.prepare_database(self.send(self.class.proxy_database_method))
end
module ClassMethods
# Define a collection that will use the base model for the database connection
# details.
def proxy_for(assoc_name, options = {})
@ -13,19 +19,19 @@ module CouchRest
options[:class_name] ||= assoc_name.to_s.singularize.camelize
class_eval <<-EOS, __FILE__, __LINE__ + 1
def #{assoc_name}
unless respond_to?('#{db_method}')
raise "Missing ##{db_method} method for proxy"
end
@#{assoc_name} ||= CouchRest::Model::Proxyable::ModelProxy.new(::#{options[:class_name]}, self, self.class.to_s.underscore, #{db_method})
end
EOS
end
# Tell this model which other model to use a base for the database
# connection to use.
def proxied_by(model_name, options = {})
raise "Model can only be proxied once or ##{model_name} already defined" if method_defined?(model_name) || !proxy_owner_method.nil?
self.proxy_owner_method = model_name
attr_accessor :model_proxy
attr_accessor model_name
overwrite_database_reader(model_name)
end
# Define an a class variable accessor ready to be inherited and unique
@ -34,6 +40,24 @@ module CouchRest
def proxy_owner_method=(name); @proxy_owner_method = name; end
def proxy_owner_method; @proxy_owner_method; end
# Define the name of a method to call to determine the name of
# the database to use as a proxy.
def proxy_database_method=(name); @proxy_database_method = name; end
def proxy_database_method; @proxy_database_method; end
private
# Ensure that the model can no longer be used for normal requests
# be overwriting the database reader method so that a helpful
# error message is displayed.
def overwrite_database_reader(model_name)
class_eval <<-EOS, __FILE__, __LINE__ + 1
def database
raise StandardError, "#{self.to_s} documents must be accessed via the '#{model_name}' proxy"
end
EOS
end
end
class ModelProxy

View file

@ -46,6 +46,7 @@ require "couchrest/model/proxyable"
require "couchrest/model/collection"
require "couchrest/model/associations"
require "couchrest/model/configuration"
require "couchrest/model/connection"
require "couchrest/model/designs"
require "couchrest/model/designs/view"
@ -61,4 +62,6 @@ require "couchrest/model/casted_model"
require "couchrest/model/base"
# Add rails support *after* everything has loaded
require "couchrest/railtie"
if defined?(Rails)
require "couchrest/railtie"
end

View file

@ -56,10 +56,52 @@ describe CouchRest::Model::Base do
end
end
describe "default configuration" do
it "should provide environment" do
@class.environment.should eql(:development)
end
it "should provide connection config file" do
@class.connection_config_file.should eql(File.join(Dir.pwd, 'config', 'couchdb.yml'))
end
it "should provided simple connection details" do
@class.connection[:prefix].should eql('couchrest')
end
end
describe "default configuration with Rails" do
before do
Rails = mock('Rails') unless defined?(Rails)
Rails.stub!(:env).and_return(:dev)
Rails.stub!(:root).and_return("/rails/root")
app = mock('Application')
app.stub!(:class).and_return("SampleCouch::Application")
Rails.stub!(:application).and_return(app)
# New anon class!
@class = Class.new()
@class.class_eval do
include CouchRest::Model::Configuration
end
end
it "should provide environment" do
@class.environment.should eql(:dev)
end
it "should provide connection config file" do
@class.connection_config_file.should eql(File.join("/rails/root", 'config', 'couchdb.yml'))
end
it "should provided simple connection details" do
@class.connection[:prefix].should eql('sample_couch')
end
end
describe "General examples" do
before(:all) do
@default_model_key = 'model'
@default_model_key = 'model-type'
end

View file

@ -0,0 +1,117 @@
# encoding: utf-8
require File.expand_path('../../spec_helper', __FILE__)
describe CouchRest::Model::Base do
before do
@class = Class.new(CouchRest::Model::Base)
end
describe "instance methods" do
before :each do
@obj = @class.new
end
describe "#database" do
it "should respond to" do
@obj.should respond_to(:database)
end
it "should provided class's database" do
@obj.class.should_receive :database
@obj.database
end
end
describe "#server" do
it "should respond to method" do
@obj.should respond_to(:server)
end
it "should return class's server" do
@obj.class.should_receive :server
@obj.server
end
end
end
describe "class methods" do
describe ".use_database" do
it "should respond to" do
@class.should respond_to(:use_database)
end
end
describe ".database" do
it "should respond to" do
@class.should respond_to(:database)
end
it "should provide a database object" do
@class.database.should be_a(CouchRest::Database)
end
it "should provide a database with default name" do
end
end
describe ".server" do
it "should respond to" do
@class.should respond_to(:server)
end
it "should provide a server object" do
@class.server.should be_a(CouchRest::Server)
end
it "should provide a server with default config" do
@class.server.uri.should eql("http://localhost:5984")
end
it "should allow the configuration to be overwritten" do
@class.connection = {
:protocol => "https",
:host => "127.0.0.1",
:port => '5985',
:prefix => 'sample',
:suffix => 'test',
:username => 'foo',
:password => 'bar'
}
@class.server.uri.should eql("https://foo:bar@127.0.0.1:5985")
end
end
describe ".prepare_database" do
it "should respond to" do
@class.should respond_to(:prepare_database)
end
end
describe "protected methods" do
describe ".connection_configuration" do
it "should provide main config by default" do
@class.send(:connection_configuration).should eql(@class.connection)
end
end
describe ".load_connection_config_file" do
it "should provide an empty hash if config not found" do
@class.send(:load_connection_config_file).should eql({})
end
it "should load file if available" do
@class.connection_config_file = File.join(FIXTURE_PATH, 'config', 'couchdb.yml')
puts @class.send(:connection_config_cache).inspect
hash = @class.send(:load_connection_config_file)
hash[:development].should_not be_nil
@class.server.uri.should eql("https://test:uesr@sample.cloudant.com:443")
end
end
end
end
end

10
spec/fixtures/config/couchdb.yml vendored Normal file
View file

@ -0,0 +1,10 @@
development:
protocol: 'https'
host: sample.cloudant.com
port: 443
prefix: project
suffix: text
username: test
password: user