Module: Hijacker
- Defined in:
- lib/hijacker.rb,
lib/hijacker/alias.rb,
lib/hijacker/base_model.rb,
lib/hijacker/middleware.rb,
lib/hijacker/request_parser.rb
Defined Under Namespace
Modules: ControllerMethods Classes: Alias, BaseModel, Database, Host, InvalidDatabase, Middleware, RequestParser, UnparseableURL
Class Attribute Summary collapse
-
.config ⇒ Object
Returns the value of attribute config.
-
.master ⇒ Object
Returns the value of attribute master.
-
.sister ⇒ Object
Returns the value of attribute sister.
- .valid_routes ⇒ Object
Class Method Summary collapse
-
.check_connection ⇒ Object
just calling establish_connection doesn’t actually check to see if we’ve established a VALID connection.
-
.connect(target_name, sister_name = nil, options = {}) ⇒ Object
Manually establishes a new connection to the database.
-
.connect_sister_site_models(master_database) ⇒ Object
very small chance this will raise, but if it does, we will still handle it the same as
Hijacker.connectso we don’t lock up the app. - .connect_to_master(db_name) ⇒ Object
- .current_client ⇒ Object
- .database_configurations ⇒ Object
- .do_hijacking? ⇒ Boolean
-
.establish_root_connection ⇒ Object
this should establish a connection to a database containing the bare minimum for loading the app, usually a sessions table if using sql-based sessions.
- .processing_sister_site? ⇒ Boolean
- .root_config ⇒ Object
-
.root_connection ⇒ Object
The advantage of using this over just calling ActiveRecord::Base.establish_connection (without arguments) to reconnect to the root database is that reusing the same connection greatly reduces context switching overhead etc involved with establishing a connection to the database.
-
.temporary_sister_connect(db, &block) ⇒ Object
connects the sister_site_models to
dbwhile calling the block ifdband self.master differ.
Class Attribute Details
.config ⇒ Object
Returns the value of attribute config.
18 19 20 |
# File 'lib/hijacker.rb', line 18 def config @config end |
.master ⇒ Object
Returns the value of attribute master.
18 19 20 |
# File 'lib/hijacker.rb', line 18 def master @master end |
.sister ⇒ Object
Returns the value of attribute sister.
18 19 20 |
# File 'lib/hijacker.rb', line 18 def sister @sister end |
.valid_routes ⇒ Object
22 23 24 |
# File 'lib/hijacker.rb', line 22 def self.valid_routes @valid_routes ||= {} end |
Class Method Details
.check_connection ⇒ Object
just calling establish_connection doesn’t actually check to see if we’ve established a VALID connection. a call to connection will check this, and throw an error if the connection’s invalid. It is important to catch the error and reconnect to a known valid database or rails will get stuck. This is because once we establish a connection to an invalid database, the next request will do a courteousy touch to the invalid database before reaching establish_connection and throw an error, preventing us from retrying to establish a valid connection and effectively locking us out of the app.
195 196 197 |
# File 'lib/hijacker.rb', line 195 def self.check_connection ::ActiveRecord::Base.connection end |
.connect(target_name, sister_name = nil, options = {}) ⇒ Object
Manually establishes a new connection to the database.
Background: every time rails gets information from the database, it uses the last established connection. So, although we’ve already established a connection to a root db (“crystal”, in this case), if we establish a new connection, all subsequent database calls will use these settings instead (well, until it’s called again when it gets another request).
Note that you can manually call this from script/console (or wherever) to connect to the database you want, ex Hijacker.connect(“database”)
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/hijacker.rb', line 41 def self.connect(target_name, sister_name = nil, = {}) original_database = Hijacker::Database.current begin raise InvalidDatabase.new(nil, 'master cannot be nil') if target_name.nil? target_name = target_name.downcase sister_name = sister_name.downcase unless sister_name.nil? if already_connected?(target_name, sister_name) run_after_hijack_callback return "Already connected to #{target_name}" end database = determine_database(target_name, sister_name) establish_connection_to_database(database) check_connection if database.sister? self.master = database.master.name self.sister = database.name else self.master = database.name self.sister = nil end # don't cache sister site cache_database_route(target_name, database) unless sister_name # Do this even on a site without a master so we reconnect these models connect_sister_site_models(database.master || database) reenable_query_caching run_after_hijack_callback rescue if original_database.present? establish_connection_to_database(original_database) else self.establish_root_connection end raise end end |
.connect_sister_site_models(master_database) ⇒ Object
very small chance this will raise, but if it does, we will still handle it the same as Hijacker.connect so we don’t lock up the app.
Also note that sister site models share a connection via minor management of AR’s connection_pool stuff, and will use ActiveRecord::Base.connection_pool if we’re not in a sister-site situation
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/hijacker.rb', line 94 def self.connect_sister_site_models(master_database) master_db_connection_pool = if processing_sister_site? nil else ActiveRecord::Base.connection_pool end master_config = connection_config(master_database) config[:sister_site_models].each do |model_name| klass = model_name.constantize klass.establish_connection(master_config) if !master_db_connection_pool begin klass.connection rescue klass.establish_connection(root_config) raise Hijacker::InvalidDatabase.new(database.name) end master_db_connection_pool = klass.connection_pool else ActiveRecord::Base.connection_handler.connection_pools[model_name] = master_db_connection_pool end end end |
.connect_to_master(db_name) ⇒ Object
26 27 28 |
# File 'lib/hijacker.rb', line 26 def self.connect_to_master(db_name) connect(*Hijacker::Database.find_master_and_sister_for(db_name)) end |
.current_client ⇒ Object
177 178 179 |
# File 'lib/hijacker.rb', line 177 def self.current_client sister || master end |
.database_configurations ⇒ Object
159 160 161 |
# File 'lib/hijacker.rb', line 159 def self.database_configurations ActiveRecord::Base.configurations end |
.do_hijacking? ⇒ Boolean
181 182 183 184 |
# File 'lib/hijacker.rb', line 181 def self.do_hijacking? (Hijacker.config[:hosted_environments] || %w[staging production]). include?(ENV['RAILS_ENV'] || Rails.env) end |
.establish_root_connection ⇒ Object
this should establish a connection to a database containing the bare minimum for loading the app, usually a sessions table if using sql-based sessions.
165 166 167 |
# File 'lib/hijacker.rb', line 165 def self.establish_root_connection ActiveRecord::Base.establish_connection('root') end |
.processing_sister_site? ⇒ Boolean
169 170 171 |
# File 'lib/hijacker.rb', line 169 def self.processing_sister_site? !sister.nil? end |
.root_config ⇒ Object
155 156 157 |
# File 'lib/hijacker.rb', line 155 def self.root_config database_configurations.fetch('root').with_indifferent_access end |
.root_connection ⇒ Object
The advantage of using this over just calling ActiveRecord::Base.establish_connection (without arguments) to reconnect to the root database is that reusing the same connection greatly reduces context switching overhead etc involved with establishing a connection to the database. It may seem trivial, but it actually seems to speed things up by ~ 1/3 for already fast requests (probably less noticeable on slower pages).
Note: does not hijack, just returns the root connection (i.e. AR::Base will maintain its connection)
144 145 146 147 148 149 150 151 152 153 |
# File 'lib/hijacker.rb', line 144 def self.root_connection unless $hijacker_root_connection current_config = ActiveRecord::Base.connection.config ActiveRecord::Base.establish_connection('root') # establish with defaults $hijacker_root_connection = ActiveRecord::Base.connection ActiveRecord::Base.establish_connection(current_config) # reconnect, we don't intend to hijack end $hijacker_root_connection end |
.temporary_sister_connect(db, &block) ⇒ Object
connects the sister_site_models to db while calling the block if db and self.master differ
123 124 125 126 127 128 129 130 131 132 |
# File 'lib/hijacker.rb', line 123 def self.temporary_sister_connect(db, &block) processing_sister_site = (db != master && db != sister) self.sister = db if processing_sister_site self.connect_sister_site_models(db) if processing_sister_site result = block.call self.connect_sister_site_models(self.master) if processing_sister_site self.sister = nil if processing_sister_site result end |