Module: ActiveRecord::SnapshotView::ClassMethods
- Defined in:
- lib/activerecord_snapshot_view/snapshot_view.rb
Constant Summary collapse
- ALPHABET =
"abcdefghijklmnopqrstuvwxyz"
Class Method Summary collapse
Instance Method Summary collapse
-
#active_table_name ⇒ Object
name of the active table read direct from db.
- #active_working_table_name ⇒ Object
- #active_working_table_name=(name) ⇒ Object
-
#active_working_table_or_active_table_name ⇒ Object
name of the active table, or the working table if inside a new_version block.
-
#advance_version ⇒ Object
make working table active, then recreate new working table from base table schema.
- #base_table_name ⇒ Object
-
#create_switch_table(name) ⇒ Object
create a switch table of given name, if it doesn’t already exist.
- #default_active_table_name ⇒ Object
-
#dup_table_schema(from, to) ⇒ Object
use schema of from table to recreate to table.
- #ensure_all_tables ⇒ Object
-
#ensure_switch_table ⇒ Object
create the switch table if it doesn’t already exist.
- #ensure_version_table(name) ⇒ Object
-
#historical_version_count ⇒ Object
number of historical tables to keep around for posterity, or more likely to ensure running transactions aren’t taken down by advance_version recreating a table.
-
#new_version(&block) ⇒ Object
make the working table temporarily active [ for this thread only ], execute the block, and if completed without exception then make the working table permanently active.
-
#prepare_to_migrate ⇒ Object
copy all data to base table, reset switch table and drop suffixed tables…
-
#set_historical_version_count(count) ⇒ Object
(also: #historical_version_count=)
set the number of historical tables to keep around to ensure running transactions aren’t interrupted by truncating working tables.
-
#set_table_name(name) ⇒ Object
(also: #table_name=)
hide the ActiveRecord::Base method, which redefines a table_name method, and instead capture the given name as the base_table_name.
-
#suffixed_table_names ⇒ Object
list of suffixed table names.
-
#switch_table_name ⇒ Object
name of the table with a row holding the active table name.
-
#switch_to(table) ⇒ Object
update switch table to point to a different table.
-
#table_version_names ⇒ Object
ordered vector of table version names, starting with base name.
- #thread_local_key_name ⇒ Object
-
#updated_version(update = true, &block) ⇒ Object
like new_version, but instead of an empty table you start with a copy of the previous version (if update=true).
-
#working_table_name ⇒ Object
name of the working table.
Class Method Details
.included(mod) ⇒ Object
89 90 91 92 93 94 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 89 def ClassMethods.included(mod) mod.instance_eval do alias_method :org_table_name, :table_name alias_method :table_name, :active_working_table_or_active_table_name end end |
Instance Method Details
#active_table_name ⇒ Object
name of the active table read direct from db
199 200 201 202 203 204 205 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 199 def active_table_name st = switch_table_name begin connection.select_value( "select current from #{st}" ) rescue end || default_active_table_name end |
#active_working_table_name ⇒ Object
254 255 256 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 254 def active_working_table_name Thread.current[thread_local_key_name] end |
#active_working_table_name=(name) ⇒ Object
258 259 260 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 258 def active_working_table_name=(name) Thread.current[thread_local_key_name] = name end |
#active_working_table_or_active_table_name ⇒ Object
name of the active table, or the working table if inside a new_version block
263 264 265 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 263 def active_working_table_or_active_table_name active_working_table_name || active_table_name end |
#advance_version ⇒ Object
make working table active, then recreate new working table from base table schema
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 233 def advance_version switch_to(working_table_name) # ensure the presence of the new active and working tables. # happens after the switch table update, since this may commit a surrounding # transaction in dbs with retarded non-transactional ddl like, oh i dunno, MyFuckingSQL ensure_version_table(active_table_name) # recreate the new working table from the base schema. new_wtn = working_table_name if new_wtn != base_table_name dup_table_schema(base_table_name, new_wtn) else connection.execute( "truncate table #{new_wtn}" ) end end |
#base_table_name ⇒ Object
117 118 119 120 121 122 123 124 125 126 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 117 def base_table_name if !@base_table_name @base_table_name = org_table_name # the original table_name method re-aliases itself ! class << self alias_method :table_name, :active_working_table_or_active_table_name end end @base_table_name end |
#create_switch_table(name) ⇒ Object
create a switch table of given name, if it doesn’t already exist
160 161 162 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 160 def create_switch_table(name) connection.execute( "create table if not exists #{name} (`current` varchar(255))" ) end |
#default_active_table_name ⇒ Object
192 193 194 195 196 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 192 def default_active_table_name # no longer use a different table name for test environments... # it makes a mess with named scopes base_table_name end |
#dup_table_schema(from, to) ⇒ Object
use schema of from table to recreate to table
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 129 def dup_table_schema(from, to) connection.execute( "drop table if exists #{to}") ct = connection.select_one( "show create table #{from}")["Create Table"] ct_no_constraint_names = ct.gsub(/CONSTRAINT `[^`]*`/, "CONSTRAINT ``") i = 0 ct_uniq_constraint_names = ct_no_constraint_names.gsub(/CONSTRAINT ``/) { |s| i+=1 ; "CONSTRAINT `#{to}_#{i}`" } new_ct = ct_uniq_constraint_names.gsub( /CREATE TABLE `#{from}`/, "CREATE TABLE `#{to}`") connection.execute(new_ct) end |
#ensure_all_tables ⇒ Object
214 215 216 217 218 219 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 214 def ensure_all_tables suffixed_table_names.each do |table_name| ensure_version_table(table_name) end ensure_switch_table end |
#ensure_switch_table ⇒ Object
create the switch table if it doesn’t already exist. return the switch table name
165 166 167 168 169 170 171 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 165 def ensure_switch_table stn = switch_table_name if !connection.table_exists?(stn) # don't execute any ddl code if we don't need to create_switch_table(stn) end stn end |
#ensure_version_table(name) ⇒ Object
152 153 154 155 156 157 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 152 def ensure_version_table(name) if !connection.table_exists?(name) && base_table_name!=name # don't execute ddl unless necessary dup_table_schema(base_table_name, name) end end |
#historical_version_count ⇒ Object
number of historical tables to keep around for posterity, or more likely to ensure running transactions aren’t taken down by advance_version recreating a table. default 2
99 100 101 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 99 def historical_version_count @historical_version_count || 2 end |
#new_version(&block) ⇒ Object
make the working table temporarily active [ for this thread only ], execute the block, and if completed without exception then make the working table permanently active
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 285 def new_version(&block) begin self.active_working_table_name = working_table_name ensure_version_table(working_table_name) connection.execute("truncate table #{working_table_name}") r = block.call advance_version r rescue SaveWork => e advance_version raise e # raise the SaveWork again, in case we are inside nested new_versions ensure self.active_working_table_name = nil end end |
#prepare_to_migrate ⇒ Object
copy all data to base table, reset switch table and drop suffixed tables… for migration support
141 142 143 144 145 146 147 148 149 150 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 141 def prepare_to_migrate if active_table_name != base_table_name connection.execute( "truncate table #{base_table_name}" ) connection.execute( "insert into #{base_table_name} select * from #{active_table_name}" ) end suffixed_table_names.each do |stn| connection.execute( "drop table if exists #{stn}" ) end connection.execute( "drop table if exists #{switch_table_name}" ) end |
#set_historical_version_count(count) ⇒ Object Also known as: historical_version_count=
set the number of historical tables to keep around to ensure running transactions aren’t interrupted by truncating working tables. 2 is default
105 106 107 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 105 def set_historical_version_count(count) @historical_version_count = count end |
#set_table_name(name) ⇒ Object Also known as: table_name=
hide the ActiveRecord::Base method, which redefines a table_name method, and instead capture the given name as the base_table_name
112 113 114 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 112 def set_table_name(name) @base_table_name = name end |
#suffixed_table_names ⇒ Object
list of suffixed table names
179 180 181 182 183 184 185 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 179 def suffixed_table_names suffixes = [] (0...historical_version_count).each{ |i| suffixes << ALPHABET[i...i+1] } suffixes.map do |suffix| base_table_name + "_" + suffix end end |
#switch_table_name ⇒ Object
name of the table with a row holding the active table name
174 175 176 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 174 def switch_table_name base_table_name + "_switch" end |
#switch_to(table) ⇒ Object
update switch table to point to a different table
222 223 224 225 226 227 228 229 230 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 222 def switch_to(table) st = ensure_switch_table # want a transaction at least here [surround is ok too] so # there is never an empty switch table ActiveRecord::Base.transaction do connection.execute( "delete from #{st}") connection.execute( "insert into #{st} values (\'#{table}\')") end end |
#table_version_names ⇒ Object
ordered vector of table version names, starting with base name
188 189 190 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 188 def table_version_names [base_table_name] + suffixed_table_names end |
#thread_local_key_name ⇒ Object
250 251 252 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 250 def thread_local_key_name "ActiveRecord::SnapshotView::" + self.to_s end |
#updated_version(update = true, &block) ⇒ Object
like new_version, but instead of an empty table you start with a copy of the previous version (if update=true)
269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 269 def updated_version(update=true, &block) new_version do if update sql = <<-EOF insert into #{working_table_name} select * from #{active_table_name} EOF connection.execute(sql) end block.call end end |
#working_table_name ⇒ Object
name of the working table
208 209 210 211 212 |
# File 'lib/activerecord_snapshot_view/snapshot_view.rb', line 208 def working_table_name atn = active_table_name tvn = table_version_names tvn[ (tvn.index(atn) + 1) % tvn.size ] end |