activerecord-mysql-awesome 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +11 -0
- data/README.md +1 -1
- data/Rakefile +60 -0
- data/activerecord-mysql-awesome.gemspec +4 -2
- data/lib/activerecord-mysql-awesome/active_record/connection_adapters/abstract_mysql_adapter.rb +130 -28
- data/lib/activerecord/mysql/awesome/version.rb +1 -1
- data/test/.gitignore +1 -0
- data/test/cases/datetime_test.rb +98 -0
- data/test/cases/helper.rb +51 -0
- data/test/cases/test_case.rb +123 -0
- data/test/config.example.yml +20 -0
- data/test/config.rb +5 -0
- data/test/support/config.rb +43 -0
- data/test/support/connection.rb +18 -0
- data/test/support/schema_dumping_helper.rb +20 -0
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c4fa2c015cdf2511abc7e277b57491d4dcbbd28
|
4
|
+
data.tar.gz: 37e2778cd1ed6cd57748d255753aba1741371b4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55f18e15f1694fcb7365e3716d0394e2181bc11248d38a1b5de2c08b10794b597d490f047b88467afb93bf45d9a6f72726d08955b04d838dc0f23eaf8786ac1a
|
7
|
+
data.tar.gz: b039c7d9c91e43d1a8e8d575cc0363bd5b8daf8fcd9e30766af98b8470314ef7f59148244d071ae249630c756db0f0579ae01f94e17b3ad97143c4182410281d
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://travis-ci.org/kamipo/activerecord-mysql-awesome)
|
4
4
|
|
5
|
-
|
5
|
+
Awesome patches backported for ActiveRecord MySQL adapters.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
data/Rakefile
CHANGED
@@ -1,2 +1,62 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
2
3
|
|
4
|
+
require File.expand_path(File.dirname(__FILE__)) + "/test/config"
|
5
|
+
require File.expand_path(File.dirname(__FILE__)) + "/test/support/config"
|
6
|
+
|
7
|
+
desc 'Run mysql2 tests by default'
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
desc 'Run mysql2 tests'
|
11
|
+
task :test => :test_mysql2
|
12
|
+
|
13
|
+
desc 'Build MySQL test databases'
|
14
|
+
namespace :db do
|
15
|
+
task :create => ['db:mysql:build']
|
16
|
+
task :drop => ['db:mysql:drop']
|
17
|
+
end
|
18
|
+
|
19
|
+
%w( mysql mysql2 ).each do |adapter|
|
20
|
+
namespace :test do
|
21
|
+
Rake::TestTask.new(adapter => "#{adapter}:env") { |t|
|
22
|
+
t.libs << 'test'
|
23
|
+
t.test_files = Dir.glob( "test/cases/**/*_test.rb" ).sort
|
24
|
+
|
25
|
+
t.warning = true
|
26
|
+
t.verbose = true
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
namespace adapter do
|
31
|
+
task :test => "test_#{adapter}"
|
32
|
+
|
33
|
+
# Set the connection environment for the adapter
|
34
|
+
task(:env) { ENV['ARCONN'] = adapter }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Make sure the adapter test evaluates the env setting task
|
38
|
+
task "test_#{adapter}" => ["#{adapter}:env", "test:#{adapter}"]
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :db do
|
42
|
+
namespace :mysql do
|
43
|
+
desc 'Build the MySQL test databases'
|
44
|
+
task :build do
|
45
|
+
config = ARTest.config['connections']['mysql']
|
46
|
+
%x( mysql --user=#{config['arunit']['username']} -e "create DATABASE #{config['arunit']['database']} DEFAULT CHARACTER SET utf8" )
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'Drop the MySQL test databases'
|
50
|
+
task :drop do
|
51
|
+
config = ARTest.config['connections']['mysql']
|
52
|
+
%x( mysqladmin --user=#{config['arunit']['username']} -f drop #{config['arunit']['database']} )
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'Rebuild the MySQL test databases'
|
56
|
+
task :rebuild => [:drop, :build]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
task :build_mysql_databases => 'db:mysql:build'
|
61
|
+
task :drop_mysql_databases => 'db:mysql:drop'
|
62
|
+
task :rebuild_mysql_databases => 'db:mysql:rebuild'
|
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = ActiveRecord::Mysql::Awesome::VERSION
|
9
9
|
spec.authors = ["Ryuta Kamizono"]
|
10
10
|
spec.email = ["kamipo@gmail.com"]
|
11
|
-
spec.summary = %q{
|
12
|
-
spec.description = %q{
|
11
|
+
spec.summary = %q{Awesome patches backported for ActiveRecord MySQL adapters}
|
12
|
+
spec.description = %q{Awesome patches backported for ActiveRecord MySQL adapters}
|
13
13
|
spec.homepage = "https://github.com/kamipo/activerecord-mysql-awesome"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
21
23
|
spec.add_development_dependency "bundler", "~> 1.7"
|
22
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
25
|
spec.add_runtime_dependency "activesupport", "~> 4.0"
|
data/lib/activerecord-mysql-awesome/active_record/connection_adapters/abstract_mysql_adapter.rb
CHANGED
@@ -1,8 +1,138 @@
|
|
1
1
|
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
+
module Mysql
|
5
|
+
module Awesome
|
6
|
+
def type_to_sql(type, limit = nil, precision = nil, scale = nil, unsigned = false)
|
7
|
+
case type.to_s
|
8
|
+
when 'integer'
|
9
|
+
case limit
|
10
|
+
when nil, 4, 11; 'int' # compatibility with MySQL default
|
11
|
+
else
|
12
|
+
super(type, limit, precision, scale)
|
13
|
+
end.tap do |sql_type|
|
14
|
+
sql_type << ' unsigned' if unsigned
|
15
|
+
end
|
16
|
+
when 'float', 'decimal'
|
17
|
+
super(type, limit, precision, scale).tap do |sql_type|
|
18
|
+
sql_type << ' unsigned' if unsigned
|
19
|
+
end
|
20
|
+
when 'primary_key'
|
21
|
+
"#{type_to_sql(:integer, limit, precision, scale, unsigned)} auto_increment PRIMARY KEY"
|
22
|
+
when 'datetime', 'time'
|
23
|
+
return super(type, limit, precision, scale) unless precision
|
24
|
+
|
25
|
+
native_type = native_database_types[type.to_sym][:name]
|
26
|
+
case precision
|
27
|
+
when 0..6; "#{native_type}(#{precision})"
|
28
|
+
else raise(ActiveRecordError, "No #{native_type} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
|
29
|
+
end
|
30
|
+
else
|
31
|
+
super(type, limit, precision, scale)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Column
|
36
|
+
def unsigned?
|
37
|
+
sql_type =~ /unsigned/i
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if ActiveRecord::VERSION::STRING < "4.2.0"
|
42
|
+
def quote(value, column = nil) #:nodoc:
|
43
|
+
return super unless column && column.type
|
44
|
+
|
45
|
+
case column.type
|
46
|
+
when :datetime, :time
|
47
|
+
if value.acts_like?(:time) && value.respond_to?(:usec)
|
48
|
+
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
49
|
+
result = value.send(zone_conversion_method).to_s(:db)
|
50
|
+
precision = column.precision
|
51
|
+
case precision
|
52
|
+
when 1..6
|
53
|
+
"'#{result}.#{sprintf("%0#{precision}d", value.usec / 10**(6 - precision))}'"
|
54
|
+
when 0, nil
|
55
|
+
"'#{result}'"
|
56
|
+
end
|
57
|
+
else
|
58
|
+
super
|
59
|
+
end
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module Column
|
66
|
+
def extract_limit(sql_type)
|
67
|
+
case sql_type
|
68
|
+
when /^(?:date)?time/i; nil
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_precision(sql_type)
|
75
|
+
case sql_type
|
76
|
+
when /^(?:date)?time/i
|
77
|
+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
|
78
|
+
else
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
else
|
84
|
+
protected
|
85
|
+
|
86
|
+
def initialize_type_map(m) # :nodoc:
|
87
|
+
super
|
88
|
+
register_class_with_precision m, %r(time)i, MysqlTime
|
89
|
+
register_class_with_precision m, %r(datetime)i, MysqlDateTime
|
90
|
+
end
|
91
|
+
|
92
|
+
def register_class_with_precision(mapping, key, klass) # :nodoc:
|
93
|
+
mapping.register_type(key) do |*args|
|
94
|
+
precision = extract_precision(args.last)
|
95
|
+
klass.new(precision: precision)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
module TimeValueWithPrecision
|
102
|
+
def type_cast_for_database(value)
|
103
|
+
if value.acts_like?(:time) && value.respond_to?(:usec)
|
104
|
+
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
|
105
|
+
result = value.send(zone_conversion_method).to_s(:db)
|
106
|
+
case precision
|
107
|
+
when 1..6
|
108
|
+
"#{result}.#{sprintf("%0#{precision}d", value.usec / 10**(6 - precision))}"
|
109
|
+
when 0, nil
|
110
|
+
result
|
111
|
+
end
|
112
|
+
else
|
113
|
+
super
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class MysqlTime < Type::Time # :nodoc:
|
119
|
+
include TimeValueWithPrecision
|
120
|
+
end
|
121
|
+
|
122
|
+
class MysqlDateTime < Type::DateTime # :nodoc:
|
123
|
+
include TimeValueWithPrecision
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
4
129
|
module ConnectionAdapters
|
5
130
|
class AbstractMysqlAdapter < AbstractAdapter
|
131
|
+
prepend Mysql::Awesome
|
132
|
+
|
133
|
+
class Column < ConnectionAdapters::Column # :nodoc:
|
134
|
+
prepend Mysql::Awesome::Column
|
135
|
+
end
|
6
136
|
|
7
137
|
class ChangeColumnDefinition < Struct.new(:column, :name) #:nodoc:
|
8
138
|
end
|
@@ -95,12 +225,6 @@ module ActiveRecord
|
|
95
225
|
end
|
96
226
|
end
|
97
227
|
|
98
|
-
class Column < ConnectionAdapters::Column # :nodoc:
|
99
|
-
def unsigned?
|
100
|
-
sql_type =~ /unsigned/i
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
228
|
def options_for_column_spec(table_name)
|
105
229
|
if collation = select_one("SHOW TABLE STATUS LIKE '#{table_name}'")["Collation"]
|
106
230
|
super.merge(collation: collation)
|
@@ -132,28 +256,6 @@ module ActiveRecord
|
|
132
256
|
raw_table_options.sub(/(ENGINE=\w+)(?: AUTO_INCREMENT=\d+)/, '\1')
|
133
257
|
end
|
134
258
|
|
135
|
-
alias type_to_sql_without_awesome type_to_sql
|
136
|
-
def type_to_sql(type, limit = nil, precision = nil, scale = nil, unsigned = false)
|
137
|
-
case type.to_s
|
138
|
-
when 'integer'
|
139
|
-
case limit
|
140
|
-
when nil, 4, 11; 'int' # compatibility with MySQL default
|
141
|
-
else
|
142
|
-
type_to_sql_without_awesome(type, limit, precision, scale)
|
143
|
-
end.tap do |sql_type|
|
144
|
-
sql_type << ' unsigned' if unsigned
|
145
|
-
end
|
146
|
-
when 'float', 'decimal'
|
147
|
-
type_to_sql_without_awesome(type, limit, precision, scale).tap do |sql_type|
|
148
|
-
sql_type << ' unsigned' if unsigned
|
149
|
-
end
|
150
|
-
when 'primary_key'
|
151
|
-
"#{type_to_sql(:integer, limit, precision, scale, unsigned)} auto_increment PRIMARY KEY"
|
152
|
-
else
|
153
|
-
type_to_sql_without_awesome(type, limit, precision, scale)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
259
|
def add_column_sql(table_name, column_name, type, options = {})
|
158
260
|
td = create_table_definition(table_name)
|
159
261
|
cd = td.new_column_definition(column_name, type, options)
|
data/test/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/config.yml
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'cases/helper'
|
2
|
+
require 'support/schema_dumping_helper'
|
3
|
+
|
4
|
+
if mysql_56?
|
5
|
+
class DateTimeTest < ActiveRecord::TestCase
|
6
|
+
include SchemaDumpingHelper
|
7
|
+
|
8
|
+
class Foo < ActiveRecord::Base; end
|
9
|
+
|
10
|
+
def test_default_datetime_precision
|
11
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true)
|
12
|
+
ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime
|
13
|
+
ActiveRecord::Base.connection.add_column :foos, :updated_at, :datetime
|
14
|
+
assert_nil activerecord_column_option('foos', 'created_at', 'precision')
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_datetime_data_type_with_precision
|
18
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true)
|
19
|
+
ActiveRecord::Base.connection.add_column :foos, :created_at, :datetime, precision: 1
|
20
|
+
ActiveRecord::Base.connection.add_column :foos, :updated_at, :datetime, precision: 5
|
21
|
+
assert_equal 1, activerecord_column_option('foos', 'created_at', 'precision')
|
22
|
+
assert_equal 5, activerecord_column_option('foos', 'updated_at', 'precision')
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_timestamps_helper_with_custom_precision
|
26
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
|
27
|
+
t.timestamps null: true, precision: 4
|
28
|
+
end
|
29
|
+
assert_equal 4, activerecord_column_option('foos', 'created_at', 'precision')
|
30
|
+
assert_equal 4, activerecord_column_option('foos', 'updated_at', 'precision')
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_passing_precision_to_datetime_does_not_set_limit
|
34
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
|
35
|
+
t.timestamps null: true, precision: 4
|
36
|
+
end
|
37
|
+
assert_nil activerecord_column_option("foos", "created_at", "limit")
|
38
|
+
assert_nil activerecord_column_option("foos", "updated_at", "limit")
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_invalid_datetime_precision_raises_error
|
42
|
+
assert_raises ActiveRecord::ActiveRecordError do
|
43
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
|
44
|
+
t.timestamps null: true, precision: 7
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_mysql_agrees_with_activerecord_about_precision
|
50
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
|
51
|
+
t.timestamps null: true, precision: 4
|
52
|
+
end
|
53
|
+
assert_equal 4, mysql_datetime_precision('foos', 'created_at')
|
54
|
+
assert_equal 4, mysql_datetime_precision('foos', 'updated_at')
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_datetime_format_according_to_precision
|
58
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
|
59
|
+
t.datetime :created_at, precision: 0
|
60
|
+
t.datetime :updated_at, precision: 4
|
61
|
+
end
|
62
|
+
date = ::Time.utc(2014, 8, 17, 12, 30, 0, 999999)
|
63
|
+
Foo.create!(created_at: date, updated_at: date)
|
64
|
+
assert foo = Foo.find_by(created_at: date)
|
65
|
+
assert_equal date.to_s, foo.created_at.to_s
|
66
|
+
assert_equal date.to_s, foo.updated_at.to_s
|
67
|
+
assert_equal 000000, foo.created_at.usec
|
68
|
+
assert_equal 999900, foo.updated_at.usec
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_schema_dump_includes_datetime_precision
|
72
|
+
ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
|
73
|
+
t.datetime :created_at, precision: 4
|
74
|
+
t.datetime :updated_at, precision: 6
|
75
|
+
end
|
76
|
+
schema = dump_table_schema "foos"
|
77
|
+
assert_match %r{t.datetime\s+"created_at",\s+precision: 4$}, schema
|
78
|
+
assert_match %r{t.datetime\s+"updated_at",\s+precision: 6$}, schema
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def mysql_datetime_precision(table_name, column_name)
|
84
|
+
results = ActiveRecord::Base.connection.exec_query("SELECT column_name, datetime_precision FROM information_schema.columns WHERE table_name ='#{table_name}'")
|
85
|
+
result = results.find do |result_hash|
|
86
|
+
result_hash["column_name"] == column_name
|
87
|
+
end
|
88
|
+
result && result["datetime_precision"]
|
89
|
+
end
|
90
|
+
|
91
|
+
def activerecord_column_option(tablename, column_name, option)
|
92
|
+
result = ActiveRecord::Base.connection.columns(tablename).find do |column|
|
93
|
+
column.name == column_name
|
94
|
+
end
|
95
|
+
result && result.send(option)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'activerecord-mysql-awesome'
|
5
|
+
require 'config'
|
6
|
+
|
7
|
+
require 'active_support/testing/autorun'
|
8
|
+
require 'stringio'
|
9
|
+
|
10
|
+
require 'active_record'
|
11
|
+
require 'cases/test_case'
|
12
|
+
require 'active_support/dependencies'
|
13
|
+
require 'active_support/logger'
|
14
|
+
require 'active_support/core_ext/string/strip'
|
15
|
+
|
16
|
+
require 'support/config'
|
17
|
+
require 'support/connection'
|
18
|
+
|
19
|
+
# TODO: Move all these random hacks into the ARTest namespace and into the support/ dir
|
20
|
+
|
21
|
+
Thread.abort_on_exception = true
|
22
|
+
|
23
|
+
# Show backtraces for deprecated behavior for quicker cleanup.
|
24
|
+
ActiveSupport::Deprecation.debug = true
|
25
|
+
|
26
|
+
# Enable raise errors in after_commit and after_rollback.
|
27
|
+
ActiveRecord::Base.tap do |klass|
|
28
|
+
klass.raise_in_transactional_callbacks = true if klass.respond_to?(:raise_in_transactional_callbacks=)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Connect to the database
|
32
|
+
ARTest.connect
|
33
|
+
|
34
|
+
def current_adapter?(*types)
|
35
|
+
types.any? do |type|
|
36
|
+
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
|
37
|
+
ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters.const_get(type))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def mysql_56?
|
42
|
+
current_adapter?(:Mysql2Adapter) &&
|
43
|
+
ActiveRecord::Base.connection.send(:version).join(".") >= "5.6.0"
|
44
|
+
end
|
45
|
+
|
46
|
+
# FIXME: we have tests that depend on run order, we should fix that and
|
47
|
+
# remove this method call.
|
48
|
+
require 'active_support/test_case'
|
49
|
+
ActiveSupport::TestCase.tap do |klass|
|
50
|
+
klass.test_order = :sorted if klass.respond_to?(:test_order=)
|
51
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'active_support/test_case'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# = Active Record Test Case
|
5
|
+
#
|
6
|
+
# Defines some test assertions to test against SQL queries.
|
7
|
+
class TestCase < ActiveSupport::TestCase #:nodoc:
|
8
|
+
def teardown
|
9
|
+
SQLCounter.clear_log
|
10
|
+
end
|
11
|
+
|
12
|
+
def assert_date_from_db(expected, actual, message = nil)
|
13
|
+
assert_equal expected.to_s, actual.to_s, message
|
14
|
+
end
|
15
|
+
|
16
|
+
def capture(stream)
|
17
|
+
stream = stream.to_s
|
18
|
+
captured_stream = Tempfile.new(stream)
|
19
|
+
stream_io = eval("$#{stream}")
|
20
|
+
origin_stream = stream_io.dup
|
21
|
+
stream_io.reopen(captured_stream)
|
22
|
+
|
23
|
+
yield
|
24
|
+
|
25
|
+
stream_io.rewind
|
26
|
+
return captured_stream.read
|
27
|
+
ensure
|
28
|
+
captured_stream.close
|
29
|
+
captured_stream.unlink
|
30
|
+
stream_io.reopen(origin_stream)
|
31
|
+
end
|
32
|
+
|
33
|
+
def capture_sql
|
34
|
+
SQLCounter.clear_log
|
35
|
+
yield
|
36
|
+
SQLCounter.log_all.dup
|
37
|
+
end
|
38
|
+
|
39
|
+
def assert_sql(*patterns_to_match)
|
40
|
+
capture_sql { yield }
|
41
|
+
ensure
|
42
|
+
failed_patterns = []
|
43
|
+
patterns_to_match.each do |pattern|
|
44
|
+
failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql }
|
45
|
+
end
|
46
|
+
assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def assert_queries(num = 1, options = {})
|
50
|
+
ignore_none = options.fetch(:ignore_none) { num == :any }
|
51
|
+
SQLCounter.clear_log
|
52
|
+
x = yield
|
53
|
+
the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
|
54
|
+
if num == :any
|
55
|
+
assert_operator the_log.size, :>=, 1, "1 or more queries expected, but none were executed."
|
56
|
+
else
|
57
|
+
mesg = "#{the_log.size} instead of #{num} queries were executed.#{the_log.size == 0 ? '' : "\nQueries:\n#{the_log.join("\n")}"}"
|
58
|
+
assert_equal num, the_log.size, mesg
|
59
|
+
end
|
60
|
+
x
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert_no_queries(options = {}, &block)
|
64
|
+
options.reverse_merge! ignore_none: true
|
65
|
+
assert_queries(0, options, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def assert_column(model, column_name, msg=nil)
|
69
|
+
assert has_column?(model, column_name), msg
|
70
|
+
end
|
71
|
+
|
72
|
+
def assert_no_column(model, column_name, msg=nil)
|
73
|
+
assert_not has_column?(model, column_name), msg
|
74
|
+
end
|
75
|
+
|
76
|
+
def has_column?(model, column_name)
|
77
|
+
model.reset_column_information
|
78
|
+
model.column_names.include?(column_name.to_s)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class SQLCounter
|
83
|
+
class << self
|
84
|
+
attr_accessor :ignored_sql, :log, :log_all
|
85
|
+
def clear_log; self.log = []; self.log_all = []; end
|
86
|
+
end
|
87
|
+
|
88
|
+
self.clear_log
|
89
|
+
|
90
|
+
self.ignored_sql = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /^SHOW max_identifier_length/, /^BEGIN/, /^COMMIT/]
|
91
|
+
|
92
|
+
# FIXME: this needs to be refactored so specific database can add their own
|
93
|
+
# ignored SQL, or better yet, use a different notification for the queries
|
94
|
+
# instead examining the SQL content.
|
95
|
+
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
|
96
|
+
mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /]
|
97
|
+
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
|
98
|
+
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im]
|
99
|
+
|
100
|
+
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
|
101
|
+
ignored_sql.concat db_ignored_sql
|
102
|
+
end
|
103
|
+
|
104
|
+
attr_reader :ignore
|
105
|
+
|
106
|
+
def initialize(ignore = Regexp.union(self.class.ignored_sql))
|
107
|
+
@ignore = ignore
|
108
|
+
end
|
109
|
+
|
110
|
+
def call(name, start, finish, message_id, values)
|
111
|
+
sql = values[:sql]
|
112
|
+
|
113
|
+
# FIXME: this seems bad. we should probably have a better way to indicate
|
114
|
+
# the query was cached
|
115
|
+
return if 'CACHE' == values[:name]
|
116
|
+
|
117
|
+
self.class.log_all << sql
|
118
|
+
self.class.log << sql unless ignore =~ sql
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new)
|
123
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
default_connection: 'mysql2'
|
2
|
+
|
3
|
+
connections:
|
4
|
+
mysql:
|
5
|
+
arunit:
|
6
|
+
username: rails
|
7
|
+
encoding: utf8
|
8
|
+
collation: utf8_unicode_ci
|
9
|
+
arunit2:
|
10
|
+
username: rails
|
11
|
+
encoding: utf8
|
12
|
+
|
13
|
+
mysql2:
|
14
|
+
arunit:
|
15
|
+
username: rails
|
16
|
+
encoding: utf8
|
17
|
+
collation: utf8_unicode_ci
|
18
|
+
arunit2:
|
19
|
+
username: rails
|
20
|
+
encoding: utf8
|
data/test/config.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module ARTest
|
7
|
+
class << self
|
8
|
+
def config
|
9
|
+
@config ||= read_config
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def config_file
|
15
|
+
Pathname.new(ENV['ARCONFIG'] || TEST_ROOT + '/config.yml')
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_config
|
19
|
+
unless config_file.exist?
|
20
|
+
FileUtils.cp TEST_ROOT + '/config.example.yml', config_file
|
21
|
+
end
|
22
|
+
|
23
|
+
erb = ERB.new(config_file.read)
|
24
|
+
expand_config(YAML.parse(erb.result(binding)).transform)
|
25
|
+
end
|
26
|
+
|
27
|
+
def expand_config(config)
|
28
|
+
config['connections'].each do |adapter, connection|
|
29
|
+
dbs = [['arunit', 'activerecord_unittest'], ['arunit2', 'activerecord_unittest2']]
|
30
|
+
dbs.each do |name, dbname|
|
31
|
+
unless connection[name].is_a?(Hash)
|
32
|
+
connection[name] = { 'database' => connection[name] }
|
33
|
+
end
|
34
|
+
|
35
|
+
connection[name]['database'] ||= dbname
|
36
|
+
connection[name]['adapter'] ||= adapter
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
config
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'active_support/logger'
|
2
|
+
|
3
|
+
module ARTest
|
4
|
+
def self.connection_name
|
5
|
+
ENV['ARCONN'] || config['default_connection']
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.connection_config
|
9
|
+
config['connections'][connection_name]
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.connect
|
13
|
+
puts "Using #{connection_name}"
|
14
|
+
ActiveRecord::Base.logger = ActiveSupport::Logger.new("debug.log", 0, 100 * 1024 * 1024)
|
15
|
+
ActiveRecord::Base.configurations = connection_config
|
16
|
+
ActiveRecord::Base.establish_connection :arunit
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module SchemaDumpingHelper
|
2
|
+
def dump_table_schema(table, connection = ActiveRecord::Base.connection)
|
3
|
+
old_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
4
|
+
ActiveRecord::SchemaDumper.ignore_tables = connection.tables - [table]
|
5
|
+
stream = StringIO.new
|
6
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
7
|
+
stream.string
|
8
|
+
ensure
|
9
|
+
ActiveRecord::SchemaDumper.ignore_tables = old_ignore_tables
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump_all_table_schema(ignore_tables)
|
13
|
+
old_ignore_tables, ActiveRecord::SchemaDumper.ignore_tables = ActiveRecord::SchemaDumper.ignore_tables, ignore_tables
|
14
|
+
stream = StringIO.new
|
15
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
16
|
+
stream.string
|
17
|
+
ensure
|
18
|
+
ActiveRecord::SchemaDumper.ignore_tables = old_ignore_tables
|
19
|
+
end
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-mysql-awesome
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryuta Kamizono
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-12-
|
11
|
+
date: 2014-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
83
|
+
description: Awesome patches backported for ActiveRecord MySQL adapters
|
84
84
|
email:
|
85
85
|
- kamipo@gmail.com
|
86
86
|
executables: []
|
@@ -88,6 +88,7 @@ extensions: []
|
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
90
|
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
91
92
|
- Gemfile
|
92
93
|
- LICENSE.txt
|
93
94
|
- README.md
|
@@ -101,6 +102,15 @@ files:
|
|
101
102
|
- lib/activerecord/mysql/awesome/base.rb
|
102
103
|
- lib/activerecord/mysql/awesome/railtie.rb
|
103
104
|
- lib/activerecord/mysql/awesome/version.rb
|
105
|
+
- test/.gitignore
|
106
|
+
- test/cases/datetime_test.rb
|
107
|
+
- test/cases/helper.rb
|
108
|
+
- test/cases/test_case.rb
|
109
|
+
- test/config.example.yml
|
110
|
+
- test/config.rb
|
111
|
+
- test/support/config.rb
|
112
|
+
- test/support/connection.rb
|
113
|
+
- test/support/schema_dumping_helper.rb
|
104
114
|
homepage: https://github.com/kamipo/activerecord-mysql-awesome
|
105
115
|
licenses:
|
106
116
|
- MIT
|
@@ -113,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
123
|
requirements:
|
114
124
|
- - ">="
|
115
125
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
126
|
+
version: 2.0.0
|
117
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
128
|
requirements:
|
119
129
|
- - ">="
|
@@ -124,5 +134,14 @@ rubyforge_project:
|
|
124
134
|
rubygems_version: 2.2.2
|
125
135
|
signing_key:
|
126
136
|
specification_version: 4
|
127
|
-
summary:
|
128
|
-
test_files:
|
137
|
+
summary: Awesome patches backported for ActiveRecord MySQL adapters
|
138
|
+
test_files:
|
139
|
+
- test/.gitignore
|
140
|
+
- test/cases/datetime_test.rb
|
141
|
+
- test/cases/helper.rb
|
142
|
+
- test/cases/test_case.rb
|
143
|
+
- test/config.example.yml
|
144
|
+
- test/config.rb
|
145
|
+
- test/support/config.rb
|
146
|
+
- test/support/connection.rb
|
147
|
+
- test/support/schema_dumping_helper.rb
|