occams-record 1.0.0.rc10 → 1.0.0.rc11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0cdafe04c023de8a937ebef5fb1c274459e11b10c06f7fec38cf5632b93c969
4
- data.tar.gz: f98a82b06655ddaf71bcc68f171b3aa077b08f64bdfca9f35725e8fe2ad0c4f3
3
+ metadata.gz: a507ce21c11a7303250948fdcb29fdf9e6422968609b4915bfcd6800dfe7789c
4
+ data.tar.gz: b4fab3a88d28a742fed134800dde46c2919e400f04e93d427871dff349e36e76
5
5
  SHA512:
6
- metadata.gz: dfe0c1a6f0c18be9e37c17b5928a4911e62615cc95e5399b3d181b75d769bdf803c13823773d285daab4602f526c397d15f3d0f9dfa62fa9970539e07028a9e7
7
- data.tar.gz: e606aee2bae1577a3d77649979efa76c32b30b40614d0bbd4acb5f19ea1f453c16175e466a9afbad6997112c1cb12a64b6b890dd929cafbee039d2e1cbdb665d
6
+ metadata.gz: df7f2eb7c4f0181c8ad6d0f6b8a9d9c40bc23132578768dece1cd13fbc064b42db32678908a5701eb5493955035fb7bef9fae0cabc236e041dde4213f9594191
7
+ data.tar.gz: ac478403eb241853853465c1002f3ba804cbe0bb942f3379a42c0df5252852f1f959f3a9d7082d110536d92171d853656c8c273a724ff33f0274960d78d107c3
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ### Occams Record [![Build Status](https://travis-ci.org/jhollinger/occams-record.svg?branch=master)](https://travis-ci.org/jhollinger/occams-record)
1
+ # Occams Record [![Build Status](https://travis-ci.org/jhollinger/occams-record.svg?branch=master)](https://travis-ci.org/jhollinger/occams-record)
2
2
 
3
3
  > Do not multiply entities beyond necessity. -- Occam's Razor
4
4
 
@@ -259,3 +259,11 @@ bundle exec rake test
259
259
  ```
260
260
 
261
261
  Look inside `Gemfile` to see all testable versions.
262
+
263
+ # License
264
+
265
+ MIT License. See LICENSE for details.
266
+
267
+ # Copyright
268
+
269
+ Copywrite (c) 2019 Jordan Hollinger.
@@ -1,6 +1,7 @@
1
1
  require 'active_record'
2
2
  require 'occams-record/version'
3
3
  require 'occams-record/merge'
4
+ require 'occams-record/measureable'
4
5
  require 'occams-record/eager_loaders'
5
6
  require 'occams-record/results/results'
6
7
  require 'occams-record/results/row'
@@ -35,11 +35,11 @@ module OccamsRecord
35
35
  # @param rows [Array<OccamsRecord::Results::Row>] Array of rows used to calculate the query.
36
36
  # @param query_logger [Array<String>]
37
37
  #
38
- def run(rows, query_logger: nil)
38
+ def run(rows, query_logger: nil, measurements: nil)
39
39
  fkey_binds = calc_fkey_binds rows
40
40
  assoc = if fkey_binds.any? { |_, vals| vals.any? }
41
41
  binds = @binds.merge(fkey_binds)
42
- RawQuery.new(@sql, binds, use: @use, eager_loaders: @eager_loaders, query_logger: query_logger).run
42
+ RawQuery.new(@sql, binds, use: @use, eager_loaders: @eager_loaders, query_logger: query_logger, measurements: measurements).run
43
43
  else
44
44
  []
45
45
  end
@@ -33,9 +33,9 @@ module OccamsRecord
33
33
  # @param rows [Array<OccamsRecord::Results::Row>] Array of rows used to calculate the query.
34
34
  # @param query_logger [Array<String>]
35
35
  #
36
- def run(rows, query_logger: nil)
36
+ def run(rows, query_logger: nil, measurements: nil)
37
37
  query(rows) { |*args|
38
- assoc_rows = args[0] ? Query.new(args[0], use: @use, eager_loaders: @eager_loaders, query_logger: query_logger).run : []
38
+ assoc_rows = args[0] ? Query.new(args[0], use: @use, eager_loaders: @eager_loaders, query_logger: query_logger, measurements: measurements).run : []
39
39
  merge! assoc_rows, rows, *args[1..-1]
40
40
  }
41
41
  nil
@@ -103,10 +103,10 @@ module OccamsRecord
103
103
  # @param rows [Array<ActiveRecord::Base>] the parent rows to load child rows into
104
104
  # @param query_logger [Array] optional query logger
105
105
  #
106
- def run!(rows, query_logger: nil)
106
+ def run!(rows, query_logger: nil, measurements: nil)
107
107
  raise "Cannot run eager loaders when @model has not been set!" if @dynamic_loaders.any? and @model.nil?
108
108
  @loaders.each { |loader|
109
- loader.run(rows, query_logger: query_logger)
109
+ loader.run(rows, query_logger: query_logger, measurements: measurements)
110
110
  }
111
111
  nil
112
112
  end
@@ -31,11 +31,11 @@ module OccamsRecord
31
31
  # @param rows [Array<OccamsRecord::Results::Row>] Array of rows used to calculate the query.
32
32
  # @param query_logger [Array<String>]
33
33
  #
34
- def run(rows, query_logger: nil)
34
+ def run(rows, query_logger: nil, measurements: nil)
35
35
  query(rows) { |scope|
36
36
  eager_loaders = @eager_loaders.dup
37
37
  eager_loaders.model = scope.klass
38
- assoc_rows = Query.new(scope, use: @use, eager_loaders: eager_loaders, query_logger: query_logger).run
38
+ assoc_rows = Query.new(scope, use: @use, eager_loaders: eager_loaders, query_logger: query_logger, measurements: measurements).run
39
39
  merge! assoc_rows, rows
40
40
  }
41
41
  nil
@@ -36,8 +36,8 @@ module OccamsRecord
36
36
  @loader.name
37
37
  end
38
38
 
39
- def run(rows, query_logger: nil)
40
- @loader.run(rows, query_logger: query_logger)
39
+ def run(rows, query_logger: nil, measurements: nil)
40
+ @loader.run(rows, query_logger: query_logger, measurements: measurements)
41
41
  attr_set = "#{name}="
42
42
  rows.each do |row|
43
43
  row.send(attr_set, reduce(row))
@@ -0,0 +1,63 @@
1
+ module OccamsRecord
2
+ Measurements = Struct.new(:total_time, :queries)
3
+ Measurement = Struct.new(:table_name, :sql, :time)
4
+
5
+ #
6
+ # Measure the time each query takes. Useful for figuring out which query is the slow one when you're doing a bunch of eager loads.
7
+ #
8
+ # orders = OccamsRecord.
9
+ # query(Order.all).
10
+ # eager_load(:customer).
11
+ # ...
12
+ # measure { |x|
13
+ # puts "Total time: #{x.total_time} sec"
14
+ # x.queries.each { |q|
15
+ # puts "Table: #{q.table_name} (#{q.time} sec)"
16
+ # puts q.sql
17
+ # }
18
+ # }.
19
+ # run
20
+ #
21
+ module Measureable
22
+ #
23
+ # Track the run time of each query, and the total run time of everything combined.
24
+ #
25
+ # @yield [OccamsRecord::Measurements]
26
+ # @return self
27
+ #
28
+ def measure(&block)
29
+ @measurements ||= []
30
+ @measurement_results_block = block
31
+ self
32
+ end
33
+
34
+ private
35
+
36
+ def measure?
37
+ !@measurements.nil?
38
+ end
39
+
40
+ def measure!(table_name, sql)
41
+ result = nil
42
+ time = Benchmark.realtime { result = yield }
43
+ @measurements << Measurement.new(table_name, sql, time)
44
+ result
45
+ end
46
+
47
+ def record_start_time!
48
+ @start_time = Time.now if top_level_measurer?
49
+ end
50
+
51
+ def yield_measurements!
52
+ if top_level_measurer?
53
+ total_time = Time.now - @start_time
54
+ measurements = Measurements.new(total_time, @measurements.sort_by(&:time))
55
+ @measurement_results_block.call(measurements)
56
+ end
57
+ end
58
+
59
+ def top_level_measurer?
60
+ defined?(@measurement_results_block) && !@measurement_results_block.nil?
61
+ end
62
+ end
63
+ end
@@ -39,6 +39,7 @@ module OccamsRecord
39
39
  include Batches
40
40
  include EagerLoaders::Builder
41
41
  include Enumerable
42
+ include Measureable
42
43
 
43
44
  #
44
45
  # Initialize a new query.
@@ -47,13 +48,14 @@ module OccamsRecord
47
48
  # @param use [Array<Module>] optional Module to include in the result class (single or array)
48
49
  # @param query_logger [Array] (optional) an array into which all queries will be inserted for logging/debug purposes
49
50
  # @param eager_loaders [OccamsRecord::EagerLoaders::Context]
51
+ # @param measurements [Array]
50
52
  #
51
- def initialize(scope, use: nil, eager_loaders: nil, query_logger: nil)
53
+ def initialize(scope, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil)
52
54
  @model = scope.klass
53
55
  @scope = scope
54
56
  @eager_loaders = eager_loaders || EagerLoaders::Context.new(@model)
55
57
  @use = use
56
- @query_logger = query_logger
58
+ @query_logger, @measurements = query_logger, measurements
57
59
  end
58
60
 
59
61
  #
@@ -93,10 +95,18 @@ module OccamsRecord
93
95
  def run
94
96
  sql = block_given? ? yield(scope).to_sql : scope.to_sql
95
97
  @query_logger << sql if @query_logger
96
- result = model.connection.exec_query sql
98
+ result = if measure?
99
+ record_start_time!
100
+ measure!(model.table_name, sql) {
101
+ model.connection.exec_query sql
102
+ }
103
+ else
104
+ model.connection.exec_query sql
105
+ end
97
106
  row_class = OccamsRecord::Results.klass(result.columns, result.column_types, @eager_loaders.names, model: model, modules: @use)
98
107
  rows = result.rows.map { |row| row_class.new row }
99
- @eager_loaders.run!(rows, query_logger: @query_logger)
108
+ @eager_loaders.run!(rows, query_logger: @query_logger, measurements: @measurements)
109
+ yield_measurements!
100
110
  rows
101
111
  end
102
112
 
@@ -53,6 +53,7 @@ module OccamsRecord
53
53
  include Batches
54
54
  include EagerLoaders::Builder
55
55
  include Enumerable
56
+ include Measureable
56
57
 
57
58
  #
58
59
  # Initialize a new query.
@@ -62,13 +63,14 @@ module OccamsRecord
62
63
  # @param use [Array<Module>] optional Module to include in the result class (single or array)
63
64
  # @param eager_loaders [OccamsRecord::EagerLoaders::Context]
64
65
  # @param query_logger [Array] (optional) an array into which all queries will be inserted for logging/debug purposes
66
+ # @param measurements [Array]
65
67
  #
66
- def initialize(sql, binds, use: nil, eager_loaders: nil, query_logger: nil)
68
+ def initialize(sql, binds, use: nil, eager_loaders: nil, query_logger: nil, measurements: nil)
67
69
  @sql = sql
68
70
  @binds = binds
69
71
  @use = use
70
72
  @eager_loaders = eager_loaders || EagerLoaders::Context.new
71
- @query_logger = query_logger
73
+ @query_logger, @measurements = query_logger, measurements
72
74
  @conn = @eager_loaders.model&.connection || ActiveRecord::Base.connection
73
75
  end
74
76
 
@@ -95,10 +97,18 @@ module OccamsRecord
95
97
  def run
96
98
  _escaped_sql = escaped_sql
97
99
  @query_logger << _escaped_sql if @query_logger
98
- result = @conn.exec_query _escaped_sql
100
+ result = if measure?
101
+ record_start_time!
102
+ measure!(table_name, _escaped_sql) {
103
+ @conn.exec_query _escaped_sql
104
+ }
105
+ else
106
+ @conn.exec_query _escaped_sql
107
+ end
99
108
  row_class = OccamsRecord::Results.klass(result.columns, result.column_types, @eager_loaders.names, model: @eager_loaders.model, modules: @use)
100
109
  rows = result.rows.map { |row| row_class.new row }
101
- @eager_loaders.run!(rows, query_logger: @query_logger)
110
+ @eager_loaders.run!(rows, query_logger: @query_logger, measurements: @measurements)
111
+ yield_measurements!
102
112
  rows
103
113
  end
104
114
 
@@ -123,6 +133,7 @@ module OccamsRecord
123
133
 
124
134
  # Returns the SQL as a String with all variables escaped
125
135
  def escaped_sql
136
+ return sql if binds.empty?
126
137
  sql % binds.reduce({}) { |a, (col, val)|
127
138
  a[col.to_sym] = if val.is_a? Array
128
139
  val.map { |x| @conn.quote x }.join(', ')
@@ -133,6 +144,10 @@ module OccamsRecord
133
144
  }
134
145
  end
135
146
 
147
+ def table_name
148
+ @sql.match(/\s+FROM\s+"?(\w+)"?/i)&.captures&.first
149
+ end
150
+
136
151
  #
137
152
  # Returns an Enumerator that yields batches of records, of size "of".
138
153
  # The SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}'.
@@ -3,5 +3,5 @@
3
3
  #
4
4
  module OccamsRecord
5
5
  # Library version
6
- VERSION = '1.0.0.rc10'.freeze
6
+ VERSION = '1.0.0.rc11'.freeze
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: occams-record
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.rc10
4
+ version: 1.0.0.rc11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-31 00:00:00.000000000 Z
11
+ date: 2019-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -54,6 +54,7 @@ files:
54
54
  - lib/occams-record/eager_loaders/polymorphic_belongs_to.rb
55
55
  - lib/occams-record/eager_loaders/through.rb
56
56
  - lib/occams-record/errors.rb
57
+ - lib/occams-record/measureable.rb
57
58
  - lib/occams-record/merge.rb
58
59
  - lib/occams-record/query.rb
59
60
  - lib/occams-record/raw_query.rb