em-sequel-async 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/em-sequel-async.gemspec +67 -0
- data/lib/em-sequel-async/mysql.rb +145 -0
- data/lib/em-sequel-async/sequel_extensions.rb +230 -0
- data/lib/em-sequel-async.rb +6 -0
- data/lib/sequel/extensions/em_sequel_async.rb +3 -0
- data/test/helper.rb +40 -0
- data/test/test_em-sequel-async.rb +209 -0
- metadata +156 -0
data/.document
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Scott Tadman
|
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,19 @@
|
|
1
|
+
= em-sequel-async
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Contributing to em-sequel-async
|
6
|
+
|
7
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
8
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
9
|
+
* Fork the project.
|
10
|
+
* Start a feature/bugfix branch.
|
11
|
+
* Commit and push until you are happy with your contribution.
|
12
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
13
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2012 Scott Tadman. See LICENSE.txt for
|
18
|
+
further details.
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Bundler.setup(:default, :development)
|
8
|
+
rescue Bundler::BundlerError => e
|
9
|
+
$stderr.puts e.message
|
10
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
11
|
+
exit e.status_code
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rake'
|
15
|
+
require 'jeweler'
|
16
|
+
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
gem.name = "em-sequel-async"
|
19
|
+
gem.homepage = "http://github.com/tadman/em-sequel-async"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{Asynchronous Helper Methods for Sequel}
|
22
|
+
gem.description = %Q{Implements a number of asynchronous helper methods for Sequel}
|
23
|
+
gem.email = "scott@twg.ca"
|
24
|
+
gem.authors = [ "Scott Tadman" ]
|
25
|
+
end
|
26
|
+
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
|
+
|
29
|
+
require 'rake/testtask'
|
30
|
+
|
31
|
+
Rake::TestTask.new(:test) do |test|
|
32
|
+
test.libs << 'lib' << 'test'
|
33
|
+
test.pattern = 'test/**/test_*.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "em-sequel-async"
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Scott Tadman"]
|
12
|
+
s.date = "2012-11-14"
|
13
|
+
s.description = "Implements a number of asynchronous helper methods for Sequel"
|
14
|
+
s.email = "scott@twg.ca"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
"Gemfile",
|
22
|
+
"LICENSE.txt",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"em-sequel-async.gemspec",
|
27
|
+
"lib/em-sequel-async.rb",
|
28
|
+
"lib/em-sequel-async/mysql.rb",
|
29
|
+
"lib/em-sequel-async/sequel_extensions.rb",
|
30
|
+
"lib/sequel/extensions/em_sequel_async.rb",
|
31
|
+
"test/helper.rb",
|
32
|
+
"test/test_em-sequel-async.rb"
|
33
|
+
]
|
34
|
+
s.homepage = "http://github.com/tadman/em-sequel-async"
|
35
|
+
s.licenses = ["MIT"]
|
36
|
+
s.require_paths = ["lib"]
|
37
|
+
s.rubygems_version = "1.8.24"
|
38
|
+
s.summary = "Asynchronous Helper Methods for Sequel"
|
39
|
+
|
40
|
+
if s.respond_to? :specification_version then
|
41
|
+
s.specification_version = 3
|
42
|
+
|
43
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
44
|
+
s.add_runtime_dependency(%q<sequel>, [">= 0"])
|
45
|
+
s.add_runtime_dependency(%q<mysql2>, [">= 0"])
|
46
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<await>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<sequel>, [">= 0"])
|
52
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
53
|
+
s.add_dependency(%q<eventmachine>, [">= 0"])
|
54
|
+
s.add_dependency(%q<await>, [">= 0"])
|
55
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
56
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
57
|
+
end
|
58
|
+
else
|
59
|
+
s.add_dependency(%q<sequel>, [">= 0"])
|
60
|
+
s.add_dependency(%q<mysql2>, [">= 0"])
|
61
|
+
s.add_dependency(%q<eventmachine>, [">= 0"])
|
62
|
+
s.add_dependency(%q<await>, [">= 0"])
|
63
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
64
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -0,0 +1,145 @@
|
|
1
|
+
class EmSequelAsync::Mysql
|
2
|
+
class ClientPool
|
3
|
+
# == Class Methods ======================================================
|
4
|
+
|
5
|
+
def self.size=(value)
|
6
|
+
@size = value.to_i
|
7
|
+
@size = 1 if (@size <= 0)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.size
|
11
|
+
@size ||= 4
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.trace?
|
15
|
+
!!@trace
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.trace
|
19
|
+
@trace
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.trace=(value)
|
23
|
+
@trace = value
|
24
|
+
end
|
25
|
+
|
26
|
+
# == Instance Methods ===================================================
|
27
|
+
|
28
|
+
def initialize(db)
|
29
|
+
@options = {
|
30
|
+
:symbolize_keys => true
|
31
|
+
}
|
32
|
+
|
33
|
+
db.opts.each do |key, value|
|
34
|
+
case (key)
|
35
|
+
when :database, :username, :password, :host, :port, :socket, :encoding, :charset, :compress, :timeout
|
36
|
+
@options[key] = value
|
37
|
+
when :user
|
38
|
+
@options[:username] = value
|
39
|
+
when :loggers
|
40
|
+
if (value and !value.empty?)
|
41
|
+
@options[:logging] = true
|
42
|
+
@options[:logger] = value.first
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@query_queue = [ ]
|
48
|
+
|
49
|
+
@connections = { }
|
50
|
+
@connection_pool = [ ]
|
51
|
+
@connections_active = { }
|
52
|
+
|
53
|
+
@connection_limit = @options[:connections] || self.class.size
|
54
|
+
|
55
|
+
if (EventMachine.reactor_running? and self.class.trace?)
|
56
|
+
EventMachine::PeriodicTimer.new(1) do
|
57
|
+
dump_file = "#{self.class.trace}.#{@options[:database]}"
|
58
|
+
|
59
|
+
File.open(dump_file, 'w') do |f|
|
60
|
+
f.puts @options[:database]
|
61
|
+
|
62
|
+
@connections.each do |c, x|
|
63
|
+
f.puts "\t#{c.inspect} -> #{x.inspect}"
|
64
|
+
end
|
65
|
+
@connection_pool.each do |c|
|
66
|
+
f.puts "\t#{c.inspect} (pool)"
|
67
|
+
end
|
68
|
+
|
69
|
+
@query_queue.each do |query, callback|
|
70
|
+
f.puts "\t#{query}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def logger
|
78
|
+
@options[:logger]
|
79
|
+
end
|
80
|
+
|
81
|
+
def log(level, *args)
|
82
|
+
@options[:logger] and @options[:logger].send(level, *args)
|
83
|
+
end
|
84
|
+
|
85
|
+
def add(connection)
|
86
|
+
@connections[connection] = nil
|
87
|
+
|
88
|
+
if (@query_queue.empty?)
|
89
|
+
@connections_active.delete(connection)
|
90
|
+
@connection_pool << connection
|
91
|
+
else
|
92
|
+
self.delegate_query(connection, *@query_queue.pop)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def delegate_query(connection, query, callback)
|
97
|
+
@connections[connection] = [ query, callback ]
|
98
|
+
|
99
|
+
start = Time.now
|
100
|
+
deferrable = connection.query(query)
|
101
|
+
|
102
|
+
deferrable.callback do |result|
|
103
|
+
log(:debug, "(%.6fs) [OK] %s" % [ Time.now - start, query ])
|
104
|
+
|
105
|
+
callback.call(result, (Time.now - start).to_f, connection)
|
106
|
+
|
107
|
+
self.add(connection)
|
108
|
+
end
|
109
|
+
deferrable.errback do |err|
|
110
|
+
log(:error, "(%.6fs) [ERR] %s (%s: %s)" % [ Time.now - start, query, err.class, err ])
|
111
|
+
log(:error, err.backtrace)
|
112
|
+
|
113
|
+
callback.call(false, (Time.now - start).to_f, connection, err)
|
114
|
+
|
115
|
+
self.add(connection)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def pool_connection
|
120
|
+
connection = @connection_pool.pop
|
121
|
+
|
122
|
+
if (!connection and @connections.length < @connection_limit)
|
123
|
+
connection = Mysql2::EM::Client.new(@options)
|
124
|
+
end
|
125
|
+
|
126
|
+
@connections_active[connection] = true
|
127
|
+
|
128
|
+
connection
|
129
|
+
end
|
130
|
+
|
131
|
+
def query(query, callback = nil, &block)
|
132
|
+
callback ||= block
|
133
|
+
|
134
|
+
if (connection = self.pool_connection)
|
135
|
+
self.delegate_query(connection, query, callback)
|
136
|
+
|
137
|
+
:executing
|
138
|
+
else
|
139
|
+
@query_queue << [ query, callback ]
|
140
|
+
|
141
|
+
:queued
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
require 'sequel/adapters/mysql2'
|
3
|
+
|
4
|
+
module EmSequelAsync::SequelExtensions
|
5
|
+
def self.install!
|
6
|
+
Mysql2::Result.send(:extend, Result::ClassMethods)
|
7
|
+
Mysql2::Result.send(:include, Result::InstanceMethods)
|
8
|
+
|
9
|
+
Sequel::Mysql2::Database.send(:extend, Database::ClassMethods)
|
10
|
+
Sequel::Mysql2::Database.send(:include, Database::InstanceMethods)
|
11
|
+
|
12
|
+
Sequel::Dataset.send(:extend, Dataset::ClassMethods)
|
13
|
+
Sequel::Dataset.send(:include, Dataset::InstanceMethods)
|
14
|
+
|
15
|
+
Sequel::Model.send(:extend, Model::ClassMethods)
|
16
|
+
Sequel::Model.send(:include, Model::InstanceMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
module Result
|
20
|
+
module ClassMethods
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
def empty?
|
25
|
+
!self.any?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module Database
|
31
|
+
module ClassMethods
|
32
|
+
end
|
33
|
+
|
34
|
+
module InstanceMethods
|
35
|
+
def async
|
36
|
+
@async_db ||= EmSequelAsync::Mysql::ClientPool.new(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# NOTE: This interface is borrowed from the deprecated tmm1/em-mysql gem.
|
42
|
+
|
43
|
+
module Dataset
|
44
|
+
module ClassMethods
|
45
|
+
# None defined.
|
46
|
+
end
|
47
|
+
|
48
|
+
module InstanceMethods
|
49
|
+
STOCK_COUNT_OPTS = {
|
50
|
+
:select => [ Sequel::LiteralString.new("COUNT(*)").freeze ],
|
51
|
+
:order => nil
|
52
|
+
}.freeze
|
53
|
+
|
54
|
+
def async_insert(*args)
|
55
|
+
self.db.async.query(insert_sql(*args)) do |result, time, client, err|
|
56
|
+
yield(err ? nil : client.last_id) if (block_given?)
|
57
|
+
end
|
58
|
+
|
59
|
+
return
|
60
|
+
end
|
61
|
+
|
62
|
+
def async_query_return_affected_rows(query)
|
63
|
+
self.db.async.query(query) do |result, time, client, err|
|
64
|
+
yield(err ? nil : client.affected_rows) if (block_given?)
|
65
|
+
end
|
66
|
+
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
def async_insert_ignore(*args, &block)
|
71
|
+
self.async_query_return_affected_rows(insert_ignore.insert_sql(*args), &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def async_update(*args, &block)
|
75
|
+
self.async_query_return_affected_rows(update_sql(*args), &block)
|
76
|
+
end
|
77
|
+
|
78
|
+
def async_delete(&block)
|
79
|
+
self.async_query_return_affected_rows(delete_sql, &block)
|
80
|
+
end
|
81
|
+
|
82
|
+
def async_multi_insert(*args, &block)
|
83
|
+
self.async_query_return_affected_rows(multi_insert_sql(*args).first, &block)
|
84
|
+
end
|
85
|
+
|
86
|
+
def async_multi_insert_ignore(*args, &block)
|
87
|
+
self.async_query_return_affected_rows(insert_ignore.multi_insert_sql(*args).first, &block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def async_fetch_rows(sql, iter = :each)
|
91
|
+
self.db.async.query(sql) do |result, time, client, err|
|
92
|
+
case (result)
|
93
|
+
when Mysql2::Result
|
94
|
+
case (iter)
|
95
|
+
when :each
|
96
|
+
result.each do |row|
|
97
|
+
yield(row)
|
98
|
+
end
|
99
|
+
else
|
100
|
+
yield(result.to_a)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
return
|
106
|
+
end
|
107
|
+
|
108
|
+
def async_first(sql)
|
109
|
+
async_fetch_rows(sql, :each) do |result, time, client, err|
|
110
|
+
yield(rows && rows[0])
|
111
|
+
|
112
|
+
return
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def async_each
|
117
|
+
async_fetch_rows(select_sql, :each) do |row|
|
118
|
+
if (row_proc = @row_proc)
|
119
|
+
yield(row_proc.call(row))
|
120
|
+
else
|
121
|
+
yield(row)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
return
|
126
|
+
end
|
127
|
+
|
128
|
+
def async_all
|
129
|
+
async_fetch_rows(sql, :all) do |rows|
|
130
|
+
if (row_proc = @row_proc)
|
131
|
+
yield(rows.map { |row| row_proc.call(row) })
|
132
|
+
else
|
133
|
+
yield(rows)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
return
|
138
|
+
end
|
139
|
+
|
140
|
+
def async_count(&callback)
|
141
|
+
if (options_overlap(Sequel::Dataset::COUNT_FROM_SELF_OPTS))
|
142
|
+
from_self.async_count(&callback)
|
143
|
+
else
|
144
|
+
clone(STOCK_COUNT_OPTS).async_each do |row|
|
145
|
+
callback.call(
|
146
|
+
case (row)
|
147
|
+
when Hash
|
148
|
+
row.values.first.to_i
|
149
|
+
else
|
150
|
+
row.values.values.first.to_i
|
151
|
+
end
|
152
|
+
)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
return
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
module Model
|
162
|
+
module ClassMethods
|
163
|
+
[ :async_insert,
|
164
|
+
:async_insert_ignore,
|
165
|
+
:async_multi_insert,
|
166
|
+
:async_multi_insert_ignore,
|
167
|
+
:async_each,
|
168
|
+
:async_all,
|
169
|
+
:async_update,
|
170
|
+
:async_count,
|
171
|
+
:async_delete
|
172
|
+
].each do |method|
|
173
|
+
eval %Q[
|
174
|
+
def #{method}(*args, &callback)
|
175
|
+
dataset.#{method}(*args, &callback)
|
176
|
+
end
|
177
|
+
]
|
178
|
+
end
|
179
|
+
|
180
|
+
# This differs from async_multi_insert_ignore in that it takes an
|
181
|
+
# array of hashes rather than a series of arrays. The columns are
|
182
|
+
# automatically determined based on the keys of first hash provided.
|
183
|
+
def async_multi_insert_ignore_hash(hashes)
|
184
|
+
if (hashes.empty?)
|
185
|
+
yield if (block_given?)
|
186
|
+
|
187
|
+
return
|
188
|
+
end
|
189
|
+
|
190
|
+
columns = hashes.first.keys
|
191
|
+
|
192
|
+
insertions = hashes.collect do |row|
|
193
|
+
columns.collect { |c| row[c] }
|
194
|
+
end
|
195
|
+
|
196
|
+
async_multi_insert_ignore(columns, insertions) do |n|
|
197
|
+
yield(n) if (block_given?)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Async version of Model#[]
|
202
|
+
def async_lookup(args)
|
203
|
+
unless (Hash === args)
|
204
|
+
args = primary_key_hash(args)
|
205
|
+
end
|
206
|
+
|
207
|
+
dataset.where(args).limit(1).async_all do |rows|
|
208
|
+
yield(rows.any? ? rows.first : nil)
|
209
|
+
end
|
210
|
+
|
211
|
+
return
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
module InstanceMethods
|
216
|
+
def async_update(*args, &callback)
|
217
|
+
this.async_update(*args, &callback)
|
218
|
+
set(*args)
|
219
|
+
|
220
|
+
self
|
221
|
+
end
|
222
|
+
|
223
|
+
def async_delete(&callback)
|
224
|
+
this.async_delete(&callback)
|
225
|
+
|
226
|
+
return
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'test/unit'
|
14
|
+
|
15
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
17
|
+
|
18
|
+
require 'em-sequel-async'
|
19
|
+
|
20
|
+
require 'mysql2'
|
21
|
+
require 'sequel'
|
22
|
+
require 'eventmachine'
|
23
|
+
|
24
|
+
Sequel.extension(:em_sequel_async)
|
25
|
+
|
26
|
+
require 'await'
|
27
|
+
|
28
|
+
class Test::Unit::TestCase
|
29
|
+
include Await
|
30
|
+
|
31
|
+
def em
|
32
|
+
EventMachine.run do
|
33
|
+
Fiber.new do
|
34
|
+
yield
|
35
|
+
|
36
|
+
EventMachine.stop_event_loop
|
37
|
+
end.resume
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require File.expand_path('helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
DB_CONFIG = {
|
4
|
+
:default => {
|
5
|
+
:adapter => 'mysql2',
|
6
|
+
:username => 'test',
|
7
|
+
:password => 'JAQv0RM6xFVb8yz06GiQ7mOq',
|
8
|
+
:database => 'emsa_test_default'
|
9
|
+
},
|
10
|
+
:a => {
|
11
|
+
:adapter => 'mysql2',
|
12
|
+
:username => 'test',
|
13
|
+
:password => 'JAQv0RM6xFVb8yz06GiQ7mOq',
|
14
|
+
:database => 'emsa_test_a'
|
15
|
+
},
|
16
|
+
:b => {
|
17
|
+
:adapter => 'mysql2',
|
18
|
+
:username => 'test',
|
19
|
+
:password => 'JAQv0RM6xFVb8yz06GiQ7mOq',
|
20
|
+
:database => 'emsa_test_b'
|
21
|
+
}
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
DB = Hash[DB_CONFIG.collect { |db, config| [ db, Sequel.connect(config) ] }]
|
25
|
+
|
26
|
+
{
|
27
|
+
:default => %w[ db_default_a_models db_default_b_models ],
|
28
|
+
:a => %w[ example_as ],
|
29
|
+
:b => %w[ example_bs ]
|
30
|
+
}.each do |db, tables|
|
31
|
+
create_config = DB_CONFIG[db].dup
|
32
|
+
db_name = create_config.delete(:database)
|
33
|
+
|
34
|
+
handle = Mysql2::Client.new(create_config)
|
35
|
+
db_name = DB_CONFIG[db][:database]
|
36
|
+
|
37
|
+
handle.query("DROP DATABASE IF EXISTS `#{db_name}`")
|
38
|
+
handle.query("CREATE DATABASE `#{db_name}`")
|
39
|
+
handle.query("USE `#{db_name}`")
|
40
|
+
|
41
|
+
tables.each do |table|
|
42
|
+
handle.query("CREATE TABLE `#{table}` (id INT AUTO_INCREMENT PRIMARY KEY, data VARCHAR(255))")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class DbDefaultAModel < Sequel::Model
|
47
|
+
end
|
48
|
+
|
49
|
+
class DbDefaultBModel < Sequel::Model
|
50
|
+
end
|
51
|
+
|
52
|
+
class DbAModel < Sequel::Model(DB[:a][:example_as])
|
53
|
+
end
|
54
|
+
|
55
|
+
class DbBModel < Sequel::Model(DB[:b][:example_bs])
|
56
|
+
end
|
57
|
+
|
58
|
+
class TestEmSequelAsync < Test::Unit::TestCase
|
59
|
+
def test_module
|
60
|
+
assert EmSequelAsync
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_async_db_handle
|
64
|
+
assert DbDefaultAModel.db
|
65
|
+
assert DbDefaultAModel.db.async
|
66
|
+
|
67
|
+
assert_equal DbDefaultAModel.db, DbDefaultBModel.db
|
68
|
+
assert_not_equal DbAModel.db, DbDefaultBModel.db
|
69
|
+
assert_not_equal DbAModel.db, DbBModel.db
|
70
|
+
|
71
|
+
assert_equal DbDefaultAModel.db.async, DbDefaultBModel.db.async
|
72
|
+
assert_not_equal DbAModel.db.async, DbDefaultBModel.db.async
|
73
|
+
assert_not_equal DbAModel.db.async, DbBModel.db.async
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_model_async_insert
|
77
|
+
inserted_id = nil
|
78
|
+
|
79
|
+
em do
|
80
|
+
await do
|
81
|
+
DbDefaultAModel.async_insert(
|
82
|
+
:data => 'Test Name',
|
83
|
+
&defer do |id|
|
84
|
+
inserted_id = id
|
85
|
+
end
|
86
|
+
)
|
87
|
+
|
88
|
+
assert_equal nil, inserted_id
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
assert inserted_id
|
93
|
+
assert inserted_id > 0
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_model_async_insert_ignore
|
97
|
+
inserted_id = nil
|
98
|
+
inserted_count = nil
|
99
|
+
found_count = nil
|
100
|
+
deleted_count = nil
|
101
|
+
|
102
|
+
em do
|
103
|
+
await do
|
104
|
+
DbDefaultAModel.async_insert(
|
105
|
+
:data => 'Test Name',
|
106
|
+
&defer do |id|
|
107
|
+
inserted_id = id
|
108
|
+
|
109
|
+
assert inserted_id > 0
|
110
|
+
|
111
|
+
DbDefaultAModel.where(:id => inserted_id).async_count(
|
112
|
+
&defer do |count|
|
113
|
+
found_count = count
|
114
|
+
end
|
115
|
+
)
|
116
|
+
|
117
|
+
DbDefaultAModel.async_insert_ignore(
|
118
|
+
:id => inserted_id,
|
119
|
+
:data => 'Duplicate',
|
120
|
+
&defer do |count|
|
121
|
+
inserted_count = count
|
122
|
+
end
|
123
|
+
)
|
124
|
+
end
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
assert_equal 0, inserted_count
|
129
|
+
assert_equal 1, found_count
|
130
|
+
end
|
131
|
+
|
132
|
+
em do
|
133
|
+
found_count = nil
|
134
|
+
delete_count = nil
|
135
|
+
inserted_count = nil
|
136
|
+
|
137
|
+
await do |a|
|
138
|
+
DbDefaultAModel.where(:id => inserted_id).async_delete(
|
139
|
+
&defer do |count|
|
140
|
+
deleted_count = count
|
141
|
+
|
142
|
+
assert_equal 1, deleted_count
|
143
|
+
|
144
|
+
DbDefaultAModel.where(:id => inserted_id).async_count(
|
145
|
+
&defer do |count|
|
146
|
+
found_count = count
|
147
|
+
|
148
|
+
DbDefaultAModel.async_insert_ignore(
|
149
|
+
:id => inserted_id,
|
150
|
+
:data => 'Duplicate',
|
151
|
+
&defer do |count|
|
152
|
+
inserted_count = count
|
153
|
+
end
|
154
|
+
)
|
155
|
+
end
|
156
|
+
)
|
157
|
+
end
|
158
|
+
)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
assert_equal 0, found_count
|
163
|
+
assert_equal 1, deleted_count
|
164
|
+
assert_equal 1, inserted_count
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_dataset_async_count
|
168
|
+
models_count = nil
|
169
|
+
|
170
|
+
em do
|
171
|
+
await do
|
172
|
+
DB[:default][:db_default_a_models].async_count(
|
173
|
+
&defer do |count|
|
174
|
+
models_count = count
|
175
|
+
end
|
176
|
+
)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
assert_equal DB[:default][:db_default_a_models].count, models_count
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_dataset_async_insert_duplicate
|
184
|
+
em do
|
185
|
+
inserted_id = nil
|
186
|
+
duplicate_id = false
|
187
|
+
|
188
|
+
await do
|
189
|
+
DbDefaultAModel.async_insert(
|
190
|
+
:data => 'Test Name',
|
191
|
+
&defer do |id|
|
192
|
+
inserted_id = id
|
193
|
+
|
194
|
+
DbDefaultAModel.async_insert(
|
195
|
+
:id => inserted_id,
|
196
|
+
:data => 'Duplicate',
|
197
|
+
&defer do |*f|
|
198
|
+
duplicate_id = f[0]
|
199
|
+
end
|
200
|
+
)
|
201
|
+
end
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
assert inserted_id
|
206
|
+
assert_equal nil, duplicate_id
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: em-sequel-async
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Scott Tadman
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mysql2
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: eventmachine
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: await
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: jeweler
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Implements a number of asynchronous helper methods for Sequel
|
111
|
+
email: scott@twg.ca
|
112
|
+
executables: []
|
113
|
+
extensions: []
|
114
|
+
extra_rdoc_files:
|
115
|
+
- LICENSE.txt
|
116
|
+
- README.rdoc
|
117
|
+
files:
|
118
|
+
- .document
|
119
|
+
- Gemfile
|
120
|
+
- LICENSE.txt
|
121
|
+
- README.rdoc
|
122
|
+
- Rakefile
|
123
|
+
- VERSION
|
124
|
+
- em-sequel-async.gemspec
|
125
|
+
- lib/em-sequel-async.rb
|
126
|
+
- lib/em-sequel-async/mysql.rb
|
127
|
+
- lib/em-sequel-async/sequel_extensions.rb
|
128
|
+
- lib/sequel/extensions/em_sequel_async.rb
|
129
|
+
- test/helper.rb
|
130
|
+
- test/test_em-sequel-async.rb
|
131
|
+
homepage: http://github.com/tadman/em-sequel-async
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 1.8.24
|
153
|
+
signing_key:
|
154
|
+
specification_version: 3
|
155
|
+
summary: Asynchronous Helper Methods for Sequel
|
156
|
+
test_files: []
|