active_record_data_loader 0.1.1 → 0.1.2

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
  SHA256:
3
- metadata.gz: fbc38165d9ffa4debdc127641a9e79c4fd3eb382dd9271c7e0eed849992c8c72
4
- data.tar.gz: d2703bbd36cdbcae163dbf19082704e39dc717499689d9754bc570d846895ada
3
+ metadata.gz: 43867d09da0479be24e9a23687d53cd4137f5ff8b47f769183a9bee19d6f8372
4
+ data.tar.gz: 71ea6ea8d1cbc9821168a0f87e90c7e6e08f2b2053c48905bb0bcfe95f5d37f0
5
5
  SHA512:
6
- metadata.gz: a999728b9926d7e87cb93fbf3329902becc4dd0b51139f38b473820010b93987ecb5e87be0be214111f28bd6c64685af6d2b09d61cc35cf4d3c41dd1fc88f026
7
- data.tar.gz: 1b6bcbf2edd88700ce91083ce7da1958ef314f65f5c57932cd978915e36603788696eb3f1cc5894f36dd6870d6d945ca9e72068e2ac05444f5992df099c4760a
6
+ metadata.gz: 2523c1db84ecd726ba333369646a0fe1e9b6ee8e0e0dc5694e2b8a8ef693fe752c8685914d122c7d9561dd05ff3c1d1905a328445a7407c82596b72cf0f5e9ff
7
+ data.tar.gz: 3c95553f3b39a535739e3fd998f67ca0c5420e37411d229b9284c0ccc99c9b7690d56a835b8fdaadfddc6f8750604f29852f77198b5a178c4cbab00bf3d80a1f
data/Appraisals CHANGED
@@ -4,10 +4,6 @@ appraise "activerecord-5" do
4
4
  gem "activerecord", "~> 5.0"
5
5
  end
6
6
 
7
- appraise "activerecord-6" do
8
- gem "activerecord", "6.0.0.rc1"
9
- end
10
-
11
7
  appraise "faker" do
12
8
  gem "faker", ">= 1.9.3"
13
9
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_record_data_loader (0.1.1)
4
+ active_record_data_loader (0.1.2)
5
5
  activerecord (>= 4.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # ActiveRecord Data Loader
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/active_record_data_loader.svg)](https://badge.fury.io/rb/active_record_data_loader)
3
4
  [![Build Status](https://travis-ci.org/abeiderman/active_record_data_loader.svg?branch=master)](https://travis-ci.org/abeiderman/active_record_data_loader)
4
- [![Coverage Status](https://coveralls.io/repos/github/abeiderman/active_record_data_loader/badge.svg?branch=master)](https://coveralls.io/github/abeiderman/active_record_data_loader?branch=master)
5
+ [![Coverage Status](https://coveralls.io/repos/github/abeiderman/active_record_data_loader/badge.svg?branch=master&service=github)](https://coveralls.io/github/abeiderman/active_record_data_loader?branch=master)
5
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/338904b3f7e8d19a3cb1/maintainability)](https://codeclimate.com/github/abeiderman/active_record_data_loader/maintainability)
6
7
 
7
8
  Efficiently bulk load data for your ActiveRecord models with a simple DSL.
data/Rakefile CHANGED
@@ -3,8 +3,10 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rspec/core/rake_task"
5
5
  require "rubocop/rake_task"
6
+ require "coveralls/rake/task"
6
7
 
7
8
  RSpec::Core::RakeTask.new(:spec)
8
9
  RuboCop::RakeTask.new(:rubocop)
10
+ Coveralls::RakeTask.new
9
11
 
10
12
  task default: [:spec, :rubocop]
@@ -11,6 +11,7 @@ require "active_record_data_loader/active_record/column_configuration"
11
11
  require "active_record_data_loader/active_record/belongs_to_configuration"
12
12
  require "active_record_data_loader/active_record/polymorphic_belongs_to_configuration"
13
13
  require "active_record_data_loader/active_record/model_data_generator"
14
+ require "active_record_data_loader/dsl/belongs_to_association"
14
15
  require "active_record_data_loader/dsl/polymorphic_association"
15
16
  require "active_record_data_loader/dsl/model"
16
17
  require "active_record_data_loader/dsl/definition"
@@ -45,14 +46,15 @@ module ActiveRecordDataLoader
45
46
  generator = ActiveRecordDataLoader::ActiveRecord::ModelDataGenerator.new(
46
47
  model: m.klass,
47
48
  column_settings: m.columns,
48
- polymorphic_settings: m.polymorphic_associations
49
+ polymorphic_settings: m.polymorphic_associations,
50
+ belongs_to_settings: m.belongs_to_associations
49
51
  )
50
52
 
51
53
  ActiveRecordDataLoader::Loader.load_data(
52
54
  data_generator: generator,
53
55
  batch_size: m.batch_size,
54
56
  total_rows: m.row_count,
55
- logger: configuration.logger
57
+ configuration: configuration
56
58
  )
57
59
  end
58
60
  end
@@ -3,14 +3,15 @@
3
3
  module ActiveRecordDataLoader
4
4
  module ActiveRecord
5
5
  class BelongsToConfiguration
6
- def self.config_for(ar_association:)
6
+ def self.config_for(ar_association:, query: nil)
7
7
  raise "#{name} does not support polymorphic associations" if ar_association.polymorphic?
8
8
 
9
- { ar_association.join_foreign_key.to_sym => new(ar_association).foreign_key_func }
9
+ { ar_association.join_foreign_key.to_sym => new(ar_association, query).foreign_key_func }
10
10
  end
11
11
 
12
- def initialize(ar_association)
12
+ def initialize(ar_association, query)
13
13
  @ar_association = ar_association
14
+ @query = query
14
15
  end
15
16
 
16
17
  def foreign_key_func
@@ -20,7 +21,15 @@ module ActiveRecordDataLoader
20
21
  private
21
22
 
22
23
  def possible_values
23
- @possible_values ||= @ar_association.klass.all.pluck(@ar_association.join_primary_key).to_a
24
+ @possible_values ||= base_query.pluck(@ar_association.join_primary_key).to_a
25
+ end
26
+
27
+ def base_query
28
+ if @query&.respond_to?(:call)
29
+ @query.call.all
30
+ else
31
+ @ar_association.klass.all
32
+ end
24
33
  end
25
34
  end
26
35
  end
@@ -5,11 +5,12 @@ module ActiveRecordDataLoader
5
5
  class ModelDataGenerator
6
6
  attr_reader :table
7
7
 
8
- def initialize(model:, column_settings:, polymorphic_settings: [])
8
+ def initialize(model:, column_settings:, polymorphic_settings: [], belongs_to_settings: [])
9
9
  @model_class = model
10
10
  @table = model.table_name
11
- @polymorphic_settings = polymorphic_settings
12
11
  @column_settings = column_settings
12
+ @polymorphic_settings = polymorphic_settings
13
+ @belongs_to_settings = belongs_to_settings.map { |s| [s.name, s.query] }.to_h
13
14
  end
14
15
 
15
16
  def column_list
@@ -58,7 +59,9 @@ module ActiveRecordDataLoader
58
59
  .reflect_on_all_associations
59
60
  .select(&:belongs_to?)
60
61
  .reject(&:polymorphic?)
61
- .map { |assoc| BelongsToConfiguration.config_for(ar_association: assoc) }
62
+ .map do |assoc|
63
+ BelongsToConfiguration.config_for(ar_association: assoc, query: @belongs_to_settings[assoc.name])
64
+ end
62
65
  .reduce({}, :merge)
63
66
  end
64
67
 
@@ -1,17 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "csv"
4
- require "benchmark"
5
-
6
3
  module ActiveRecordDataLoader
7
4
  class BulkInsertStrategy
8
5
  def initialize(data_generator)
9
6
  @data_generator = data_generator
10
7
  end
11
8
 
12
- def load_batch(row_numbers)
13
- connection = ::ActiveRecord::Base.connection
14
-
9
+ def load_batch(row_numbers, connection)
15
10
  connection.insert(<<~SQL)
16
11
  INSERT INTO #{quoted_table_name(connection)} (#{column_list(connection)})
17
12
  VALUES #{values(row_numbers, connection)}
@@ -2,16 +2,18 @@
2
2
 
3
3
  module ActiveRecordDataLoader
4
4
  class Configuration
5
- attr_accessor :default_batch_size, :default_row_count, :logger
5
+ attr_accessor :default_batch_size, :default_row_count, :logger, :statement_timeout
6
6
 
7
7
  def initialize(
8
8
  default_batch_size: 100_000,
9
9
  default_row_count: 1,
10
- logger: Logger.new(STDOUT, level: :info)
10
+ logger: Logger.new(STDOUT, level: :info),
11
+ statement_timeout: "2min"
11
12
  )
12
13
  @default_batch_size = default_batch_size
13
14
  @default_row_count = default_row_count
14
15
  @logger = logger
16
+ @statement_timeout = statement_timeout
15
17
  end
16
18
  end
17
19
  end
@@ -1,19 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "csv"
4
- require "benchmark"
5
-
6
3
  module ActiveRecordDataLoader
7
4
  class CopyStrategy
8
5
  def initialize(data_generator)
9
6
  @data_generator = data_generator
10
7
  end
11
8
 
12
- def load_batch(row_numbers)
13
- csv_data = csv_data_batch(row_numbers)
9
+ def load_batch(row_numbers, connection)
10
+ csv_data = csv_data_batch(row_numbers, connection)
14
11
 
15
- connection = ::ActiveRecord::Base.connection.raw_connection
16
- connection.copy_data(copy_command) { connection.put_copy_data(csv_data) }
12
+ raw_connection = connection.raw_connection
13
+ raw_connection.copy_data(copy_command(connection)) { raw_connection.put_copy_data(csv_data) }
17
14
  end
18
15
 
19
16
  def table_name
@@ -28,18 +25,18 @@ module ActiveRecordDataLoader
28
25
 
29
26
  attr_reader :data_generator
30
27
 
31
- def csv_data_batch(row_numbers)
28
+ def csv_data_batch(row_numbers, connection)
32
29
  row_numbers.map do |i|
33
- data_generator.generate_row(i).map { |d| quote_data(d) }.join(",")
30
+ data_generator.generate_row(i).map { |d| quote_data(d, connection) }.join(",")
34
31
  end.join("\n")
35
32
  end
36
33
 
37
- def copy_command
34
+ def copy_command(connection)
38
35
  @copy_command ||= begin
39
- quoted_table_name = ::ActiveRecord::Base.connection.quote_table_name(data_generator.table)
36
+ quoted_table_name = connection.quote_table_name(data_generator.table)
40
37
  columns = data_generator
41
38
  .column_list
42
- .map { |c| ::ActiveRecord::Base.connection.quote_column_name(c) }
39
+ .map { |c| connection.quote_column_name(c) }
43
40
  .join(", ")
44
41
 
45
42
  <<~SQL
@@ -49,10 +46,10 @@ module ActiveRecordDataLoader
49
46
  end
50
47
  end
51
48
 
52
- def quote_data(data)
49
+ def quote_data(data, connection)
53
50
  return if data.nil?
54
51
 
55
- "\"#{::ActiveRecord::Base.connection.quote_string(data.to_s)}\""
52
+ "\"#{connection.quote_string(data.to_s)}\""
56
53
  end
57
54
  end
58
55
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecordDataLoader
4
+ module Dsl
5
+ class BelongsToAssociation
6
+ attr_reader :model_class, :name, :query
7
+
8
+ def initialize(model_class, name, query = nil)
9
+ @model_class = model_class
10
+ @name = name
11
+ @query = query
12
+ end
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,7 @@
3
3
  module ActiveRecordDataLoader
4
4
  module Dsl
5
5
  class Model
6
- attr_reader :klass, :columns, :row_count, :polymorphic_associations
6
+ attr_reader :klass, :columns, :row_count, :polymorphic_associations, :belongs_to_associations
7
7
 
8
8
  def initialize(klass:, configuration:)
9
9
  @klass = klass
@@ -11,6 +11,7 @@ module ActiveRecordDataLoader
11
11
  @row_count = configuration.default_row_count
12
12
  @batch_size = configuration.default_batch_size
13
13
  @polymorphic_associations = []
14
+ @belongs_to_associations = []
14
15
  end
15
16
 
16
17
  def count(count)
@@ -30,6 +31,10 @@ module ActiveRecordDataLoader
30
31
  @klass, assoc_name
31
32
  ).tap { |a| block.call(a) }
32
33
  end
34
+
35
+ def belongs_to(assoc_name, eligible_set:)
36
+ @belongs_to_associations << BelongsToAssociation.new(@klass, assoc_name, eligible_set)
37
+ end
33
38
  end
34
39
  end
35
40
  end
@@ -9,10 +9,11 @@ module ActiveRecordDataLoader
9
9
  data_generator:,
10
10
  total_rows:,
11
11
  batch_size:,
12
- logger:
12
+ configuration:
13
13
  )
14
14
  new(
15
- logger: logger,
15
+ logger: configuration.logger,
16
+ statement_timeout: configuration.statement_timeout,
16
17
  strategy: strategy_class.new(data_generator)
17
18
  ).load_data(batch_size, total_rows)
18
19
  end
@@ -28,9 +29,10 @@ module ActiveRecordDataLoader
28
29
  end
29
30
  end
30
31
 
31
- def initialize(logger:, strategy:)
32
+ def initialize(logger:, statement_timeout:, strategy:)
32
33
  @logger = logger
33
34
  @strategy = strategy
35
+ @statement_timeout = statement_timeout
34
36
  end
35
37
 
36
38
  def load_data(batch_size, total_rows)
@@ -51,16 +53,37 @@ module ActiveRecordDataLoader
51
53
 
52
54
  private
53
55
 
54
- attr_reader :strategy, :logger
56
+ attr_reader :strategy, :statement_timeout, :logger
55
57
 
56
58
  def load_in_batches(batch_size, total_rows, batch_count)
57
- total_rows.times.each_slice(batch_size).with_index do |row_numbers, i|
58
- time = Benchmark.realtime { strategy.load_batch(row_numbers) }
59
+ with_connection do |connection|
60
+ total_rows.times.each_slice(batch_size).with_index do |row_numbers, i|
61
+ time = Benchmark.realtime { strategy.load_batch(row_numbers, connection) }
59
62
 
60
- logger.debug(
61
- "Completed batch #{i + 1}/#{batch_count}, #{row_numbers.count} row(s) in #{time} seconds"
62
- )
63
+ logger.debug(
64
+ "Completed batch #{i + 1}/#{batch_count}, #{row_numbers.count} row(s) in #{time} seconds"
65
+ )
66
+ end
67
+ end
68
+ end
69
+
70
+ def with_connection
71
+ if ::ActiveRecord::Base.connection.adapter_name.downcase.to_sym == :postgresql
72
+ original_timeout = retrieve_statement_timeout
73
+ update_statement_timeout(statement_timeout)
74
+ yield ::ActiveRecord::Base.connection
75
+ update_statement_timeout(original_timeout)
76
+ else
77
+ yield ::ActiveRecord::Base.connection
63
78
  end
64
79
  end
80
+
81
+ def retrieve_statement_timeout
82
+ ::ActiveRecord::Base.connection.execute("SHOW statement_timeout").first["statement_timeout"]
83
+ end
84
+
85
+ def update_statement_timeout(timeout)
86
+ ::ActiveRecord::Base.connection.execute("SET statement_timeout = \"#{timeout}\"")
87
+ end
65
88
  end
66
89
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordDataLoader
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
@@ -1,6 +1,4 @@
1
1
  #! /bin/bash
2
2
  bundle exec appraisal
3
- bundle exec appraisal activerecord-5 rake
4
- bundle exec appraisal faker rake
5
- bundle exec appraisal ffaker rake
6
- bundle exec rake
3
+ bundle exec appraisal rake
4
+ bundle exec rake coveralls:push
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_data_loader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alejandro Beiderman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-10 00:00:00.000000000 Z
11
+ date: 2019-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -206,6 +206,7 @@ files:
206
206
  - lib/active_record_data_loader/configuration.rb
207
207
  - lib/active_record_data_loader/copy_strategy.rb
208
208
  - lib/active_record_data_loader/data_faker.rb
209
+ - lib/active_record_data_loader/dsl/belongs_to_association.rb
209
210
  - lib/active_record_data_loader/dsl/definition.rb
210
211
  - lib/active_record_data_loader/dsl/model.rb
211
212
  - lib/active_record_data_loader/dsl/polymorphic_association.rb