upsert 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ shared_examples_for "supports multibyte" do
3
+ describe :multibyte do
4
+ it "works one-by-one" do
5
+ upsert = Upsert.new connection, :pets
6
+ assert_creates(Pet, [{:name => 'I♥NY', :gender => 'périferôl'}]) do
7
+ upsert.row({:name => 'I♥NY'}, {:gender => 'périferôl'})
8
+ end
9
+ end
10
+ it "works serially" do
11
+ upsert = Upsert.new connection, :pets
12
+ assert_creates(Pet, [{:name => 'I♥NY', :gender => 'jÚrgen'}]) do
13
+ upsert.row({:name => 'I♥NY'}, {:gender => 'périferôl'})
14
+ upsert.row({:name => 'I♥NY'}, {:gender => 'jÚrgen'})
15
+ end
16
+ end
17
+ it "works multi" do
18
+ assert_creates(Pet, [{:name => 'I♥NY', :gender => 'jÚrgen'}]) do
19
+ Upsert.new(connection, :pets).multi do |xxx|
20
+ xxx.row({:name => 'I♥NY'}, {:gender => 'périferôl'})
21
+ xxx.row({:name => 'I♥NY'}, {:gender => 'jÚrgen'})
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,72 @@
1
+ shared_examples_for 'can be speeded up with upserting' do
2
+ describe :speed do
3
+ describe 'compared to native ActiveRecord' do
4
+ it "is faster than new/set/save" do
5
+ assert_faster_than 'find + new/set/save', lotsa_records do |records|
6
+ records.each do |selector, document|
7
+ if pet = Pet.where(selector).first
8
+ pet.update_attributes document, :without_protection => true
9
+ else
10
+ pet = Pet.new
11
+ selector.each do |k, v|
12
+ pet.send "#{k}=", v
13
+ end
14
+ document.each do |k, v|
15
+ pet.send "#{k}=", v
16
+ end
17
+ pet.save!
18
+ end
19
+ end
20
+ end
21
+ end
22
+ it "is faster than find_or_create + update_attributes" do
23
+ assert_faster_than 'find_or_create + update_attributes', lotsa_records do |records|
24
+ dynamic_method = nil
25
+ records.each do |selector, document|
26
+ dynamic_method ||= "find_or_create_by_#{selector.keys.join('_or_')}"
27
+ pet = Pet.send(dynamic_method, *selector.values)
28
+ pet.update_attributes document, :without_protection => true
29
+ end
30
+ end
31
+ end
32
+ it "is faster than create + rescue/find/update" do
33
+ assert_faster_than 'create + rescue/find/update', lotsa_records do |records|
34
+ dynamic_method = nil
35
+ records.each do |selector, document|
36
+ dynamic_method ||= "find_or_create_by_#{selector.keys.join('_or_')}"
37
+ begin
38
+ Pet.create selector.merge(document), :without_protection => true
39
+ rescue
40
+ pet = Pet.send(dynamic_method, *selector.values)
41
+ pet.update_attributes document, :without_protection => true
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'compared to activerecord-import' do
49
+ it "is faster than faking upserts with activerecord-import" do
50
+ unless Pet.connection.respond_to?(:sql_for_on_duplicate_key_update)
51
+ flunk "#{Pet.connection} does not support activerecord-import's :on_duplicate_key_update"
52
+ end
53
+ assert_faster_than 'faking upserts with activerecord-import', lotsa_records do |records|
54
+ columns = nil
55
+ all_values = []
56
+ records.each do |selector, document|
57
+ columns ||= (selector.keys + document.keys).uniq
58
+ all_values << columns.map do |k|
59
+ if document.has_key?(k)
60
+ # prefer the document so that you can change rows
61
+ document[k]
62
+ else
63
+ selector[k]
64
+ end
65
+ end
66
+ end
67
+ Pet.import columns, all_values, :timestamps => false, :on_duplicate_key_update => columns
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,27 @@
1
+ shared_examples_for "doesn't mess with timezones" do
2
+ describe :timezones do
3
+ before do
4
+ @old_default_tz = ActiveRecord::Base.default_timezone
5
+ end
6
+ after do
7
+ ActiveRecord::Base.default_timezone = @old_default_tz
8
+ end
9
+
10
+ it "deals fine with UTC" do
11
+ ActiveRecord::Base.default_timezone = :utc
12
+ time = Time.now.utc
13
+ upsert = Upsert.new connection, :pets
14
+ assert_creates(Pet, [{:name => 'Jerry', :morning_walk_time => time}]) do
15
+ upsert.row({:name => 'Jerry'}, {:morning_walk_time => time})
16
+ end
17
+ end
18
+ it "won't mess with UTC" do
19
+ ActiveRecord::Base.default_timezone = :local
20
+ time = Time.now
21
+ upsert = Upsert.new connection, :pets
22
+ assert_creates(Pet, [{:name => 'Jerry', :morning_walk_time => time}]) do
23
+ upsert.row({:name => 'Jerry'}, {:morning_walk_time => time})
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ system %{ mysql -u root -ppassword -e "DROP DATABASE IF EXISTS test_upsert; CREATE DATABASE test_upsert CHARSET utf8" }
4
+ ActiveRecord::Base.establish_connection :adapter => 'mysql2', :username => 'root', :password => 'password', :database => 'test_upsert', :pool => 2
5
+
6
+ describe "using an ActiveRecord connection adapter" do
7
+ before do
8
+ ActiveRecord::Base.connection.drop_table(Pet.table_name) rescue nil
9
+ Pet.auto_upgrade!
10
+ @opened_connections = []
11
+ @connection = new_connection
12
+ end
13
+ after do
14
+ @opened_connections.each { |c| ActiveRecord::Base.connection_pool.checkin(c) }
15
+ end
16
+ def new_connection
17
+ c = ActiveRecord::Base.connection_pool.checkout
18
+ @opened_connections << c
19
+ c
20
+ end
21
+ def connection
22
+ @connection
23
+ end
24
+
25
+ it_also 'is a database with an upsert trick'
26
+
27
+ it_also 'is just as correct as other ways'
28
+
29
+ it_also 'can be speeded up with upserting'
30
+
31
+ it_also 'supports binary upserts'
32
+
33
+ it_also "supports multibyte"
34
+
35
+ it_also "doesn't mess with timezones"
36
+ end
data/test/test_mysql2.rb CHANGED
@@ -6,7 +6,7 @@ ActiveRecord::Base.establish_connection :adapter => 'mysql2', :username => 'root
6
6
 
7
7
  describe "upserting on mysql2" do
8
8
  before do
9
- ActiveRecord::Base.connection.drop_table Pet.table_name rescue nil
9
+ ActiveRecord::Base.connection.drop_table(Pet.table_name) rescue nil
10
10
  Pet.auto_upgrade!
11
11
  @opened_connections = []
12
12
  @connection = new_connection
@@ -23,6 +23,15 @@ describe "upserting on mysql2" do
23
23
  @connection
24
24
  end
25
25
 
26
- it_behaves_like :database
26
+ it_also 'is a database with an upsert trick'
27
27
 
28
+ it_also 'is just as correct as other ways'
29
+
30
+ it_also 'can be speeded up with upserting'
31
+
32
+ it_also 'supports binary upserts'
33
+
34
+ it_also "supports multibyte"
35
+
36
+ it_also "doesn't mess with timezones"
28
37
  end
data/test/test_pg.rb CHANGED
@@ -7,7 +7,7 @@ ActiveRecord::Base.establish_connection :adapter => 'postgresql', :database => '
7
7
 
8
8
  describe "upserting on postgresql" do
9
9
  before do
10
- ActiveRecord::Base.connection.drop_table Pet.table_name rescue nil
10
+ ActiveRecord::Base.connection.drop_table(Pet.table_name) rescue nil
11
11
  Pet.auto_upgrade!
12
12
  @opened_connections = []
13
13
  @connection = new_connection
@@ -24,6 +24,15 @@ describe "upserting on postgresql" do
24
24
  @connection
25
25
  end
26
26
 
27
- it_behaves_like :database
27
+ it_also 'is a database with an upsert trick'
28
28
 
29
+ it_also 'is just as correct as other ways'
30
+
31
+ it_also 'can be speeded up with upserting'
32
+
33
+ it_also 'supports binary upserts'
34
+
35
+ it_also "supports multibyte"
36
+
37
+ it_also "doesn't mess with timezones"
29
38
  end
data/test/test_sqlite.rb CHANGED
@@ -8,17 +8,33 @@ ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => db_p
8
8
 
9
9
  describe "upserting on sqlite" do
10
10
  before do
11
- ActiveRecord::Base.connection.drop_table Pet.table_name rescue nil
11
+ ActiveRecord::Base.connection.drop_table(Pet.table_name) rescue nil
12
12
  Pet.auto_upgrade!
13
+ @opened_connections = []
13
14
  @connection = new_connection
14
15
  end
16
+ after do
17
+ @opened_connections.each { |c| c.close }
18
+ end
19
+
15
20
  def new_connection
16
- db_path = File.expand_path('../../tmp/test.sqlite3', __FILE__)
17
- SQLite3::Database.open(db_path)
21
+ c = SQLite3::Database.open(File.expand_path('../../tmp/test.sqlite3', __FILE__))
22
+ @opened_connections << c
23
+ c
18
24
  end
19
25
  def connection
20
26
  @connection
21
27
  end
22
28
 
23
- it_behaves_like :database
29
+ it_also 'is a database with an upsert trick'
30
+
31
+ it_also 'is just as correct as other ways'
32
+
33
+ it_also 'can be speeded up with upserting'
34
+
35
+ it_also "supports multibyte"
36
+
37
+ it_also "doesn't mess with timezones"
38
+
39
+ it_also 'supports binary upserts'
24
40
  end
data/upsert.gemspec CHANGED
@@ -20,7 +20,9 @@ Gem::Specification.new do |gem|
20
20
  gem.add_development_dependency 'pg'
21
21
  gem.add_development_dependency 'activerecord' # testing only
22
22
  gem.add_development_dependency 'active_record_inline_schema'
23
+ gem.add_development_dependency 'faker'
23
24
  gem.add_development_dependency 'minitest'
24
25
  gem.add_development_dependency 'minitest-reporters'
25
26
  gem.add_development_dependency 'yard'
27
+ gem.add_development_dependency 'activerecord-import'
26
28
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: upsert
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-13 00:00:00.000000000 Z
12
+ date: 2012-06-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sqlite3
@@ -91,6 +91,22 @@ dependencies:
91
91
  - - ! '>='
92
92
  - !ruby/object:Gem::Version
93
93
  version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: faker
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'
94
110
  - !ruby/object:Gem::Dependency
95
111
  name: minitest
96
112
  requirement: !ruby/object:Gem::Requirement
@@ -139,6 +155,22 @@ dependencies:
139
155
  - - ! '>='
140
156
  - !ruby/object:Gem::Version
141
157
  version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: activerecord-import
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
142
174
  description: Upsert for MySQL, PostgreSQL, and SQLite. Codifies various SQL MERGE
143
175
  tricks like MySQL's ON DUPLICATE KEY UPDATE, PostgreSQL's CREATE FUNCTION merge_db,
144
176
  and SQLite's INSERT OR IGNORE.
@@ -149,11 +181,13 @@ extensions: []
149
181
  extra_rdoc_files: []
150
182
  files:
151
183
  - .gitignore
184
+ - .yardopts
152
185
  - Gemfile
153
186
  - LICENSE
154
187
  - README.md
155
188
  - Rakefile
156
189
  - lib/upsert.rb
190
+ - lib/upsert/binary.rb
157
191
  - lib/upsert/buffer.rb
158
192
  - lib/upsert/buffer/mysql2_client.rb
159
193
  - lib/upsert/buffer/pg_connection.rb
@@ -163,11 +197,16 @@ files:
163
197
  - lib/upsert/row.rb
164
198
  - lib/upsert/version.rb
165
199
  - test/helper.rb
166
- - test/shared_examples.rb
200
+ - test/shared/binary.rb
201
+ - test/shared/correctness.rb
202
+ - test/shared/database.rb
203
+ - test/shared/multibyte.rb
204
+ - test/shared/speed.rb
205
+ - test/shared/timezones.rb
206
+ - test/test_active_record_connection_adapter.rb
167
207
  - test/test_mysql2.rb
168
208
  - test/test_pg.rb
169
209
  - test/test_sqlite.rb
170
- - test/test_upsert.rb
171
210
  - upsert.gemspec
172
211
  homepage: https://github.com/seamusabshere/upsert
173
212
  licenses: []
@@ -196,9 +235,14 @@ summary: Upsert for MySQL, PostgreSQL, and SQLite. Finally, all those SQL MERGE
196
235
  codified.
197
236
  test_files:
198
237
  - test/helper.rb
199
- - test/shared_examples.rb
238
+ - test/shared/binary.rb
239
+ - test/shared/correctness.rb
240
+ - test/shared/database.rb
241
+ - test/shared/multibyte.rb
242
+ - test/shared/speed.rb
243
+ - test/shared/timezones.rb
244
+ - test/test_active_record_connection_adapter.rb
200
245
  - test/test_mysql2.rb
201
246
  - test/test_pg.rb
202
247
  - test/test_sqlite.rb
203
- - test/test_upsert.rb
204
248
  has_rdoc:
data/test/test_upsert.rb DELETED
@@ -1,7 +0,0 @@
1
- require 'helper'
2
-
3
- describe Upsert do
4
- describe :row do
5
-
6
- end
7
- end