multiple_connection_handler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/README +53 -0
  2. data/lib/multiple_connection_handler.rb +135 -0
  3. metadata +65 -0
data/README ADDED
@@ -0,0 +1,53 @@
1
+ multiple_connection_handler
2
+ =========
3
+
4
+ A gem for Rails that provides a class, MultipleConnectionHandler, that allows
5
+ for access to connections to any databases in the database.yml file for your
6
+ Rails application. Quick and dirty implementation to let us do dirty things.
7
+
8
+
9
+ Full disclosure
10
+ =========
11
+
12
+ Implemented as a singleton accessed solely through the class methods. Some
13
+ instance methods are exposed, but you should NOT use them directly. You should
14
+ also NOT try to instantiate multiple MultipleConnectionHandler objects.
15
+
16
+ Have NOT evaluated any thread-safety of this code. In fact, it's probably
17
+ not safe to multithread code that's will use the MultipleConnectionHandler,
18
+ as there is no locking on cached db connections.
19
+
20
+ NOTE NOTE NOTE NOTE NOTE NOTE
21
+ -----------------------------
22
+ This should only be used if you're being bad and are executing queries via
23
+ raw SQL, and not using ActiveRecord methods. If you just want to specify
24
+ different databases for your various models, see ActiveRecord::Base
25
+ documentation.
26
+
27
+ I admit up front this is pretty hacky in how we're piggy-backing on
28
+ ActiveRecord's connection pooling and ConnectionHandler. So please read
29
+ through the code (esp. the connection() instance method) before proceeding.
30
+
31
+
32
+ Example (yes, this is a terrible example)
33
+ ========
34
+
35
+ dev_migrations = MultipleConnectionHandler.connection(:development).execute(
36
+ "SELECT * FROM schema_migrations")
37
+ staging_migrations = MultipleConnectionHandler.connection(:staging).execute(
38
+ "SELECT * FROM schema_migrations")
39
+
40
+ if dev_migrations != staging_migrations
41
+ Rails.logger.error "Can't push data from staging to dev right now cause " +
42
+ "they're on different migrations"
43
+ end
44
+
45
+
46
+ Testing
47
+ =========
48
+
49
+ You may notice the lack of unit tests. Yeah, we just tested this manually.
50
+ Probably the best, as you basically need acess to multiple dbs in order to test
51
+ whether this functionality is working. I guess if you'd like, you could create
52
+ some models tied to different db instances, use this to directly modify those
53
+ models and then use the models to verify the changes. Meh.
@@ -0,0 +1,135 @@
1
+ # grants access to db connections for several environments at once.
2
+ # implemented as a singleton accessed through class methods.
3
+ class MultipleConnectionHandler
4
+
5
+ # raised when the requested connection isn't recognized from the db config
6
+ # file
7
+ #-----------------------------------------------------------------------------
8
+ class UnrecognizedDatabaseError < Exception
9
+ def initialize(unrecognized_db_name, db_config_filename)
10
+ super("Database specification for '#{unrecognized_db_name}' not found " +
11
+ " in '#{db_config_filename}'.")
12
+ end
13
+ end
14
+
15
+ # raised when attempting to re-initialize the handler after it's already
16
+ # been initialized. prevents competition between two clients of the class
17
+ # who may attempt to change the database config file on top of one another
18
+ #-----------------------------------------------------------------------------
19
+ class DoubleInitializationError < Exception
20
+ def initialize
21
+ super("Re-initializing MultipleConnectionHandler not allowed")
22
+ end
23
+ end
24
+
25
+
26
+ # just a little token class to hand to the ActiveRecord ConnectionHandler
27
+ # so we can piggy-back on its connection management infrastructure. it just
28
+ # requires an object that you can call .name on...though it's sort of
29
+ # expecting a (model) class object...
30
+ #-----------------------------------------------------------------------------
31
+ class DbKey
32
+ attr_accessor :name
33
+ def initialize(name)
34
+ self.name = name
35
+ end
36
+ def ==(other)
37
+ self.name == other.name
38
+ end
39
+ end
40
+
41
+ # return a db connection for the specified name, establishing new pools
42
+ # with the AR connection handler if necessary
43
+ #-----------------------------------------------------------------------------
44
+ def self.connection(spec_name)
45
+ instance.connection(spec_name)
46
+ end
47
+
48
+ # returns the db name for the given spec name
49
+ #-----------------------------------------------------------------------------
50
+ def self.db_name(spec_name)
51
+ instance.configurations[spec_name.to_s]['database']
52
+ end
53
+
54
+ @@static_initialized = false
55
+ # initialize the connections handler. only necessary to be called if
56
+ # overriding any default configuration. recommend against using this
57
+ # method explicitly and just allowing default.
58
+ #
59
+ # options:
60
+ # - :config_file => the database config file to use. default is the
61
+ # rails default of ..../config/database.yml
62
+ #-----------------------------------------------------------------------------
63
+ def self.init(options = {})
64
+ # don't allow re-initializing. just ignore if we're already initialized
65
+ # unless we're asked to use a different config file
66
+ if @@static_initialized
67
+ if @@instantiated && options[:config_file] &&
68
+ options[:config_file] != instance.db_config_file
69
+
70
+ raise DoubleInitializationError
71
+ end
72
+ else
73
+ @@db_config_file = options[:config_file] || "#{RAILS_ROOT}/config/database.yml"
74
+ @@static_initialized = true
75
+ end
76
+
77
+ end
78
+
79
+ attr_accessor :db_config_file, :configurations, :handler
80
+ attr_accessor :established_connections
81
+
82
+ # NOTE: this method is not declared protected so that the class method can
83
+ # call it...but really shouldn't ever be called by external code.
84
+
85
+ # the meat of the work is done here. retrieve a connection using cached
86
+ # connections or get a new one.
87
+ #-----------------------------------------------------------------------------
88
+ def connection(spec_name)
89
+ db_key = DbKey.new(spec_name.to_s)
90
+
91
+ unless established_connections.member? db_key
92
+ unless configurations.include? db_key.name
93
+ raise UnrecognizedDatabaseError.new(db_key.name, db_config_file)
94
+ end
95
+
96
+ spec = configurations[db_key.name]
97
+
98
+ handler.establish_connection(db_key.name,
99
+ ActiveRecord::Base::ConnectionSpecification.new(spec,
100
+ "#{spec['adapter']}_connection"))
101
+
102
+ established_connections.add db_key.name
103
+ end # end connection not established yet
104
+
105
+ handler.retrieve_connection db_key
106
+ end
107
+
108
+ protected
109
+
110
+ # constructor
111
+ # arguments:
112
+ # - db_config_file: filename of database configurations file (in yaml).
113
+ #-----------------------------------------------------------------------------
114
+ def initialize(config_file)
115
+ self.db_config_file = config_file
116
+ self.configurations = YAML::load(File.open(self.db_config_file))
117
+ self.handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
118
+ self.established_connections = Set.new
119
+ end
120
+
121
+ @@db_config_file = nil
122
+ @@instantiated = false
123
+ # the singleton instance. create if not already created, using
124
+ # config filename specified in init()
125
+ #-----------------------------------------------------------------------------
126
+ def self.instance
127
+ unless @@instantiated
128
+ self.init
129
+ @@instance = self.new(@@db_config_file)
130
+ @@instantiated = true
131
+ end
132
+ @@instance
133
+ end
134
+
135
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multiple_connection_handler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - bmpercy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-15 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "2.1"
24
+ version:
25
+ description: " Hacky utility to access dbs listed in Rails' database.yml. See README for more info.\n"
26
+ email:
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/multiple_connection_handler.rb
35
+ - README
36
+ has_rdoc: true
37
+ homepage:
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.8.1
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Rails utility for accessing arbitrary db connection listed in database.yml
64
+ test_files: []
65
+