occams-record 0.10.0 → 0.11.0

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
- SHA1:
3
- metadata.gz: 9c305e7ef50172b06abc65501fda3a2e323a4b59
4
- data.tar.gz: 8b3148052f587c1987485527473e99764132130a
2
+ SHA256:
3
+ metadata.gz: 68db1cbd50ea9da8301200e9d57ca2517fe28624663c3d3ea2e463c99825f8d4
4
+ data.tar.gz: 4d55fddeaf26d1b2bc6b84ca09ec1d7a6d4ea23e5847b537c98f9ff5827bbc24
5
5
  SHA512:
6
- metadata.gz: 4907ef959db18d068d58007f6f1df7d2aac60ce69cf1b9af4c91e89c518b8b2eb103d5574b6d03a34964a1456c62f18b372e0c50595a1bc9c2cb3b9a967afbbe
7
- data.tar.gz: 39cbe4f0a8bb3af17944aba9d6c7839a6751b9b19053adbceb8df924815718d9c9dfaa596733bb63f1b3fb873815059c79264127d99b31a9621ffa69bdb99d4e
6
+ metadata.gz: 5924cf19d1fa6923146b289a661d80a964aa2616bc09768e8967dbe3a849349bb7aaf6c6edcb9fbe9301bf88cbad6f5a6dfb7397f8287c61b332d316197a18cb
7
+ data.tar.gz: f186dbeb9ea6f15cc4eebcb5cb2e6b135e5ba5b967c12e1162c938253f22fda343b3e2cc0401b93ba591fa94ebd695e6535e3a1ea8b4447aba11759dda549d28
data/README.md CHANGED
@@ -2,28 +2,32 @@
2
2
 
3
3
  > Do not multiply entities beyond necessity. -- Occam's Razor
4
4
 
5
- Occam's Record is a high-efficiency query API for ActiveRecord. When loading thousands of records, ActiveRecord wastes a lot of RAM and CPU cycles on *things you'll never use.* Additionally, eagerly-loaded associations are forced to load each and every column, each and every record, and all in a certain order.
5
+ Occam's Record is a high-efficiency query API for ActiveRecord. It is **not** an ORM or an ActiveRecord replacement. Use it to solve pain points in your existing ActiveRecord app.
6
6
 
7
- For those stuck with ActiveRecord, OccamsRecord seeks to solve these issues by making some very specific trade-offs:
7
+ * 3x-5x faster than ActiveRecord.
8
+ * Uses 1/3 the memory of ActiveRecord.
9
+ * Eliminates the N+1 query problem.
10
+ * Allows custom SQL when eager loading associations (use `select`, `where`, `order`, etc).
11
+ * `find_each`/`find_in_batches` respects `order` and `limit`.
12
+ * Allows eager loading of associations when using raw SQL.
13
+ * Allows `find_each`/`find_in_batches` when using raw SQL.
8
14
 
9
- * OccamsRecord results are **read-only**.
10
- * OccamsRecord objects are **purely database rows** - they don't have any instance methods from your Rails models.
11
- * OccamsRecord queries must specify each association that will be used. Otherwise they simply won't be availble.
12
-
13
- For more on the rational behind OccamsRecord, see the Rational section at the end of the README. But in short, OccamsRecord is 3x-5x faster, uses 1/3 of the memory, and eliminates the N+1 query problem.
15
+ [Look over the speed and memory measurements yourself!](https://github.com/jhollinger/occams-record/wiki/Measurements). OccamsRecord achieves all of this by making some very specific trade-offs:
14
16
 
15
- **BREAKING CHANGE** to `eager_load` in version **0.10.0**. See the examples below or [HISTORY.md](https://github.com/jhollinger/occams-record/blob/v0.10.0/HISTORY.md) for the new usage.
17
+ * OccamsRecord results are **read-only**.
18
+ * OccamsRecord results are **purely database rows** - they don't have any instance methods from your Rails models.
19
+ * OccamsRecord queries must eager load each association that will be used. Otherwise they simply won't be availble.
16
20
 
17
21
  ## Usage
18
22
 
23
+ Full documentation is available at [rubydoc.info/gems/occams-record](http://www.rubydoc.info/gems/occams-record).
24
+
19
25
  **Add to your Gemfile**
20
26
 
21
27
  ```ruby
22
28
  gem 'occams-record'
23
29
  ```
24
30
 
25
- Full documentation is available at [rubydoc.info/gems/occams-record](http://www.rubydoc.info/gems/occams-record).
26
-
27
31
  **Simple example**
28
32
 
29
33
  ```ruby
@@ -62,7 +66,7 @@ widgets[1].splines.map { |s| s.description }
62
66
 
63
67
  **An insane example, but only half as insane as the one that prompted the creation of this library**
64
68
 
65
- Here we're eager loading several levels down. Notice the `Proc` given to `eager_load(:orders)`. The `select:` option is just for convenience; you may instead pass a `Proc` and customize the query with any of ActiveRecord's query builder helpers (`select`, `where`, `order`, etc).
69
+ Here we're eager loading several levels down. Notice the `Proc` given to `eager_load(:orders)`. The `select:` option is just for convenience; you may instead pass a `Proc` and customize the query with any of ActiveRecord's query builder helpers (`select`, `where`, `order`, `limit`, etc).
66
70
 
67
71
  ```ruby
68
72
  widgets = OccamsRecord.
@@ -118,14 +122,14 @@ widgets[0].expensive?
118
122
  => false
119
123
 
120
124
  widgets[0].orders[0].description
121
- => "O839SJZ98B 1/8/2017"
125
+ => "O839SJZ98B - 1/8/2017"
122
126
  ```
123
127
 
124
128
  ## Raw SQL queries
125
129
 
126
- If you have a complicated query to run, you may drop down to hand-written SQL while still taking advantage of eager loading and variable escaping. (Note the slightly different syntax for binding variables.)
130
+ If you have a complicated query to run, you may drop down to hand-written SQL while still taking advantage of eager loading and variable escaping (not possible in ActiveRecord). Note the slightly different syntax for binding variables.
127
131
 
128
- NOTE this feature is quite new and might have some bugs. Issues and Pull Requests welcome.
132
+ NOTE this feature is quite new and might have some bugs. Since we are not yet at 1.0, breaking changes may occur. Issues and Pull Requests welcome.
129
133
 
130
134
  ```ruby
131
135
  widgets = OccamsRecord.sql(%(
@@ -151,30 +155,20 @@ widgets = OccamsRecord.
151
155
  run
152
156
  ```
153
157
 
154
- ## Rational
155
-
156
- **What does OccamsRecord buy you?**
157
-
158
- * OccamsRecord results are **one-third the size** of ActiveRecord results.
159
- * OccamsRecord queries run **three to five times faster** than ActiveRecord queries.
160
- * When eager loading associations you may specify which columns to `SELECT`. (This can be a significant performance boost to both your database and Rails app, on top of the above numbers.)
161
- * When eager loading associations you may completely customize the query (`WHERE`, `ORDER BY`, `LIMIT`, etc.)
162
- * By forcing eager loading of associations, OccamsRecord bypasses the primary cause of performance problems in Rails: N+1 queries.
163
- * Forced eager loading also makes you consider the "shape" of your data, which can help you identify areas that need refactored (e.g. redundant foreign keys, more denormalization, etc.)
164
-
165
- **What don't you give up?**
166
-
167
- * You can still write your queries using ActiveRecord's query builder, as well as your existing models' associations & scopes.
168
- * You can still use ActiveRecord for everything else - small queries, creating, updating, and deleting records.
169
- * You can still inject some instance methods into your results, if you must. See below.
158
+ To use `find_each` or `find_in_batches` with raw SQL you must provide the `LIMIT` and `OFFSET` statements yourself.
170
159
 
171
- **Is there evidence to back any of this up?**
172
-
173
- Glad you asked. [Look over the results yourself.](https://github.com/jhollinger/occams-record/wiki/Measurements)
174
-
175
- **Why not use a different ORM?**
176
-
177
- That's a great idea; check out [sequel](https://rubygems.org/gems/sequel) or [rom](https://rubygems.org/gems/rom)! But for large, legacy codebases heavily invested in ActiveRecord, switching ORMs often isn't practical. OccamsRecord can help you get some of those wins without a rewrite.
160
+ ```ruby
161
+ widgets = OccamsRecord.sql(%(
162
+ SELECT * FROM widgets
163
+ WHERE category_id = %{cat_id}
164
+ LIMIT %{batch_limit}
165
+ OFFSET %{batch_offset}
166
+ ), {
167
+ cat_id: 5
168
+ }).find_each { |widget|
169
+ puts widget.name
170
+ }
171
+ ```
178
172
 
179
173
  ## Unsupported features
180
174
 
@@ -12,7 +12,7 @@ module OccamsRecord
12
12
  return if rows.empty?
13
13
  ids = rows.map { |r| r.send @ref.active_record_primary_key }.compact.uniq
14
14
  q = base_scope.where(@ref.foreign_key => ids)
15
- q.where!(@ref.type => rows[0].class.try!(:model_name)) if @ref.options[:as]
15
+ q.where!(@ref.type => rows[0].class&.model_name) if @ref.options[:as]
16
16
  yield q
17
17
  end
18
18
 
@@ -25,6 +25,10 @@ module OccamsRecord
25
25
  # eager_load(:category).
26
26
  # run
27
27
  #
28
+ # NOTE To use find_each/find_in_batches, your SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}',
29
+ # and an ORDER BY is strongly recomended.
30
+ # OccamsRecord will provide the bind values for you.
31
+ #
28
32
  # @param sql [String] The SELECT statement to run. Binds should use Ruby's named string substitution.
29
33
  # @param binds [Hash] Bind values (Symbol keys)
30
34
  # @param use [Array<Module>] optional Module to include in the result class (single or array)
@@ -46,6 +50,7 @@ module OccamsRecord
46
50
  attr_reader :binds
47
51
 
48
52
  include EagerLoaders::Builder
53
+ include Batches
49
54
 
50
55
  #
51
56
  # Initialize a new query.
@@ -133,5 +138,33 @@ module OccamsRecord
133
138
  a
134
139
  }
135
140
  end
141
+
142
+ #
143
+ # Returns an Enumerator that yields batches of records, of size "of".
144
+ # The SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}'.
145
+ # The bind values will be provided by OccamsRecord.
146
+ #
147
+ # @param of [Integer] batch size
148
+ # @return [Enumerator] yields batches
149
+ #
150
+ def batches(of:)
151
+ unless @sql =~ /LIMIT\s+%\{batch_limit\}/i and @sql =~ /OFFSET\s+%\{batch_offset\}/i
152
+ raise ArgumentError, "When using find_each/find_in_batches you must specify 'LIMIT %{batch_limit} OFFSET %{batch_offset}'. SQL statement: #{@sql}"
153
+ end
154
+
155
+ Enumerator.new do |y|
156
+ offset = 0
157
+ loop do
158
+ results = RawQuery.new(@sql, @binds.merge({
159
+ batch_limit: of,
160
+ batch_offset: offset,
161
+ }), use: @use, query_logger: @query_logger, eager_loaders: @eager_loaders).model(@model).run
162
+
163
+ y.yield results if results.any?
164
+ break if results.size < of
165
+ offset += results.size
166
+ end
167
+ end
168
+ end
136
169
  end
137
170
  end
@@ -3,5 +3,5 @@
3
3
  #
4
4
  module OccamsRecord
5
5
  # Library version
6
- VERSION = '0.10.0'.freeze
6
+ VERSION = '0.11.0'.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: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-21 00:00:00.000000000 Z
11
+ date: 2018-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -72,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
72
  version: '0'
73
73
  requirements: []
74
74
  rubyforge_project:
75
- rubygems_version: 2.5.2
75
+ rubygems_version: 2.7.3
76
76
  signing_key:
77
77
  specification_version: 4
78
78
  summary: The missing high-efficiency query API for ActiveRecord