table_renamable 0.0.1

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 (54) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/javascripts/table_renamable/application.js +15 -0
  6. data/app/assets/stylesheets/table_renamable/application.css +13 -0
  7. data/app/controllers/table_renamable/application_controller.rb +4 -0
  8. data/app/helpers/table_renamable/application_helper.rb +4 -0
  9. data/app/views/layouts/table_renamable/application.html.erb +14 -0
  10. data/config/routes.rb +2 -0
  11. data/lib/table_renamable/connection_adapters/mysql2_adapter.rb +31 -0
  12. data/lib/table_renamable/connection_adapters/sqlite3_adapter.rb +62 -0
  13. data/lib/table_renamable/connection_adapters.rb +13 -0
  14. data/lib/table_renamable/deprecated_table.rb +106 -0
  15. data/lib/table_renamable/engine.rb +22 -0
  16. data/lib/table_renamable/model.rb +95 -0
  17. data/lib/table_renamable/version.rb +3 -0
  18. data/lib/table_renamable.rb +11 -0
  19. data/lib/tasks/table_renamable_tasks.rake +4 -0
  20. data/spec/dummy/README.rdoc +261 -0
  21. data/spec/dummy/Rakefile +7 -0
  22. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  23. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  25. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  26. data/spec/dummy/app/models/post.rb +3 -0
  27. data/spec/dummy/app/models/user.rb +5 -0
  28. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  29. data/spec/dummy/config/application.rb +65 -0
  30. data/spec/dummy/config/boot.rb +10 -0
  31. data/spec/dummy/config/database.yml +11 -0
  32. data/spec/dummy/config/environment.rb +5 -0
  33. data/spec/dummy/config/environments/development.rb +37 -0
  34. data/spec/dummy/config/environments/production.rb +67 -0
  35. data/spec/dummy/config/environments/test.rb +37 -0
  36. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  37. data/spec/dummy/config/initializers/inflections.rb +15 -0
  38. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  39. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  40. data/spec/dummy/config/initializers/session_store.rb +8 -0
  41. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  42. data/spec/dummy/config/locales/en.yml +5 -0
  43. data/spec/dummy/config/routes.rb +4 -0
  44. data/spec/dummy/config.ru +4 -0
  45. data/spec/dummy/db/test.sqlite3 +0 -0
  46. data/spec/dummy/log/test.log +1158 -0
  47. data/spec/dummy/public/404.html +26 -0
  48. data/spec/dummy/public/422.html +26 -0
  49. data/spec/dummy/public/500.html +25 -0
  50. data/spec/dummy/public/favicon.ico +0 -0
  51. data/spec/dummy/script/rails +6 -0
  52. data/spec/lib/table_renamable/model_spec.rb +82 -0
  53. data/spec/spec_helper.rb +40 -0
  54. metadata +157 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 295d31b7d7ae975d082ac5d807465cd1eafad278
4
+ data.tar.gz: 1af6841d11a014733e54832144587c531a795013
5
+ SHA512:
6
+ metadata.gz: 83340587d906f332996104c16470cf33f59c41cceeea0297956ba371901e459565132335a23406abf245ac4e554bbdc1f5b232ad4678f95efb9a264001d5215f
7
+ data.tar.gz: 6e5bc7a75d85aef2fcb27f2d5e0cff43f9fca58ae2bc07fb9209d8ad2bcb7417e3ae015594c1e808e2a9caf2c0637f4eb43d8beeae3932bb6dbf87e3fc2db54d
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = TableRenamable
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'TableRenamable'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,4 @@
1
+ module TableRenamable
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module TableRenamable
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>TableRenamable</title>
5
+ <%= stylesheet_link_tag "table_renamable/application", :media => "all" %>
6
+ <%= javascript_include_tag "table_renamable/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ TableRenamable::Engine.routes.draw do
2
+ end
@@ -0,0 +1,31 @@
1
+ module TableRenamable
2
+ module ConnectionAdapters
3
+ module Mysql2Adapter
4
+
5
+ #
6
+ # Override execute to reload database info
7
+ # @param *args [Array<Mixed>] Just here so we can call super
8
+ #
9
+ # @return [type] [description]
10
+ def execute(sql, name = nil)
11
+ # set up tries so we don't keep retrying
12
+ tries = 0
13
+ begin
14
+ tries += 1
15
+ # call the actual execut behavior
16
+ super(sql, name)
17
+ rescue ActiveRecord::StatementInvalid => e
18
+ # only try once
19
+ raise e if tries > 1
20
+ # re-raise if it's not an error we care about
21
+ raise e unless e.message =~ /Table.*doesn't exist/
22
+ # otherwise we reload and retry
23
+ TableRenamable::Model.reload_tables
24
+ sql = TableRenamable::Model.process_sql(sql)
25
+ retry
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,62 @@
1
+ module TableRenamable
2
+ module ConnectionAdapters
3
+ module SQLite3Adapter
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ # when we are included we add our behavior
8
+ self.included do |klass|
9
+ klass.alias_method_chain(:exec_query, :table_renamable)
10
+ klass.alias_method_chain(:execute, :table_renamable)
11
+ klass.alias_method_chain(:table_structure, :table_renamable)
12
+ end
13
+
14
+ def exec_query_with_table_renamable(*args, &block)
15
+ self.with_retry do
16
+ self.exec_query_without_table_renamable(*args, &block)
17
+ end
18
+ end
19
+
20
+ #
21
+ # Override execute to reload database info
22
+ # @param *args [Array<Mixed>] Just here so we can call super
23
+ #
24
+ # @return [type] [description]
25
+ def execute_with_table_renamable(*args, &block)
26
+ self.with_retry do
27
+ self.execute_without_table_renamable(*args, &block)
28
+ end
29
+ end
30
+
31
+ def table_structure_with_table_renamable(table_name)
32
+ self.with_retry do
33
+ # get the correct table name to check - otherwise this will fail
34
+ # on retry
35
+ current_table_name = TableRenamable::Model.get_current_table_name(
36
+ table_name
37
+ )
38
+ self.table_structure_without_table_renamable(current_table_name)
39
+ end
40
+ end
41
+
42
+
43
+ def with_retry(&block)
44
+ # set up tries so we don't keep retrying
45
+ tries = 0
46
+ begin
47
+ tries += 1
48
+ # call the actual execute behavior
49
+ yield
50
+ rescue ActiveRecord::StatementInvalid => e
51
+ # only try once
52
+ raise e if tries > 1
53
+ # re-raise if it's not an error we care about
54
+ raise e unless e.message =~ /Could not find table/
55
+ # otherwise we reload and retry
56
+ TableRenamable::Model.reload_tables
57
+ retry
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,13 @@
1
+ module TableRenamable
2
+ module ConnectionAdapters
3
+
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Mysql2Adapter
7
+ autoload(
8
+ :SQLite3Adapter,
9
+ 'table_renamable/connection_adapters/sqlite3_adapter'
10
+ )
11
+
12
+ end
13
+ end
@@ -0,0 +1,106 @@
1
+ module TableRenamable
2
+ #
3
+ # Class that handles the renaming of tables. This sets the table name
4
+ # of the class based on whether or not the table exists in the database
5
+ #
6
+ # @author [dlangevin]
7
+ #
8
+ class DeprecatedTable
9
+
10
+ class NoTableError < Exception; end;
11
+
12
+ # @!attribute klass
13
+ # @return [Class] The class whose table we are renaming
14
+ attr_reader :klass
15
+
16
+ # @!attribute old_name
17
+ # @return [Symbol] The old table name
18
+ attr_reader :old_name
19
+
20
+ # @!attribute new_name
21
+ # @return [Symbol] The new table name
22
+ attr_reader :new_name
23
+
24
+ #
25
+ # Constructor - sets up the record and tries to connect to
26
+ # the correct database
27
+ #
28
+ # @param klass [Class] Class whose table we are renaming
29
+ # @param old_name [String, Symbol] The old table name
30
+ # @param new_name [String, Symbol] The new table name
31
+ #
32
+ def initialize(klass, old_name, new_name)
33
+ @klass = klass
34
+ @old_name = old_name
35
+ @new_name = new_name
36
+ # call to set the correct table name
37
+ self.set_table_name
38
+ end
39
+
40
+ #
41
+ # Returns the name of the table that currently exists of our
42
+ # two options (old or new)
43
+ #
44
+ # @return [String] The name of the existing table
45
+ def get_current_table_name
46
+ [self.old_name, self.new_name].each do |name|
47
+ return name.to_s if self.table_exists?(name)
48
+ end
49
+ # raise exception if we don't have a valid table
50
+ self.raise_no_table_error
51
+ end
52
+
53
+ #
54
+ # Is this table name part of our DeprecatedTable definition
55
+ # @param table_name [String, Symbol] Table name to chck
56
+ #
57
+ # @return [Boolean] Whether or not we have it in our definition
58
+ def has_table?(table_name)
59
+ [self.old_name, self.new_name].include?(table_name.to_sym)
60
+ end
61
+
62
+ #
63
+ # Set the correct table name for the Class we are controlling
64
+ #
65
+ # @raise [TableRenamable::NoTableError] Error if neither name works
66
+ #
67
+ # @return [Boolean] True if we set the table name
68
+ def set_table_name
69
+ [self.old_name, self.new_name].each do |name|
70
+ # make sure this table exists
71
+ if self.table_exists?(name)
72
+ # return true if we are already using this table
73
+ return true if self.klass.table_name == name.to_s
74
+ # otherwise we can change the table name
75
+ self.klass.table_name = name
76
+ return true
77
+ end
78
+ end
79
+ self.raise_no_table_error
80
+ end
81
+
82
+ protected
83
+
84
+ #
85
+ # Wrapper to raise an error
86
+ #
87
+ # @raise [DeprecatedTable::NoTableError]
88
+ def raise_no_table_error
89
+ raise NoTableError.new(
90
+ "No table for #{self.klass}. " +
91
+ "Tried #{self.old_name} and #{self.new_name}"
92
+ )
93
+ end
94
+
95
+ #
96
+ # Does a given table exist?
97
+ # @param name [String, Symbol] Table name
98
+ #
99
+ # @return [Boolean] Whether or not it exists
100
+ def table_exists?(name)
101
+ self.klass.connection.tables.include?(name.to_s)
102
+ end
103
+
104
+
105
+ end
106
+ end
@@ -0,0 +1,22 @@
1
+ module TableRenamable
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace TableRenamable
4
+
5
+ config.after_initialize do
6
+ # set up our reload behavior for when table names change for MySQL
7
+ if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
8
+ ActiveRecord::ConnectionAdapters::Mysql2Adapter.send(
9
+ :include,
10
+ TableRenamable::ConnectionAdapters::Mysql2Adapter
11
+ )
12
+ # same thing for SQLite
13
+ elsif defined?(ActiveRecord::ConnectionAdapters::SQLite3Adapter)
14
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.send(
15
+ :include,
16
+ TableRenamable::ConnectionAdapters::SQLite3Adapter
17
+ )
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,95 @@
1
+ module TableRenamable
2
+ #
3
+ # Model that is included in ActiveRecord to enable our behavior
4
+ #
5
+ # @author [dlangevin]
6
+ #
7
+ module Model
8
+
9
+ extend ActiveSupport::Concern
10
+
11
+ class NoTableError < Exception; end;
12
+
13
+
14
+ included do |klass|
15
+ klass.class_attribute :deprecated_columns
16
+ klass.deprecated_columns = []
17
+ end
18
+
19
+
20
+ #
21
+ # Return our list of deprecated tables
22
+ #
23
+ # @return [Array<DeprecatedTable>] Our list of deprecated tables
24
+ def self.deprecated_tables
25
+ @deprecated_tables ||= []
26
+ end
27
+
28
+ #
29
+ # Update a string of SQL to replace deprecated tables
30
+ # @param sql [String] SQL TO update
31
+ #
32
+ # @return [String] Updated SQL
33
+ def self.process_sql(sql)
34
+ self.deprecated_tables.each do |deprecated_table|
35
+ # our current table name
36
+ current_table_name = deprecated_table.get_current_table_name
37
+ old_table_name = deprecated_table.old_name
38
+ sql = sql.gsub(/#{old_table_name}/, current_table_name.to_s)
39
+ end
40
+ sql
41
+ end
42
+
43
+ #
44
+ # Reload our table names so we pick up any changes
45
+ #
46
+ # @return [Boolean] Always true
47
+ def self.reload_tables
48
+ self.deprecated_tables.each(&:set_table_name)
49
+ true
50
+ end
51
+
52
+ #
53
+ # ClassMethods to be mixed in to the model using this behavior
54
+ #
55
+ # @author [dlangevin]
56
+ #
57
+ module ClassMethods
58
+
59
+ #
60
+ # Overrides columns to remove deprecated columns
61
+ #
62
+ # @return [Array<>] Filtered array of columns
63
+ def columns
64
+ super.reject { |column|
65
+ self.deprecated_columns.include?(column.name)
66
+ }
67
+ end
68
+
69
+ #
70
+ # Mark columns as deprecated
71
+ # @param *column_names [Array<String, Symbol>] Column names
72
+ #
73
+ # @return [Array<String>] List of deprecated columns
74
+ def deprecate_columns(*column_names)
75
+ self.deprecated_columns =
76
+ self.deprecated_columns + Array.wrap(column_names).collect(&:to_s)
77
+ end
78
+
79
+ #
80
+ # Mark this class as having a potentially new table name
81
+ # @param old_name [String, Symbol] The old table name
82
+ # @param new_name [String, Symbol] The new table name
83
+ #
84
+ # @return [TableRenamable::DeprecatedTable] The newly
85
+ # created TableRenamable::DeprecatedTable records
86
+ def deprecate_table_name(old_name, new_name)
87
+ deprecated_table = DeprecatedTable.new(self, old_name, new_name)
88
+ TableRenamable::Model.deprecated_tables << deprecated_table
89
+ deprecated_table
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,3 @@
1
+ module TableRenamable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require "table_renamable/engine"
2
+
3
+ module TableRenamable
4
+
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :ConnectionAdapters
8
+ autoload :DeprecatedTable
9
+ autoload :Model
10
+
11
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :table_renamable do
3
+ # # Task goes here
4
+ # end