methodmissing-mysqlplus_adapter 1.0.1 → 1.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.
data/README.textile CHANGED
@@ -82,7 +82,7 @@ Since ActiveRecord 2.1 preloading favors multiple efficient queries to cumbersom
82
82
 
83
83
  h2. Garbage Collection
84
84
 
85
- There's some experimental GC patches "available":http://github.com/oldmoe/mysqlplus/tree/with_async_validation - the mysql ruby gem forces GC every 20 queries, that's a guaranteed GC cycle every 5th request for a request with a 4 query overhead.
85
+ There's some experimental GC patches "available":http://github.com/oldmoe/mysqlplus/tree/with_async_validation - the mysql ruby gem forces GC every 20 queries, that's a guaranteed GC cycle every 5th request for a request with a 4 query overhead.This adapter will automatically detect the presence of those patches and disable the forced GC runs.
86
86
 
87
87
  h2. Stability
88
88
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 1
2
+ :patch: 2
3
3
  :major: 1
4
4
  :minor: 0
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  begin
13
13
  require_library_or_gem('fastthread')
14
- rescue => LoadError
14
+ rescue LoadError
15
15
  $stderr.puts "'gem install fastthread' for better performance"
16
16
  end
17
17
 
@@ -36,7 +36,7 @@ module ActiveRecord
36
36
  if skip_logging
37
37
  @connection.c_async_query( sql )
38
38
  else
39
- log("(Socket #{socket.to_s}) #{sql}",name) do
39
+ log("(Socket #{socket.to_s}, Thread #{Thread.current.object_id.to_s}) #{sql}",name) do
40
40
  @connection.c_async_query( sql )
41
41
  end
42
42
  end
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  def install!
8
8
  ActiveRecord::Base.send :extend, SingletonMethods
9
9
  ar_eigenclass::VALID_FIND_OPTIONS << :defer
10
- alias_deferred :find, :find_by_sql, :preload_associations
10
+ alias_deferred :find, :find_by_sql, :preload_associations, :find_every
11
11
  end
12
12
 
13
13
  private
@@ -35,13 +35,27 @@ module ActiveRecord
35
35
  # connection.
36
36
  #
37
37
  # ....
38
- # Record.find(:first, :include => [:other. :another], :defer => true)
38
+ # Record.find(:first, :include => [:other, :another], :defer => true)
39
39
  # ....
40
40
  #
41
41
  def preload_associations_with_defer(records, associations, preload_options={})
42
- if preload_options.key?(:defer)
43
- ActiveRecord::Deferrable::Result.new do
44
- preload_associations_without_defer(records, associations, preload_options)
42
+ if preload_deferred?( associations )
43
+ associations.delete(:defer)
44
+ records = [records].flatten.compact.uniq
45
+ return if records.empty?
46
+ case associations
47
+ when Array then associations.each {|association| ActiveRecord::Deferrable::Result.new{ preload_associations_without_defer(records, association, preload_options) } }
48
+ when Symbol, String then ActiveRecord::Deferrable::Result.new{ preload_one_association(records, associations.to_sym, preload_options) }
49
+ when Hash then
50
+ associations.each do |parent, child|
51
+ raise "parent must be an association name" unless parent.is_a?(String) || parent.is_a?(Symbol)
52
+ preload_associations(records, parent, preload_options)
53
+ reflection = reflections[parent]
54
+ parents = records.map {|record| record.send(reflection.name)}.flatten.compact
55
+ unless parents.empty?
56
+ parents.first.class.preload_associations(parents, child)
57
+ end
58
+ end
45
59
  end
46
60
  else
47
61
  preload_associations_without_defer(records, associations, preload_options)
@@ -91,6 +105,37 @@ module ActiveRecord
91
105
  with_scope( { :find => { :defer => true } }, :merge, &block )
92
106
  end
93
107
 
108
+ def find_every_with_defer(options) #:nodoc:
109
+ include_associations = merge_includes(scope(:find, :include), options[:include])
110
+ if include_associations.any? && references_eager_loaded_tables?(options)
111
+ records = find_with_associations(options)
112
+ else
113
+ records = find_by_sql(construct_finder_sql(options))
114
+ if include_associations.any?
115
+ preload_associations(records, preload_deferred_includes( include_associations, options ))
116
+ end
117
+ end
118
+
119
+ records.each { |record| record.readonly! } if options[:readonly]
120
+
121
+ records
122
+ end
123
+
124
+ def preload_deferred_includes( include_associations, options ) #:nodoc:
125
+ options[:defer] ? (Array(include_associations) << :defer) : include_associations
126
+ end
127
+
128
+ def preload_deferred?( associations ) #:nodoc:
129
+ begin
130
+ associations.respond_to?(:include?) && associations.include?(:defer)
131
+ rescue TypeError
132
+ #failing test cases :
133
+ # * test_eager_with_valid_association_as_string_not_symbol(EagerAssociationTest) &&
134
+ # * test_eager_with_invalid_association_reference(EagerAssociationTest)
135
+ false
136
+ end
137
+ end
138
+
94
139
  end
95
140
 
96
141
  end
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # back in on completion.
16
16
  #
17
17
  def defer!( deferrable )
18
- @result = Thread.new( deferrable ) do |deferrable|
18
+ @result = ::Thread.new( deferrable ) do |deferrable|
19
19
  begin
20
20
  deferrable.call
21
21
  rescue => exception
@@ -38,7 +38,7 @@ module ActiveRecord
38
38
  # Re-raise any Exceptions from the background Thread.
39
39
  #
40
40
  def validate!
41
- raise @_result if @_result.is_a?( Exception )
41
+ raise @_result if @_result.is_a?( ::Exception )
42
42
  end
43
43
 
44
44
  end
@@ -0,0 +1,32 @@
1
+ require "#{File.dirname(__FILE__)}/../helper"
2
+
3
+ Mysqlplus::Test.prepare!
4
+
5
+ class MacroTest < ActiveSupport::TestCase
6
+
7
+ def teardown
8
+ ActiveRecord::Base.clear_all_connections!
9
+ ActiveRecord::Base.establish_connection( Mysqlplus::Test::CONNECTION_SPEC )
10
+ super
11
+ end
12
+
13
+ def test_should_be_able_to_find_records_in_a_background_thread
14
+ ActiveRecord::Base.connection_pool.expects(:release_connection).twice
15
+ assert_equal MysqlUser.find(:first, :defer => true), MysqlUser.find(:first)
16
+ assert_instance_of MysqlUser, MysqlUser.find(:first, :defer => true)
17
+ end
18
+
19
+ def test_should_be_able_to_find_records_by_sql_background_thread
20
+ ActiveRecord::Base.connection_pool.expects(:release_connection).once
21
+ assert_equal MysqlUser.find_by_sql("SELECT * FROM mysql.user WHERE User = 'root'", true), MysqlUser.find(:all, :conditions => ['user.User = ?', 'root'])
22
+ end
23
+
24
+ def test_should_be_able_to_preload_related_records_on_multiple_connections
25
+ ActiveRecord::Base.connection_pool.expects(:release_connection).twice
26
+ assert_instance_of MysqlUser, MysqlUser.find( :first, :defer => true, :include => :mysql_user_info)
27
+ sleep(0.5)
28
+ end
29
+
30
+ end
31
+
32
+ Thread.list.each{|t| t.join unless t == Thread.main }
data/test/helper.rb CHANGED
@@ -1,11 +1,20 @@
1
1
  require 'rubygems'
2
+ require 'mocha'
2
3
  require 'active_support'
4
+ require 'active_support/test_case'
3
5
  require 'activerecord'
6
+
4
7
  ActiveRecord.load_all!
5
8
 
6
9
  module Mysqlplus
7
10
  class Test
8
11
 
12
+ CONNECTION_SPEC = { :adapter => 'mysqlplus',
13
+ :username => 'root',
14
+ :database => 'mysql',
15
+ :pool => 5,
16
+ :warmup => true }
17
+
9
18
  MODELS_DIR = "#{File.dirname(__FILE__)}/models".freeze
10
19
 
11
20
  class << self
@@ -32,17 +41,13 @@ module Mysqlplus
32
41
  end
33
42
 
34
43
  def test_files
35
- glob( "#{File.dirname(__FILE__)}/*_test.rb" )
44
+ glob( "#{File.dirname(__FILE__)}/**/*_test.rb" )
36
45
  end
37
46
 
38
47
  private
39
48
 
40
49
  def connect!
41
- ::ActiveRecord::Base.establish_connection( :adapter => 'mysqlplus',
42
- :username => 'root',
43
- :database => 'mysql',
44
- :pool => 5,
45
- :warmup => true )
50
+ ::ActiveRecord::Base.establish_connection( CONNECTION_SPEC )
46
51
  end
47
52
 
48
53
  def require_models
@@ -1,3 +1,6 @@
1
1
  class MysqlUser < ActiveRecord::Base
2
2
  set_table_name 'user'
3
+ set_primary_key :User
4
+
5
+ has_one :mysql_user_info, :class_name => 'MysqlUserInfo', :foreign_key => :User
3
6
  end
@@ -0,0 +1,6 @@
1
+ class MysqlUserInfo < ActiveRecord::Base
2
+ set_table_name 'user_info'
3
+ set_primary_key :User
4
+
5
+ belongs_to :mysql_user, :class_name => 'MysqlUser', :foreign_key => :User
6
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: methodmissing-mysqlplus_adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Lourens Naud\xC3\xA9"
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-07 00:00:00 -08:00
12
+ date: 2009-02-09 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -35,10 +35,12 @@ files:
35
35
  - test/connections
36
36
  - test/connections/mysqlplus
37
37
  - test/connections/mysqlplus/connection.rb
38
- - test/deferrable_test.rb
38
+ - test/deferrable
39
+ - test/deferrable/macro_test.rb
39
40
  - test/helper.rb
40
41
  - test/models
41
42
  - test/models/mysql_user.rb
43
+ - test/models/mysql_user_info.rb
42
44
  has_rdoc: true
43
45
  homepage: http://github.com/methodmissing/mysqplus_adapter
44
46
  post_install_message:
@@ -1,15 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/helper"
2
- Mysqlplus::Test.prepare!
3
-
4
- class DeferrableTest < Test::Unit::TestCase
5
-
6
- def test_should_be_able_to_find_records_in_a_background_thread
7
- assert_equal MysqlUser.find(:first, :defer => true), MysqlUser.find(:first)
8
- assert_instance_of MysqlUser, MysqlUser.find(:first, :defer => true)
9
- end
10
-
11
- def test_should_be_able_to_find_records_by_sql_background_thread
12
- assert_equal MysqlUser.find_by_sql("SELECT * FROM mysql.user WHERE User = 'root'", true), MysqlUser.find(:all, :conditions => ['user.User = ?', 'root'])
13
- end
14
-
15
- end