bulk_update 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bulk_update.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Philip Kurmann
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # BulkUpdate
2
+
3
+ Updates a large amount of Records in a highliy efficient way.
4
+ Enhances Active Record with a method for bulk inserts and a method for bulk updates. Both merthods are used for inserting or updating large amount of Records.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'bulk_update'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install bulk_update
19
+
20
+ ## Usage
21
+
22
+ ### Bulk insert
23
+
24
+ Bulk insert inserts a large amount of data into as SQL-Bulk-Inserts. Example:
25
+ columns = [:name, :value]
26
+ values = [['name1', 'value1'], ['name2', 'value2'], ['name3', 'value3']]
27
+ Model.bulk_insert columns, values
28
+
29
+ ### Bulk update
30
+
31
+ Bulk update updates a large amount of data. Update means, it creates new records, updates existing records which have changed and deletes old records. This is all done with ActiveRecord which means, all callbacks are executed.
32
+ You have to provide a columns as a key, which is used determine which records are new, have changed or has to be deleted. Only the values provided in the array 'values' are compared and will be updated. Example:
33
+
34
+ columns = [:name, :value]
35
+ values = [['name1', 'value1'], ['name2', 'value2'], ['name3', 'value3']]
36
+ Model.bulk_insert columns, values
37
+
38
+ You have now the following entries in your database:
39
+ <pre>
40
+ +----+----------------+
41
+ | id | name | value |
42
+ +----+----------------+
43
+ | 0 | name1 | value1 |
44
+ | 1 | name2 | value2 |
45
+ | 2 | name3 | value3 |
46
+ +----+----------------+
47
+ </pre>
48
+
49
+ If you now do a bulk update:
50
+
51
+ values = [['name1', 'value1.1'], ['name2', 'value2'], ['name4', 'value4.1']]
52
+ Model.bulk_update columns, values, key: 'name'
53
+
54
+ You have now the following entries in your database:
55
+ <pre>
56
+ +----+------------------+
57
+ | id | name | value |
58
+ +----+------------------+
59
+ | 0 | name1 | value1.1 |
60
+ | 1 | name2 | value2 |
61
+ | 3 | name4 | value4.1 |
62
+ +----+------------------+
63
+ </pre>
64
+
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
71
+ 4. Push to the branch (`git push origin my-new-feature`)
72
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new('spec')
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/bulk_update/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Philip Kurmann"]
6
+ gem.email = ["philip@kman.ch"]
7
+ gem.description = %q{Updates a large amount of Records in a highly efficient way}
8
+ gem.summary = %q{Enhances Active Record with a method for bulk inserts and a method for bulk updates. Both merthods are used for inserting or updating large amount of Records.}
9
+ gem.homepage = ""
10
+
11
+ gem.add_dependency "activerecord"
12
+ gem.add_development_dependency "rspec"
13
+ gem.add_development_dependency "sqlite3"
14
+ gem.add_development_dependency "pry"
15
+
16
+ gem.files = `git ls-files`.split($\)
17
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.name = "bulk_update"
20
+ gem.require_paths = ["lib"]
21
+ gem.version = BulkUpdate::VERSION
22
+ end
@@ -0,0 +1,5 @@
1
+ require "bulk_update/version"
2
+ require "bulk_update/active_record_inflections"
3
+ require "active_record"
4
+
5
+ ActiveRecord::Base.send(:extend, BulkUpdate::ActiveRecordInflections)
@@ -0,0 +1,261 @@
1
+ module BulkUpdate
2
+ module ActiveRecordInflections
3
+ #
4
+ # Clone the database structure of a table
5
+ def clone_table args = {}
6
+ if args[:to]
7
+ case ActiveRecord::Base.connection_config[:adapter]
8
+ when 'sqlite3'
9
+ ActiveRecord::Base.connection.execute "CREATE TABLE `#{args[:to]}` AS SELECT * FROM `#{table_name}` LIMIT 1"
10
+ ActiveRecord::Base.connection.execute "DELETE FROM `#{args[:to]}`"
11
+ else
12
+ ActiveRecord::Base.connection.execute "CREATE TABLE `#{args[:to]}` LIKE `#{table_name}`"
13
+ end
14
+ end
15
+ end
16
+
17
+
18
+ def insert_str element
19
+ if element.class == Fixnum || element.class == Float
20
+ element
21
+ elsif element.class == NilClass
22
+ 'NULL'
23
+ else
24
+ if element.to_s[0] == '(' || element.to_s.downcase == 'true' || element.to_s.downcase == 'false'
25
+ element.to_s
26
+ else
27
+ "'#{element}'"
28
+ end
29
+ end
30
+ end
31
+
32
+
33
+ #
34
+ # Bulk insert records
35
+ def bulk_insert columns, values, args = {}
36
+ # Limit inserts
37
+ max_records_per_insert = args[:max_records_per_insert] || 100
38
+ table = args[:into] || table_name
39
+ columns = columns.clone
40
+
41
+ # Add timestamp
42
+ timestamp = Time.now
43
+ add_timestamp = false
44
+ add_updated_at = false
45
+ unless columns.map(&:to_sym).include?(:created_at)
46
+ columns << :created_at
47
+ add_created_at = true
48
+ end
49
+ unless columns.map(&:to_sym).include?(:updated_at)
50
+ columns << :updated_at
51
+ add_updated_at = true
52
+ end
53
+
54
+ if table_name
55
+ # Create header for insert with all column names
56
+ columns = columns.clone.map!{ |c| "`#{c}`" }
57
+ insert_head = "INSERT INTO `#{table}` (#{columns.join(', ')})"
58
+
59
+ # Create inserts
60
+ inserts = []
61
+ values.each do |values_per_record|
62
+ values_per_record = values_per_record.clone
63
+ values_per_record << timestamp if add_created_at
64
+ values_per_record << timestamp if add_updated_at
65
+ inserts << "(#{values_per_record.map{ |e| insert_str(e) }.join(', ')})"
66
+ if inserts.count > max_records_per_insert
67
+ ActiveRecord::Base.connection.execute "#{insert_head} VALUES #{inserts.join(', ')}"
68
+ inserts.clear
69
+ end
70
+ end
71
+ ActiveRecord::Base.connection.execute "#{insert_head} VALUES #{inserts.join(', ')}" unless inserts.empty?
72
+ end
73
+ end
74
+
75
+
76
+ #
77
+ # Create, update and delete Records according to a set of new values through ActiveRecord but optimized for performance by
78
+ # finding all diferences by SQL.
79
+ def bulk_update columns, values, args = {}
80
+ temp_table = "#{table_name}_temp_table_#{$$}"
81
+ key = args[:key] || args[:keys] || 'id'
82
+ condition = args[:condition]
83
+ exclude_fields = args[:exclude_fields]
84
+ insert = args[:insert].nil? ? true : args[:insert]
85
+ update = args[:update].nil? ? true : args[:update]
86
+ remove = args[:remove].nil? ? true : args[:remove]
87
+
88
+ # Clone temp-table and load it
89
+ clone_table to: temp_table
90
+ bulk_insert columns, values, into: temp_table
91
+
92
+ # Find differences and create, update and delete these through ActiveRecord to handle Callbacks, etc.
93
+ create(get_new_records for: self, compare_with: temp_table, on: key, condition: condition, exclude_fields: exclude_fields) if insert
94
+ if update
95
+ get_updated_records(for: self, compare_with: temp_table, on: key, condition: condition, exclude_virtual: args[:exclude_virtual], exclude_fields: exclude_fields).each do |id, new_attributes|
96
+ find(id).update_attributes new_attributes
97
+ end
98
+ end
99
+ destroy(get_deleted_records for: self, compare_with: temp_table, on: key, condition: condition, exclude_virtual: args[:exclude_virtual]) if remove
100
+
101
+ ensure
102
+ # Drop temp table
103
+ ActiveRecord::Base.connection.execute "DROP TABLE IF EXISTS #{temp_table}"
104
+ end
105
+
106
+
107
+ #
108
+ # Exclude List
109
+ def default_exclude
110
+ ['id', 'version', 'created_at', 'updated_at']
111
+ end
112
+
113
+
114
+ #
115
+ # Compare Table of args[:model] with its temporary table args[:compare_with] and return all new records as a Array of Hashes
116
+ def get_new_records args = {}
117
+ model = args[:for] || self
118
+ compare_table = args[:compare_with]
119
+ keys = args[:on] || 'id'
120
+ exclude = args[:exclude_fields] || []
121
+ exclude |= default_exclude
122
+
123
+ # Generate conditions for query and sub-query
124
+ conditions = []
125
+ conditions2 = []
126
+ conditions << "#{args[:condition].gsub('--tt--', compare_table)}" if args[:condition]
127
+ conditions2 << "#{args[:condition].gsub('--tt--', model.table_name)}" if args[:condition]
128
+ if keys.class == String || keys.class == Symbol
129
+ key = keys.to_s
130
+ else
131
+ key = keys[0].to_s
132
+ conditions2 << keys[1..-1].map{|k| "#{model.table_name}.#{k.to_s} = #{compare_table}.#{k.to_s}" }
133
+ end
134
+
135
+ # Generate and execute SQL-Statement
136
+ condition = conditions.join(' AND ')
137
+ condition2 = conditions2.join(' AND ')
138
+ sql = "SELECT * FROM #{compare_table} WHERE #{condition} #{'AND' unless conditions.blank?} #{compare_table}.#{key} NOT IN " +
139
+ "(SELECT #{key} FROM #{model.table_name} #{'WHERE' unless conditions2.blank?} #{condition2})"
140
+ results = ActiveRecord::Base.connection.execute sql
141
+
142
+ # Generate Array with Hashes of all new records
143
+ new_records = []
144
+ keys_to_log = []
145
+ results.each do |attributes|
146
+ new_records << result2hash(attributes, exclude)
147
+ keys_to_log << new_records.last[key.to_sym]
148
+ end
149
+ args[:logger].info "New Records for Model #{model.to_s}: #{keys_to_log.join(', ')}" unless keys_to_log.blank? || args[:logger].blank?
150
+ new_records
151
+ end
152
+
153
+
154
+ #
155
+ # Compare Table of args[:model] with its temporary table args[:compare_with] and return all updated records as a Hash of Hashes whose
156
+ # key is the ID of the changed record
157
+ def get_updated_records args = {}
158
+ model = args[:for] || self
159
+ compare_table = args[:compare_with]
160
+ keys = args[:on] || 'id'
161
+ exclude = args[:exclude_fields] || []
162
+ exclude |= default_exclude
163
+ exclude_virtual = args[:exclude_virtual].nil? ? false : args[:exclude_virtual]
164
+
165
+ # Generate conditions for query and sub-query
166
+ conditions = []
167
+ conditions2 = []
168
+ conditions << "NOT #{model.table_name}.virtual" if exclude_virtual
169
+ if keys.class == String || keys.class == Symbol
170
+ key = keys.to_s
171
+ conditions << "#{model.table_name}.#{key} = #{compare_table}.#{key}"
172
+ exclude |= [keys.to_s]
173
+ else
174
+ key = keys[0].to_s
175
+ conditions |= keys.map{|k| "#{model.table_name}.#{k.to_s} = #{compare_table}.#{k.to_s}" }
176
+ exclude |= keys.map(&:to_s)
177
+ end
178
+ conditions << "#{args[:condition].gsub('--tt--', model.table_name)} AND #{args[:condition].gsub('--tt--', compare_table)}" if args[:condition]
179
+ model.attribute_names.each do |an|
180
+ unless exclude.include?(an)
181
+ if ActiveRecord::Base.connection_config[:adapter] =~ /mysql?/
182
+ conditions2 << "NOT #{model.table_name}.#{an} <=> #{compare_table}.#{an}" unless exclude.include?(an)
183
+ else
184
+ conditions2 << "NOT (#{model.table_name}.#{an} = #{compare_table}.#{an} OR (#{model.table_name}.#{an} IS NULL AND #{compare_table}.#{an} IS NULL))"
185
+ end
186
+ end
187
+ end
188
+
189
+ # Generate and execute SQL-Statement
190
+ condition = "#{conditions.join(' AND ')} AND (#{conditions2.join(' OR ')})"
191
+ sql = "SELECT #{model.table_name}.id, #{compare_table}.* FROM #{model.table_name}, #{compare_table} WHERE #{condition}"
192
+ results = ActiveRecord::Base.connection.execute sql
193
+
194
+ # Generate Hash with id as the key and values as a Hashes of all changed records
195
+ results_hash = {}
196
+ keys_to_log = []
197
+ results.each do |attributes|
198
+ id = attributes[0]
199
+ results_hash[id] = result2hash attributes, exclude, 1
200
+ keys_to_log << (args[:debug] ? model.find(id).send(key) : id)
201
+ end
202
+ args[:logger].info "Change Records for Model #{model.to_s}: #{keys_to_log.join(', ')}" unless keys_to_log.blank? || args[:logger].blank?
203
+
204
+ results_hash
205
+ end
206
+
207
+
208
+ #
209
+ # Compare Table of args[:model] with its temporary table args[:compare_with] and return all deleted records as a Array of IDs
210
+ def get_deleted_records args = {}
211
+ model = args[:for] || self
212
+ compare_table = args[:compare_with]
213
+ keys = args[:on] || 'id'
214
+ exclude_virtual = args[:exclude_virtual].nil? ? false : args[:exclude_virtual]
215
+
216
+ # Generate conditions for query and sub-query
217
+ conditions = []
218
+ conditions2 = []
219
+ conditions << "NOT #{model.table_name}.virtual" if exclude_virtual
220
+ conditions << "#{args[:condition].gsub('--tt--', model.table_name)}" if args[:condition]
221
+ conditions2 << "#{args[:condition].gsub('--tt--', compare_table)}" if args[:condition]
222
+ if keys.class == String || keys.class == Symbol
223
+ key = keys.to_s
224
+ else
225
+ key = keys[0].to_s
226
+ conditions2 |= keys[1..-1].map{|k| "#{model.table_name}.#{k.to_s} = #{compare_table}.#{k.to_s}" }
227
+ end
228
+
229
+ # Generate and execute SQL-Statement
230
+ condition = conditions.join(' AND ') unless conditions.blank?
231
+ condition2 = conditions2.join(' AND ') unless conditions2.blank?
232
+ sql = "SELECT id, #{key} FROM #{model.table_name} WHERE #{condition} #{'AND' unless conditions.blank?} #{model.table_name}.#{key} NOT IN " +
233
+ "(SELECT #{key} FROM #{compare_table} #{'WHERE' unless conditions2.blank?} #{condition2})"
234
+ results = ActiveRecord::Base.connection.execute sql
235
+
236
+ # Generate Array with ids of all deleted records
237
+ deleted_records = []
238
+ keys_to_log = []
239
+ results.each do |attributes|
240
+ deleted_records << attributes[0]
241
+ keys_to_log << attributes[1]
242
+ end
243
+ args[:logger].info "Deleting Records from Model #{model.to_s}: #{keys_to_log.join(', ')}" unless keys_to_log.blank? || args[:logger].blank?
244
+ deleted_records
245
+ end
246
+
247
+
248
+ private
249
+
250
+
251
+ def result2hash attributes, exclude, attribute_nr = 0
252
+ hash = {}
253
+ attribute_names.each do |an|
254
+ hash[an.to_sym] = attributes[attribute_nr] unless exclude.include?(an)
255
+ attribute_nr += 1
256
+ end
257
+ hash
258
+ end
259
+
260
+ end
261
+ end
@@ -0,0 +1,3 @@
1
+ module BulkUpdate
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper.rb'
2
+
3
+ require 'pry'
4
+
5
+ describe BulkUpdate do
6
+ before :each do
7
+ @columns = [:name, :value]
8
+ @values = [['test1', 'value1'], ['test2', 'value2'], ['test3', 'value3'], ['test4', 'value4']]
9
+ MyHash.bulk_insert @columns, @values
10
+ end
11
+
12
+
13
+ it 'inserts multiple records in one SQL' do
14
+ MyHash.count.should be 4
15
+ end
16
+
17
+
18
+ it 'updates and deletes records' do
19
+ @values = [['test1', 'value1.1'], ['test2', 'value2'], ['test4', 'value4.4'], ['test5', 'value5.5']]
20
+ MyHash.bulk_update @columns, @values, key: 'name'
21
+ MyHash.count.should be 4
22
+ MyHash.where(name: 'test1').first.value.should eq 'value1.1'
23
+ MyHash.where(name: 'test2').first.value.should eq 'value2'
24
+ MyHash.where(name: 'test3').first.should be nil
25
+ MyHash.where(name: 'test4').first.value.should eq 'value4.4'
26
+ MyHash.where(name: 'test5').first.value.should eq 'value5.5'
27
+ end
28
+
29
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'support/active_record'
4
+ require 'support/my_hash'
5
+
6
+ require 'bulk_update'
7
+
8
+
9
+ RSpec.configure do |config|
10
+ config.color_enabled = true
11
+ config.formatter = 'documentation'
12
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_record'
2
+ require 'active_record/version'
3
+ puts "Testing ActiveRecord #{ActiveRecord::VERSION::STRING}"
4
+
5
+ ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
6
+ ActiveRecord::Migrator.up "db/migrate"
7
+
8
+ load 'spec/support/schema.rb'
9
+
10
+
11
+ module ActiveModel::Validations
12
+ # Extension to enhance `should have` on AR Model instances. Calls
13
+ # model.valid? in order to prepare the object's errors object.
14
+ #
15
+ # You can also use this to specify the content of the error messages.
16
+ #
17
+ # @example
18
+ #
19
+ # model.should have(:no).errors_on(:attribute)
20
+ # model.should have(1).error_on(:attribute)
21
+ # model.should have(n).errors_on(:attribute)
22
+ #
23
+ # model.errors_on(:attribute).should include("can't be blank")
24
+ def errors_on(attribute)
25
+ self.valid?
26
+ [self.errors[attribute]].flatten.compact
27
+ end
28
+ alias :error_on :errors_on
29
+ end
30
+
31
+
32
+ RSpec.configure do |config|
33
+ config.around do |example|
34
+ ActiveRecord::Base.transaction do
35
+ example.run
36
+ raise ActiveRecord::Rollback
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+ require 'active_record'
2
+
3
+ class MyHash < ActiveRecord::Base
4
+ end
@@ -0,0 +1,5 @@
1
+ ActiveRecord::Migration.create_table :my_hashes do |t|
2
+ t.string :name
3
+ t.string :value
4
+ t.timestamps
5
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bulk_update
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Philip Kurmann
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
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: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
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: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
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: pry
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
+ description: Updates a large amount of Records in a highly efficient way
79
+ email:
80
+ - philip@kman.ch
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE
88
+ - README.md
89
+ - Rakefile
90
+ - bulk_update.gemspec
91
+ - lib/bulk_update.rb
92
+ - lib/bulk_update/active_record_inflections.rb
93
+ - lib/bulk_update/version.rb
94
+ - spec/bulk_update_spec.rb
95
+ - spec/spec_helper.rb
96
+ - spec/support/active_record.rb
97
+ - spec/support/my_hash.rb
98
+ - spec/support/schema.rb
99
+ homepage: ''
100
+ licenses: []
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ none: false
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ required_rubygems_version: !ruby/object:Gem::Requirement
112
+ none: false
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 1.8.24
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Enhances Active Record with a method for bulk inserts and a method for bulk
123
+ updates. Both merthods are used for inserting or updating large amount of Records.
124
+ test_files:
125
+ - spec/bulk_update_spec.rb
126
+ - spec/spec_helper.rb
127
+ - spec/support/active_record.rb
128
+ - spec/support/my_hash.rb
129
+ - spec/support/schema.rb