active-record-transactioner 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0d85c7182312c5329e6c8731c57aa2d737f9952
4
- data.tar.gz: 42316fbb652cfef2b9b35b0c572244dde822b404
3
+ metadata.gz: 47b2ab9839e7398fbbfa1684d0e205596df258ab
4
+ data.tar.gz: e0197e8fbd713edd92990767c92711b8fe286c8b
5
5
  SHA512:
6
- metadata.gz: 90e200215749d32941aace7319d6c05d0cd305bf237afa40740d441078542fc83a6e647385449e2e4b9908f389471c59b27c90e4034b0d9612157816452b81ce
7
- data.tar.gz: 2ee07d9fa63ebb9bb7ec9f4d620eef2ea98166fe25464497c42ecfebb533cdbb3e392fcd0bcdd4eeb4eb586452bf707797f86b4ad5b9059392ec9943d832da21
6
+ metadata.gz: 25fd2e63c7d0e7ba9bcb8d674a8cf236e657a18243e172d07b4fa327b92196da22f5773cbb2dbf4a64da6157f4419432b32764812acc91f7019b32af87e438dd
7
+ data.tar.gz: 65fdf15c694d40a88c2aee97dbaeb638516b1b19a6491b7e7b70f158ef9a383e08f32a892e0c1501dc085f761ee9e05b2b3724545b4c8a27222e7931bd75816a
data/Gemfile CHANGED
@@ -7,14 +7,20 @@ source "http://rubygems.org"
7
7
  # Include everything needed to run rake, tests, features, etc.
8
8
  group :development do
9
9
  gem "rails", "~> 4.0.10"
10
- gem "rspec-rails", "~> 3.1.0"
10
+ gem "rspec-rails", "~> 3.4.0"
11
11
  gem "rdoc", "~> 3.12"
12
12
  gem "bundler", ">= 1.0.0"
13
13
  gem "jeweler", ">= 1.8.4"
14
14
  gem "builder"
15
15
  gem "activerecord"
16
- gem "mysql2"
16
+ gem "sqlite3", "1.3.11"
17
+ gem "mysql2", "0.3.20"
17
18
  gem "pry"
19
+ gem "best_practice_project", github: "kaspernj/best_practice_project" # path: "/home/kaspernj/Dev/Ruby/best_practice_project"
20
+ gem "rubocop", "0.35.1"
21
+ gem "database_cleaner"
18
22
  end
19
23
 
20
- gem "codeclimate-test-reporter", group: :test, require: nil
24
+ group :test do
25
+ gem "codeclimate-test-reporter", require: nil
26
+ end
@@ -1,3 +1,10 @@
1
+ GIT
2
+ remote: git://github.com/kaspernj/best_practice_project.git
3
+ revision: a733a53ffa7fcf2af0b49c30fa7d949579319782
4
+ specs:
5
+ best_practice_project (0.0.5)
6
+ psych (~> 2.0.0)
7
+
1
8
  GEM
2
9
  remote: http://rubygems.org/
3
10
  specs:
@@ -27,10 +34,14 @@ GEM
27
34
  tzinfo (~> 0.3.37)
28
35
  addressable (2.3.6)
29
36
  arel (4.0.2)
37
+ ast (2.1.0)
38
+ astrolabe (1.3.1)
39
+ parser (~> 2.2)
30
40
  builder (3.1.4)
31
41
  codeclimate-test-reporter (0.4.2)
32
42
  simplecov (>= 0.7.1, < 1.0.0)
33
43
  coderay (1.1.0)
44
+ database_cleaner (1.4.1)
34
45
  descendants_tracker (0.0.4)
35
46
  thread_safe (~> 0.3, >= 0.3.1)
36
47
  diff-lcs (1.2.5)
@@ -72,7 +83,7 @@ GEM
72
83
  multi_json (1.10.0)
73
84
  multi_xml (0.5.5)
74
85
  multipart-post (2.0.0)
75
- mysql2 (0.3.16)
86
+ mysql2 (0.3.20)
76
87
  nokogiri (1.6.2.1)
77
88
  mini_portile (= 0.6.0)
78
89
  oauth2 (0.9.3)
@@ -81,10 +92,14 @@ GEM
81
92
  multi_json (~> 1.3)
82
93
  multi_xml (~> 0.5)
83
94
  rack (~> 1.2)
95
+ parser (2.2.3.0)
96
+ ast (>= 1.1, < 3.0)
97
+ powerpack (0.1.1)
84
98
  pry (0.10.1)
85
99
  coderay (~> 1.1.0)
86
100
  method_source (~> 0.8.1)
87
101
  slop (~> 3.4)
102
+ psych (2.0.15)
88
103
  rack (1.5.2)
89
104
  rack-test (0.6.2)
90
105
  rack (>= 1.0)
@@ -101,25 +116,35 @@ GEM
101
116
  activesupport (= 4.0.12)
102
117
  rake (>= 0.8.7)
103
118
  thor (>= 0.18.1, < 2.0)
119
+ rainbow (2.0.0)
104
120
  rake (10.3.2)
105
121
  rdoc (3.12.2)
106
122
  json (~> 1.4)
107
- rspec-core (3.1.7)
108
- rspec-support (~> 3.1.0)
109
- rspec-expectations (3.1.2)
123
+ rspec-core (3.4.1)
124
+ rspec-support (~> 3.4.0)
125
+ rspec-expectations (3.4.0)
110
126
  diff-lcs (>= 1.2.0, < 2.0)
111
- rspec-support (~> 3.1.0)
112
- rspec-mocks (3.1.3)
113
- rspec-support (~> 3.1.0)
114
- rspec-rails (3.1.0)
115
- actionpack (>= 3.0)
116
- activesupport (>= 3.0)
117
- railties (>= 3.0)
118
- rspec-core (~> 3.1.0)
119
- rspec-expectations (~> 3.1.0)
120
- rspec-mocks (~> 3.1.0)
121
- rspec-support (~> 3.1.0)
122
- rspec-support (3.1.2)
127
+ rspec-support (~> 3.4.0)
128
+ rspec-mocks (3.4.0)
129
+ diff-lcs (>= 1.2.0, < 2.0)
130
+ rspec-support (~> 3.4.0)
131
+ rspec-rails (3.4.0)
132
+ actionpack (>= 3.0, < 4.3)
133
+ activesupport (>= 3.0, < 4.3)
134
+ railties (>= 3.0, < 4.3)
135
+ rspec-core (~> 3.4.0)
136
+ rspec-expectations (~> 3.4.0)
137
+ rspec-mocks (~> 3.4.0)
138
+ rspec-support (~> 3.4.0)
139
+ rspec-support (3.4.1)
140
+ rubocop (0.35.1)
141
+ astrolabe (~> 1.3)
142
+ parser (>= 2.2.3.0, < 3.0)
143
+ powerpack (~> 0.1)
144
+ rainbow (>= 1.99.1, < 3.0)
145
+ ruby-progressbar (~> 1.7)
146
+ tins (<= 1.6.0)
147
+ ruby-progressbar (1.7.5)
123
148
  simplecov (0.9.1)
124
149
  docile (~> 1.1.0)
125
150
  multi_json (~> 1.0)
@@ -135,9 +160,11 @@ GEM
135
160
  actionpack (>= 3.0)
136
161
  activesupport (>= 3.0)
137
162
  sprockets (>= 2.8, < 4.0)
163
+ sqlite3 (1.3.11)
138
164
  thor (0.19.1)
139
165
  thread_safe (0.3.4)
140
166
  tilt (1.4.1)
167
+ tins (1.6.0)
141
168
  tzinfo (0.3.42)
142
169
 
143
170
  PLATFORMS
@@ -145,12 +172,19 @@ PLATFORMS
145
172
 
146
173
  DEPENDENCIES
147
174
  activerecord
175
+ best_practice_project!
148
176
  builder
149
177
  bundler (>= 1.0.0)
150
178
  codeclimate-test-reporter
179
+ database_cleaner
151
180
  jeweler (>= 1.8.4)
152
- mysql2
181
+ mysql2 (= 0.3.20)
153
182
  pry
154
183
  rails (~> 4.0.10)
155
184
  rdoc (~> 3.12)
156
- rspec-rails (~> 3.1.0)
185
+ rspec-rails (~> 3.4.0)
186
+ rubocop (= 0.35.1)
187
+ sqlite3 (= 1.3.11)
188
+
189
+ BUNDLED WITH
190
+ 1.10.6
data/README.md CHANGED
@@ -28,8 +28,6 @@ end
28
28
  You can also do it a bit more complicated with some custom options.
29
29
  ```ruby
30
30
  ActiveRecordTransactioner.new(
31
- call_args: ["Hello world!"],
32
- call_method: :save!,
33
31
  transaction_method: :transaction,
34
32
  transaction_size: 1000,
35
33
  threadded: false
@@ -41,6 +39,24 @@ ActiveRecordTransactioner.new(
41
39
  end
42
40
  ```
43
41
 
42
+ ### Update columns
43
+ ```ruby
44
+ ActiveRecordTransactioner.new do |trans|
45
+ models.each do |model|
46
+ trans.update_columns(model, some_column: "new_value")
47
+ end
48
+ end
49
+ ```
50
+
51
+ ### Inserts in a single SQL statement (bulk inserts)
52
+ ```ruby
53
+ ActiveRecordTransactioner.new do |trans|
54
+ 1000.times do |count|
55
+ trans.bulk_create!(User.new(email: "test#{count}@example.com"))
56
+ end
57
+ end
58
+ ```
59
+
44
60
  ### Destroy
45
61
  ```ruby
46
62
  ActiveRecordTransactioner.new do |trans|
@@ -52,9 +68,9 @@ end
52
68
 
53
69
  ### Threadded
54
70
 
55
- The "threadded" and "max_running_threads" options will start new threads to actually do the saving of the models, while continuing to queue up new models for saving in the primary thread. This way the database can utilize multiple cores, and if you use a threadded VM like JRuby or Rubinius, you will utilize even more cores.
71
+ The "threadded" and "max_running_threads" options will start new threads to do the saving of the models, while continuing to queue up new models in the primary thread. This way the database can utilize multiple cores, and if you use a threadded VM like JRuby or Rubinius, you will utilize even more.
56
72
 
57
- This can help greatly speed up the processing.
73
+ This can help greatly speed up the processing of records.
58
74
 
59
75
  Be aware that the saving of only one type of model, will be limited to only one thread, so it will make sense to try and queue up as many type of models as possible. Like users, orders and so on.
60
76
 
@@ -71,7 +87,7 @@ end
71
87
  ```
72
88
 
73
89
  ## Contributing to active-record-transactioner
74
-
90
+
75
91
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
76
92
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
77
93
  * Fork the project.
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'rubygems'
4
- require 'bundler'
3
+ require "rubygems"
4
+ require "bundler"
5
5
  begin
6
6
  Bundler.setup(:default, :development)
7
7
  rescue Bundler::BundlerError => e
@@ -9,41 +9,44 @@ rescue Bundler::BundlerError => e
9
9
  $stderr.puts "Run `bundle install` to install missing gems"
10
10
  exit e.status_code
11
11
  end
12
- require 'rake'
12
+ require "rake"
13
13
 
14
- require 'jeweler'
14
+ require "jeweler"
15
15
  Jeweler::Tasks.new do |gem|
16
16
  # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
17
  gem.name = "active-record-transactioner"
18
18
  gem.homepage = "http://github.com/kaspernj/active-record-transactioner"
19
19
  gem.license = "MIT"
20
- gem.summary = %Q{Queue up calls to specific models and execute them in transactions, after a certain number of models have been added.}
21
- gem.description = %Q{Queue up calls to specific models and execute them in transactions, after a certain number of models have been added.}
20
+ gem.summary = "Queue up calls to specific models and execute them in transactions, after a certain number of models have been added."
21
+ gem.description = "Queue up calls to specific models and execute them in transactions, after a certain number of models have been added."
22
22
  gem.email = "kj@gfish.com"
23
23
  gem.authors = ["Kasper Johansen"]
24
24
  # dependencies defined in Gemfile
25
25
  end
26
26
  Jeweler::RubygemsDotOrgTasks.new
27
27
 
28
- require 'rspec/core'
29
- require 'rspec/core/rake_task'
28
+ require "rspec/core"
29
+ require "rspec/core/rake_task"
30
30
  RSpec::Core::RakeTask.new(:spec) do |spec|
31
- spec.pattern = FileList['spec/**/*_spec.rb']
31
+ spec.pattern = FileList["spec/**/*_spec.rb"]
32
32
  end
33
33
 
34
34
  RSpec::Core::RakeTask.new(:rcov) do |spec|
35
- spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.pattern = "spec/**/*_spec.rb"
36
36
  spec.rcov = true
37
37
  end
38
38
 
39
- task :default => :spec
39
+ task default: :spec
40
40
 
41
- require 'rdoc/task'
41
+ require "rdoc/task"
42
42
  Rake::RDocTask.new do |rdoc|
43
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+ version = File.exist?("VERSION") ? File.read("VERSION") : ""
44
44
 
45
- rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.rdoc_dir = "rdoc"
46
46
  rdoc.title = "active-record-transactioner #{version}"
47
- rdoc.rdoc_files.include('README*')
48
- rdoc.rdoc_files.include('lib/**/*.rb')
47
+ rdoc.rdoc_files.include("README*")
48
+ rdoc.rdoc_files.include("lib/**/*.rb")
49
49
  end
50
+
51
+ require "best_practice_project"
52
+ BestPracticeProject.load_tasks
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.6
1
+ 0.0.7
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: active-record-transactioner 0.0.6 ruby lib
5
+ # stub: active-record-transactioner 0.0.7 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "active-record-transactioner"
9
- s.version = "0.0.6"
9
+ s.version = "0.0.7"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Kasper Johansen"]
14
- s.date = "2014-12-05"
14
+ s.date = "2015-12-05"
15
15
  s.description = "Queue up calls to specific models and execute them in transactions, after a certain number of models have been added."
16
16
  s.email = "kj@gfish.com"
17
17
  s.extra_rdoc_files = [
@@ -28,6 +28,8 @@ Gem::Specification.new do |s|
28
28
  "Rakefile",
29
29
  "VERSION",
30
30
  "active-record-transactioner.gemspec",
31
+ "config/best_project_practice_rubocop.yml",
32
+ "config/best_project_practice_rubocop_todo.yml",
31
33
  "lib/active-record-transactioner.rb",
32
34
  "shippable.yml",
33
35
  "spec/active-record-transactioner_spec.rb",
@@ -81,7 +83,7 @@ Gem::Specification.new do |s|
81
83
  ]
82
84
  s.homepage = "http://github.com/kaspernj/active-record-transactioner"
83
85
  s.licenses = ["MIT"]
84
- s.rubygems_version = "2.4.0"
86
+ s.rubygems_version = "2.2.2"
85
87
  s.summary = "Queue up calls to specific models and execute them in transactions, after a certain number of models have been added."
86
88
 
87
89
  if s.respond_to? :specification_version then
@@ -89,35 +91,47 @@ Gem::Specification.new do |s|
89
91
 
90
92
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
91
93
  s.add_development_dependency(%q<rails>, ["~> 4.0.10"])
92
- s.add_development_dependency(%q<rspec-rails>, ["~> 3.1.0"])
94
+ s.add_development_dependency(%q<rspec-rails>, ["~> 3.4.0"])
93
95
  s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
94
96
  s.add_development_dependency(%q<bundler>, [">= 1.0.0"])
95
97
  s.add_development_dependency(%q<jeweler>, [">= 1.8.4"])
96
98
  s.add_development_dependency(%q<builder>, [">= 0"])
97
99
  s.add_development_dependency(%q<activerecord>, [">= 0"])
98
- s.add_development_dependency(%q<mysql2>, [">= 0"])
100
+ s.add_development_dependency(%q<sqlite3>, ["= 1.3.11"])
101
+ s.add_development_dependency(%q<mysql2>, ["= 0.3.20"])
99
102
  s.add_development_dependency(%q<pry>, [">= 0"])
103
+ s.add_development_dependency(%q<best_practice_project>, [">= 0"])
104
+ s.add_development_dependency(%q<rubocop>, ["= 0.35.1"])
105
+ s.add_development_dependency(%q<database_cleaner>, [">= 0"])
100
106
  else
101
107
  s.add_dependency(%q<rails>, ["~> 4.0.10"])
102
- s.add_dependency(%q<rspec-rails>, ["~> 3.1.0"])
108
+ s.add_dependency(%q<rspec-rails>, ["~> 3.4.0"])
103
109
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
104
110
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
105
111
  s.add_dependency(%q<jeweler>, [">= 1.8.4"])
106
112
  s.add_dependency(%q<builder>, [">= 0"])
107
113
  s.add_dependency(%q<activerecord>, [">= 0"])
108
- s.add_dependency(%q<mysql2>, [">= 0"])
114
+ s.add_dependency(%q<sqlite3>, ["= 1.3.11"])
115
+ s.add_dependency(%q<mysql2>, ["= 0.3.20"])
109
116
  s.add_dependency(%q<pry>, [">= 0"])
117
+ s.add_dependency(%q<best_practice_project>, [">= 0"])
118
+ s.add_dependency(%q<rubocop>, ["= 0.35.1"])
119
+ s.add_dependency(%q<database_cleaner>, [">= 0"])
110
120
  end
111
121
  else
112
122
  s.add_dependency(%q<rails>, ["~> 4.0.10"])
113
- s.add_dependency(%q<rspec-rails>, ["~> 3.1.0"])
123
+ s.add_dependency(%q<rspec-rails>, ["~> 3.4.0"])
114
124
  s.add_dependency(%q<rdoc>, ["~> 3.12"])
115
125
  s.add_dependency(%q<bundler>, [">= 1.0.0"])
116
126
  s.add_dependency(%q<jeweler>, [">= 1.8.4"])
117
127
  s.add_dependency(%q<builder>, [">= 0"])
118
128
  s.add_dependency(%q<activerecord>, [">= 0"])
119
- s.add_dependency(%q<mysql2>, [">= 0"])
129
+ s.add_dependency(%q<sqlite3>, ["= 1.3.11"])
130
+ s.add_dependency(%q<mysql2>, ["= 0.3.20"])
120
131
  s.add_dependency(%q<pry>, [">= 0"])
132
+ s.add_dependency(%q<best_practice_project>, [">= 0"])
133
+ s.add_dependency(%q<rubocop>, ["= 0.35.1"])
134
+ s.add_dependency(%q<database_cleaner>, [">= 0"])
121
135
  end
122
136
  end
123
137
 
@@ -0,0 +1,7 @@
1
+ ---
2
+ inherit_from: best_project_practice_rubocop_todo.yml
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - active-record-transactioner.gemspec
7
+ - spec/dummy/db/schema.rb
@@ -0,0 +1,21 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+ # This configuration was generated by
8
+ # `rubocop --auto-gen-config`
9
+ # on 2015-12-05 17:13:03 +0100 using RuboCop version 0.35.1.
10
+ # The point is for the user to remove these configuration records
11
+ # one by one as the offenses are removed from the code base.
12
+ # Note that changes in the inspected code, or installation of new
13
+ # versions of RuboCop, may require this file to be generated again.
14
+
15
+ # Offense count: 1
16
+ Metrics/CyclomaticComplexity:
17
+ Max: 8
18
+
19
+ # Offense count: 1
20
+ Metrics/PerceivedComplexity:
21
+ Max: 9
@@ -11,21 +11,23 @@ class ActiveRecordTransactioner
11
11
  debug: false
12
12
  }
13
13
 
14
+ EMPTY_ARGS = []
15
+
14
16
  ALLOWED_ARGS = DEFAULT_ARGS.keys
15
17
 
16
18
  def initialize(args = {})
17
- args.each { |key, val| raise "Invalid key: '#{key}'." unless ALLOWED_ARGS.include?(key) }
19
+ args.each_key { |key| raise "Invalid key: '#{key}'." unless ALLOWED_ARGS.include?(key) }
18
20
 
19
21
  @args = DEFAULT_ARGS.merge(args)
20
22
  parse_and_set_args
21
23
 
22
- if block_given?
23
- begin
24
- yield self
25
- ensure
26
- flush
27
- join if threadded?
28
- end
24
+ return unless block_given?
25
+
26
+ begin
27
+ yield self
28
+ ensure
29
+ flush
30
+ join if threadded?
29
31
  end
30
32
  end
31
33
 
@@ -35,6 +37,27 @@ class ActiveRecordTransactioner
35
37
  queue(model, type: :save!, validate: false)
36
38
  end
37
39
 
40
+ def bulk_create!(model)
41
+ attributes = model.attributes
42
+ attributes.delete("id")
43
+ attributes.delete("created_at")
44
+ attributes.delete("updated_at")
45
+
46
+ klass = model.class
47
+ @bulk_creates[klass] ||= []
48
+ @bulk_creates[klass] << attributes
49
+
50
+ @count += 1
51
+ end
52
+
53
+ def update_columns(model, updates)
54
+ queue(model, type: :update_columns, validate: false, method_args: [updates])
55
+ end
56
+
57
+ def update_column(model, column_name, new_value)
58
+ update_columns(model, column_name => new_value)
59
+ end
60
+
38
61
  def destroy!(model)
39
62
  queue(model, type: :destroy!)
40
63
  end
@@ -49,8 +72,15 @@ class ActiveRecordTransactioner
49
72
  validate = args.key?(:validate) ? args[:validate] : true
50
73
 
51
74
  @lock_models[klass] ||= Monitor.new
75
+
52
76
  @models[klass] ||= []
53
- @models[klass] << {model: model, type: args[:type], validate: validate}
77
+ @models[klass] << {
78
+ model: model,
79
+ type: args.fetch(:type),
80
+ validate: validate,
81
+ method_args: args[:method_args] || EMPTY_ARGS
82
+ }
83
+
54
84
  @count += 1
55
85
  end
56
86
 
@@ -62,6 +92,14 @@ class ActiveRecordTransactioner
62
92
  wait_for_threads if threadded?
63
93
 
64
94
  @lock.synchronize do
95
+ @bulk_creates.each do |klass, attribute_array|
96
+ if threadded?
97
+ bulk_insert_attribute_array_threadded(klass, attribute_array)
98
+ else
99
+ bulk_insert_attribute_array(klass, attribute_array)
100
+ end
101
+ end
102
+
65
103
  @models.each do |klass, models|
66
104
  next if models.empty?
67
105
 
@@ -82,9 +120,7 @@ class ActiveRecordTransactioner
82
120
  threads_to_join = @lock_threads.synchronize { @threads.clone }
83
121
 
84
122
  debug "Threads to join: #{threads_to_join}" if @debug
85
- threads_to_join.each do |thread|
86
- thread.join
87
- end
123
+ threads_to_join.each(&:join)
88
124
  end
89
125
 
90
126
  def threadded?
@@ -95,6 +131,7 @@ private
95
131
 
96
132
  def parse_and_set_args
97
133
  @models = {}
134
+ @bulk_creates = {}
98
135
  @threads = []
99
136
  @count = 0
100
137
  @lock = Monitor.new
@@ -110,16 +147,15 @@ private
110
147
  end
111
148
 
112
149
  def wait_for_threads
113
- break_loop = false
114
- while !break_loop
150
+ loop do
115
151
  debug "Running threads: #{@threads.length} / #{@max_running_threads}" if @debug
116
152
  if allowed_to_start_new_thread?
117
- break_loop = true
153
+ break
118
154
  else
119
155
  debug "Waiting for threads #{@threads.length} / #{@max_running_threads}" if @debug
120
156
  end
121
157
 
122
- sleep 0.2 unless break_loop
158
+ sleep 0.2
123
159
  end
124
160
 
125
161
  debug "Done waiting." if @debug
@@ -132,23 +168,30 @@ private
132
168
  debug "Opening new transaction by using '#{@args[:transaction_method]}'." if @debug
133
169
 
134
170
  klass.__send__(@args[:transaction_method]) do
135
- debug "Going through models." if @debug
136
- models.each do |work|
137
- debug work if @debug
138
-
139
- if work[:type] == :save!
140
- validate = work.key?(:validate) ? work[:validate] : true
141
- work[:model].save! validate: validate
142
- elsif work[:type] == :destroy!
143
- work[:model].destroy!
144
- else
145
- raise "Invalid type: '#{work[:type]}'."
146
- end
147
- end
171
+ work_models(models)
172
+ end
173
+ end
174
+ end
175
+
176
+ def work_models(models)
177
+ debug "Going through models." if @debug
178
+ models.each do |work|
179
+ debug work if @debug
180
+
181
+ work_type = work.fetch(:type)
182
+ model = work.fetch(:model)
148
183
 
149
- debug "Done working with models." if @debug
184
+ if work_type == :save!
185
+ validate = work.key?(:validate) ? work[:validate] : true
186
+ model.save! validate: validate
187
+ elsif work_type == :update_columns || work_type == :destroy!
188
+ model.__send__(work_type, *work.fetch(:method_args))
189
+ else
190
+ raise "Invalid type: '#{work[:type]}'."
150
191
  end
151
192
  end
193
+
194
+ debug "Done working with models." if @debug
152
195
  end
153
196
 
154
197
  def work_threadded(klass, models)
@@ -159,7 +202,7 @@ private
159
202
  work_models_through_transaction(klass, models)
160
203
  end
161
204
  rescue => e
162
- pute e.inspect
205
+ puts e.inspect
163
206
  puts e.backtrace
164
207
 
165
208
  raise e
@@ -182,4 +225,69 @@ private
182
225
  def allowed_to_start_new_thread?
183
226
  @lock_threads.synchronize { return @threads.length < @max_running_threads }
184
227
  end
228
+
229
+ def bulk_insert_attribute_array_threadded(klass, attribute_array)
230
+ @lock_threads.synchronize do
231
+ @threads << Thread.new do
232
+ begin
233
+ bulk_insert_attribute_array(klass, attribute_array)
234
+ rescue => e
235
+ puts e.inspect
236
+ puts e.backtrace
237
+
238
+ raise e
239
+ ensure
240
+ debug "Removing thread #{Thread.current.__id__}" if @debug
241
+ @lock_threads.synchronize { @threads.delete(Thread.current) }
242
+
243
+ debug "Threads count after remove: #{@threads.length}" if @debug
244
+ end
245
+ end
246
+ end
247
+ end
248
+
249
+ def bulk_insert_attribute_array(klass, attribute_array)
250
+ sql = "INSERT INTO `#{klass.table_name}` ("
251
+
252
+ first = true
253
+ attribute_array.first.each_key do |key|
254
+ if first
255
+ first = false
256
+ else
257
+ sql << ", "
258
+ end
259
+
260
+ sql << "`#{key}`"
261
+ end
262
+
263
+ sql << ") VALUES ("
264
+
265
+ first_insert = true
266
+ attribute_array.each do |attributes|
267
+ if first_insert
268
+ first_insert = false
269
+ else
270
+ sql << "), ("
271
+ end
272
+
273
+ first_value = true
274
+ attributes.each_value do |value|
275
+ if first_value
276
+ first_value = false
277
+ else
278
+ sql << ", "
279
+ end
280
+
281
+ sql << klass.connection.quote(value)
282
+ end
283
+ end
284
+
285
+ sql << ")"
286
+
287
+ klass.connection.execute(sql)
288
+
289
+ @lock.synchronize do
290
+ @count -= attribute_array.length
291
+ end
292
+ end
185
293
  end
@@ -4,13 +4,13 @@ archive: true
4
4
  rvm:
5
5
  - 2.1.2
6
6
  before_script:
7
- - cp spec/dummy/config/database.shippable.yml spec/dummy/config/database.yml
8
- - mysql -e 'DROP DATABASE IF EXISTS transactioner;'
9
- - mysql -e 'CREATE DATABASE transactioner;'
7
+ - mysql -e "CREATE DATABASE IF NOT EXISTS shippa;"
10
8
  - cd spec/dummy
11
- - RAILS_ENV=test bundle exec rake db:migrate
9
+ - cp config/database.shippable.yml config/database.yml
10
+ - RAILS_ENV=test bundle exec rake db:schema:load
12
11
  - cd ../..
13
12
  script:
14
13
  - CODECLIMATE_REPO_TOKEN=b7d9c971bea2879b81bdf35f600b985bf3a016c6104169b2c29c213a5d2e7142 bundle exec rspec
14
+ - bundle exec rake best_practice_project:run
15
15
  notifications:
16
16
  email: false
@@ -16,25 +16,25 @@ describe "ActiveRecordTransactioner" do
16
16
 
17
17
  trans.join
18
18
 
19
- model1.save_called.should eq true
20
- model2.save_called.should eq true
21
- model3.save_called.should eq false
19
+ expect(model1.save_called).to eq true
20
+ expect(model2.save_called).to eq true
21
+ expect(model3.save_called).to eq false
22
22
 
23
23
  called = false
24
- ActiveRecordTransactioner.new do |trans|
24
+ ActiveRecordTransactioner.new do |transactioner|
25
25
  called = true
26
- trans.class.should eql(ActiveRecordTransactioner)
26
+ expect(transactioner).to be_a ActiveRecordTransactioner
27
27
  end
28
28
 
29
- called.should eq true
29
+ expect(called).to eq true
30
30
  end
31
31
 
32
- it "should not fail under the Rails reverse bug" do
32
+ it "doesnt fail under the Rails reverse bug" do
33
33
  trans = ActiveRecordTransactioner.new(transaction_size: 1)
34
34
  model1 = ActiveRecordTransactionerTestClass.new
35
35
  trans.save!(model1)
36
36
  trans.join
37
37
 
38
- ActiveRecordTransactionerTestClass::ARGS[:nilraise].should eq false
38
+ expect(ActiveRecordTransactionerTestClass::ARGS[:nilraise]).to eq false
39
39
  end
40
40
  end
@@ -1,6 +1,6 @@
1
1
  # Add your own tasks in files placed in lib/tasks ending in .rake,
2
2
  # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
3
 
4
- require File.expand_path('../config/application', __FILE__)
4
+ require File.expand_path("../config/application", __FILE__)
5
5
 
6
6
  Dummy::Application.load_tasks
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env ruby
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
3
- load Gem.bin_path('bundler', 'bundle')
2
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
3
+ load Gem.bin_path("bundler", "bundle")
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- APP_PATH = File.expand_path('../../config/application', __FILE__)
3
- require_relative '../config/boot'
4
- require 'rails/commands'
2
+ APP_PATH = File.expand_path("../../config/application", __FILE__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
- require_relative '../config/boot'
3
- require 'rake'
2
+ require_relative "../config/boot"
3
+ require "rake"
4
4
  Rake.application.run
@@ -1,4 +1,4 @@
1
1
  # This file is used by Rack-based servers to start the application.
2
2
 
3
- require ::File.expand_path('../config/environment', __FILE__)
3
+ require ::File.expand_path("../config/environment", __FILE__)
4
4
  run Rails.application
@@ -1,4 +1,4 @@
1
- require File.expand_path('../boot', __FILE__)
1
+ require File.expand_path("../boot", __FILE__)
2
2
 
3
3
  # Pick the frameworks you want:
4
4
  require "active_record/railtie"
@@ -10,19 +10,18 @@ require "sprockets/railtie"
10
10
  Bundler.require(*Rails.groups)
11
11
  require "active-record-transactioner"
12
12
 
13
- module Dummy
14
- class Application < Rails::Application
15
- # Settings in config/environments/* take precedence over those specified here.
16
- # Application configuration should go into files in config/initializers
17
- # -- all .rb files in that directory are automatically loaded.
13
+ module Dummy; end
18
14
 
19
- # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
20
- # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
21
- # config.time_zone = 'Central Time (US & Canada)'
15
+ class Dummy::Application < Rails::Application
16
+ # Settings in config/environments/* take precedence over those specified here.
17
+ # Application configuration should go into files in config/initializers
18
+ # -- all .rb files in that directory are automatically loaded.
22
19
 
23
- # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
24
- # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
25
- # config.i18n.default_locale = :de
26
- end
27
- end
20
+ # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
21
+ # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
22
+ # config.time_zone = 'Central Time (US & Canada)'
28
23
 
24
+ # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
25
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
26
+ # config.i18n.default_locale = :de
27
+ end
@@ -1,5 +1,5 @@
1
1
  # Set up gems listed in the Gemfile.
2
- ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
2
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../../Gemfile", __FILE__)
3
3
 
4
- require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5
- $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)
4
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
5
+ $LOAD_PATH.unshift File.expand_path("../../../../lib", __FILE__)
@@ -1,7 +1,5 @@
1
1
  test:
2
- adapter: mysql2
3
- pool: 15
2
+ adapter: sqlite3
3
+ database: db/test.sqlite3
4
+ pool: 5
4
5
  timeout: 5000
5
- username: username
6
- password: password
7
- database: transactioner-test
@@ -1,7 +1,5 @@
1
1
  test:
2
2
  adapter: mysql2
3
- pool: 15
4
- timeout: 5000
5
3
  encoding: utf8
6
- database: transactioner
4
+ database: shippa
7
5
  username: shippa
@@ -1,7 +1,5 @@
1
1
  test:
2
- adapter: mysql2
3
- pool: 15
2
+ adapter: sqlite3
3
+ database: db/test.sqlite3
4
+ pool: 5
4
5
  timeout: 5000
5
- username: transactioner
6
- password: pRrdQjEhRfEu7Get
7
- database: transactioner-test
@@ -1,5 +1,5 @@
1
1
  # Load the Rails application.
2
- require File.expand_path('../application', __FILE__)
2
+ require File.expand_path("../application", __FILE__)
3
3
 
4
4
  # Initialize the Rails application.
5
5
  Dummy::Application.initialize!
@@ -33,7 +33,7 @@ Dummy::Application.configure do
33
33
  config.assets.digest = true
34
34
 
35
35
  # Version of your assets, change this if you want to expire all your assets.
36
- config.assets.version = '1.0'
36
+ config.assets.version = "1.0"
37
37
 
38
38
  # Specifies the header that your server uses for sending files.
39
39
  # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
@@ -9,4 +9,5 @@
9
9
 
10
10
  # Make sure your secret_key_base is kept private
11
11
  # if you're sharing your code publicly.
12
- Dummy::Application.config.secret_key_base = '6512c5484d76faa4df05f364d5681de25ee5e657672d98b0177cdb0a489862795e63d12244848be4d8b68f716f27b847654ac307c69f517f8fbc296796b06546'
12
+ Dummy::Application.config.secret_key_base =
13
+ "6512c5484d76faa4df05f364d5681de25ee5e657672d98b0177cdb0a489862795e63d12244848be4d8b68f716f27b847654ac307c69f517f8fbc296796b06546"
@@ -1,3 +1,3 @@
1
1
  # Be sure to restart your server when you modify this file.
2
2
 
3
- Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
3
+ Dummy::Application.config.session_store :cookie_store, key: "_dummy_session"
@@ -1,27 +1,38 @@
1
1
  require "codeclimate-test-reporter"
2
2
  CodeClimate::TestReporter.start
3
3
 
4
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
5
5
  $LOAD_PATH.unshift(File.dirname(__FILE__))
6
- require 'active_record'
7
- require 'active-record-transactioner'
6
+ require "active_record"
7
+ require "active-record-transactioner"
8
+ require "database_cleaner"
8
9
 
9
- ENV["RAILS_ENV"] ||= 'test'
10
+ ENV["RAILS_ENV"] ||= "test"
10
11
  require_relative "dummy/config/environment"
11
- require 'rspec/rails'
12
+ require "rspec/rails"
12
13
 
13
14
  # Requires supporting files with custom matchers and macros, etc,
14
15
  # in ./support/ and its subdirectories.
15
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
16
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
16
17
 
17
18
  ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)
18
19
 
19
20
  RSpec.configure do |config|
20
21
  config.expect_with :rspec do |c|
21
- c.syntax = [:should, :expect]
22
+ c.syntax = [:expect]
22
23
  end
23
24
 
24
25
  config.infer_base_class_for_anonymous_controllers = false
25
26
  config.infer_spec_type_from_file_location!
26
27
  config.use_transactional_fixtures = false
28
+
29
+ config.before(:suite) do
30
+ DatabaseCleaner.strategy = :truncation
31
+ end
32
+
33
+ config.around(:each) do |example|
34
+ DatabaseCleaner.cleaning do
35
+ example.run
36
+ end
37
+ end
27
38
  end
@@ -1,7 +1,5 @@
1
1
  shared_examples_for "basic user operations" do
2
2
  before do
3
- ActiveRecord::Base.connection.execute("TRUNCATE TABLE users")
4
-
5
3
  transactioner do |trans|
6
4
  100.times do |count|
7
5
  user = User.new(username: "User #{count}", email: "user#{count}@example.com")
@@ -11,7 +9,7 @@ shared_examples_for "basic user operations" do
11
9
  end
12
10
 
13
11
  it "can create a lot of models" do
14
- User.count.should eq 100
12
+ expect(User.count).to eq 100
15
13
  end
16
14
 
17
15
  it "can both insert and update a lot of records correct" do
@@ -19,17 +17,36 @@ shared_examples_for "basic user operations" do
19
17
  200.times do |count|
20
18
  user = User.find_or_initialize_by(email: "user#{count}@example.com")
21
19
  user.username = "User upsert #{count}"
20
+
22
21
  trans.save!(user)
23
22
  end
24
23
  end
25
24
 
26
25
  count = 0
27
26
  User.find_each do |user|
28
- user.email.should eq "user#{count}@example.com"
27
+ expect(user.email).to eq "user#{count}@example.com"
28
+ count += 1
29
+ end
30
+
31
+ expect(User.count).to eq 200
32
+ end
33
+
34
+ it "#update_columns" do
35
+ transactioner do |trans|
36
+ count = 0
37
+ User.find_each do |user|
38
+ trans.update_columns(user, email: "test#{count}@example.com")
39
+ count += 1
40
+ end
41
+ end
42
+
43
+ count = 0
44
+ User.find_each do |user|
45
+ expect(user.email).to eq "test#{count}@example.com"
29
46
  count += 1
30
47
  end
31
48
 
32
- User.count.should eq 200
49
+ expect(User.count).to eq 100
33
50
  end
34
51
 
35
52
  it "can delete a lot of records" do
@@ -39,6 +56,24 @@ shared_examples_for "basic user operations" do
39
56
  end
40
57
  end
41
58
 
42
- User.count.should eq 50
59
+ expect(User.count).to eq 50
60
+ end
61
+
62
+ it "does bulk inserts" do
63
+ User.delete_all
64
+
65
+ transactioner do |trans|
66
+ 300.times do |count|
67
+ trans.bulk_create!(User.new(email: "test#{count}@example.com"))
68
+ end
69
+ end
70
+
71
+ count = 0
72
+ User.order(:id).each do |user|
73
+ expect(user.email).to eq "test#{count}@example.com"
74
+ count += 1
75
+ end
76
+
77
+ expect(count).to eq 300
43
78
  end
44
79
  end
@@ -8,7 +8,7 @@ class ActiveRecordTransactionerTestClass
8
8
  end
9
9
 
10
10
  def self.transaction
11
- Thread.current[:trans] = self.name
11
+ Thread.current[:trans] = name
12
12
 
13
13
  begin
14
14
  yield
@@ -17,7 +17,7 @@ class ActiveRecordTransactionerTestClass
17
17
  end
18
18
  end
19
19
 
20
- def save!(args = {})
20
+ def save!(_args = {})
21
21
  raise "Failure - no transaction: #{Thread.current[:trans]}, #{self.class.name}" if Thread.current[:trans] != self.class.name
22
22
  @save_called = true
23
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active-record-transactioner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kasper Johansen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-05 00:00:00.000000000 Z
11
+ date: 2015-12-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 3.1.0
33
+ version: 3.4.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 3.1.0
40
+ version: 3.4.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rdoc
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -108,8 +108,36 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 1.3.11
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '='
123
+ - !ruby/object:Gem::Version
124
+ version: 1.3.11
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: mysql2
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '='
130
+ - !ruby/object:Gem::Version
131
+ version: 0.3.20
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '='
137
+ - !ruby/object:Gem::Version
138
+ version: 0.3.20
139
+ - !ruby/object:Gem::Dependency
140
+ name: pry
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - ">="
@@ -123,7 +151,35 @@ dependencies:
123
151
  - !ruby/object:Gem::Version
124
152
  version: '0'
125
153
  - !ruby/object:Gem::Dependency
126
- name: pry
154
+ name: best_practice_project
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '='
172
+ - !ruby/object:Gem::Version
173
+ version: 0.35.1
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - '='
179
+ - !ruby/object:Gem::Version
180
+ version: 0.35.1
181
+ - !ruby/object:Gem::Dependency
182
+ name: database_cleaner
127
183
  requirement: !ruby/object:Gem::Requirement
128
184
  requirements:
129
185
  - - ">="
@@ -154,6 +210,8 @@ files:
154
210
  - Rakefile
155
211
  - VERSION
156
212
  - active-record-transactioner.gemspec
213
+ - config/best_project_practice_rubocop.yml
214
+ - config/best_project_practice_rubocop_todo.yml
157
215
  - lib/active-record-transactioner.rb
158
216
  - shippable.yml
159
217
  - spec/active-record-transactioner_spec.rb
@@ -224,7 +282,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
224
282
  version: '0'
225
283
  requirements: []
226
284
  rubyforge_project:
227
- rubygems_version: 2.4.0
285
+ rubygems_version: 2.2.2
228
286
  signing_key:
229
287
  specification_version: 4
230
288
  summary: Queue up calls to specific models and execute them in transactions, after