occams-record 1.0.0.rc10 → 1.0.0.rc11

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: 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