occams-record 1.2.1 → 1.3.0.rc1

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: a1e01657287159a2aa1a27ff66c1932ae83235ad0140dbaafd58103862ddaa8f
4
- data.tar.gz: ec9530c3844cd79739d1a2387031654e85f4a2a127d948df0c537431aa235ed5
3
+ metadata.gz: dd827ce90c189d89558863ece800e233c3296f590f18e6b756d4f4983c673e7e
4
+ data.tar.gz: 0bfd98422a9e2a313aae5faef338e442979235377ba1ddad9f5b38e2ccee1b27
5
5
  SHA512:
6
- metadata.gz: a0303147fa894e42d0642020a4a207c67b3a79fa3fc2dfa1b9be819224c8eee6bb261bfa2079a073cd1894f744b4995f307afff594f458c5b7ae7574b26b0d32
7
- data.tar.gz: bf81dc3eeb78cbf9ed99740a1c4c8e1c71287f4af278c2a623d9220384460f952ccc50fa835393ef3fd416fa0019fccc6a6e41d0c8dbd77c637f08689ca5c585
6
+ metadata.gz: 00352ecd359c2cd617f29d225aa3a566eed578a81f875714896b0e380931c9e5611b87971f2e68197225a59ebab5e7cefb96f65f4855f08481ca38b02d9f9c8c
7
+ data.tar.gz: 6f82a98e4790c92d86d44f4ea2e4babd8dde300a08a60106d8e1f8b0e1e0210351ff89915d1f56e44160779b358df8e8d4355ab69f14f81ee23d91e13f033f71
data/README.md CHANGED
@@ -17,17 +17,17 @@ Continue using ActiveRecord's query builder, but let Occams take over running th
17
17
  **Customize the SQL used to eager load associations**
18
18
 
19
19
  ```ruby
20
- OccamsRecord.
21
- query(User.active).
22
- eager_load(:orders, ->(q) { q.where("created_at >= ?", date).order("created_at DESC") })
20
+ OccamsRecord
21
+ .query(User.active)
22
+ .eager_load(:orders, ->(q) { q.where("created_at >= ?", date).order("created_at DESC") })
23
23
  ```
24
24
 
25
25
  **Use `ORDER BY` with `find_each`/`find_in_batches`**
26
26
 
27
27
  ```ruby
28
- OccamsRecord.
29
- query(Order.order("created_at DESC")).
30
- find_each { |order|
28
+ OccamsRecord
29
+ .query(Order.order("created_at DESC"))
30
+ .find_each { |order|
31
31
  ...
32
32
  }
33
33
  ```
@@ -35,15 +35,15 @@ OccamsRecord.
35
35
  **Use `find_each`/`find_in_batches` with raw SQL**
36
36
 
37
37
  ```ruby
38
- OccamsRecord.
39
- sql("
38
+ OccamsRecord
39
+ .sql("
40
40
  SELECT * FROM orders
41
41
  WHERE created_at >= %{date}
42
42
  LIMIT %{batch_limit}
43
43
  OFFSET %{batch_offset}",
44
44
  {date: 10.years.ago}
45
- ).
46
- find_each { |order|
45
+ )
46
+ .find_each { |order|
47
47
  ...
48
48
  }
49
49
  ```
@@ -51,13 +51,13 @@ OccamsRecord.
51
51
  **Eager load associations when you're writing raw SQL**
52
52
 
53
53
  ```ruby
54
- OccamsRecord.
55
- sql("
54
+ OccamsRecord
55
+ .sql("
56
56
  SELECT * FROM users
57
57
  LEFT OUTER JOIN ...
58
- ").
59
- model(User).
60
- eager_load(:orders)
58
+ ")
59
+ .model(User)
60
+ .eager_load(:orders)
61
61
  ```
62
62
 
63
63
  **Eager load "ad hoc associations" using raw SQL**
@@ -66,9 +66,9 @@ Relationships are complicated, and sometimes they can't be expressed in ActiveRe
66
66
  (Don't worry, there's a full explanation later on.)
67
67
 
68
68
  ```ruby
69
- OccamsRecord.
70
- query(User.all).
71
- eager_load_many(:orders, {:id => :user_id}, "
69
+ OccamsRecord
70
+ .query(User.all)
71
+ .eager_load_many(:orders, {:id => :user_id}, "
72
72
  SELECT user_id, orders.*
73
73
  FROM orders INNER JOIN ...
74
74
  WHERE user_id IN (%{ids})
@@ -102,14 +102,14 @@ Code lives at at [github.com/jhollinger/occams-record](https://github.com/jholli
102
102
  Build your queries like normal, using ActiveRecord's excellent query builder. Then pass them off to Occams Record.
103
103
 
104
104
  ```ruby
105
- q = Order.
106
- completed.
107
- where("order_date > ?", 30.days.ago).
108
- order("order_date DESC")
109
-
110
- orders = OccamsRecord.
111
- query(q).
112
- run
105
+ q = Order
106
+ .completed
107
+ .where("order_date > ?", 30.days.ago)
108
+ .order("order_date DESC")
109
+
110
+ orders = OccamsRecord
111
+ .query(q)
112
+ .run
113
113
  ````
114
114
 
115
115
  `each`, `map`, `reduce`, and other Enumerable methods may be used instead of *run*. `find_each` and `find_in_batches` are also supported, and unlike in ActiveRecord, `ORDER BY` works as you'd expect.
@@ -121,14 +121,14 @@ Occams Record has great support for raw SQL queries too, but we'll get to those
121
121
  Eager loading is similiar to ActiveRecord's `preload`: each association is loaded in a separate query. Unlike ActiveRecord, nested associations use blocks instead of Hashes. More importantly, if you try to use an association you didn't eager load *an exception will be raised*. In other words, the N+1 query problem simply doesn't exist.
122
122
 
123
123
  ```ruby
124
- OccamsRecord.
125
- query(q).
126
- eager_load(:customer).
127
- eager_load(:line_items) {
124
+ OccamsRecord
125
+ .query(q)
126
+ .eager_load(:customer)
127
+ .eager_load(:line_items) {
128
128
  eager_load(:product)
129
129
  eager_load(:something_else)
130
- }.
131
- find_each { |order|
130
+ }
131
+ .find_each { |order|
132
132
  puts order.customer.name
133
133
  order.line_items.each { |line_item|
134
134
  puts line_item.product.name
@@ -143,17 +143,17 @@ OccamsRecord.
143
143
  Occams Record allows you to tweak the SQL of any eager load. Pull back only the columns you need, change the order, add a `WHERE` clause, etc.
144
144
 
145
145
  ```ruby
146
- orders = OccamsRecord.
147
- query(q).
146
+ orders = OccamsRecord
147
+ .query(q)
148
148
  # Only SELECT the columns you need. Your DBA will thank you.
149
- eager_load(:customer, select: "id, name").
149
+ .eager_load(:customer, select: "id, name")
150
150
 
151
151
  # A Proc can use ActiveRecord's query builder
152
- eager_load(:line_items, ->(q) { q.active.order("created_at") }) {
152
+ .eager_load(:line_items, ->(q) { q.active.order("created_at") }) {
153
153
  eager_load(:product)
154
154
  eager_load(:something_else)
155
- }.
156
- run
155
+ }
156
+ .run
157
157
  ```
158
158
 
159
159
  Occams Record also supports loading ad hoc associations using raw SQL. We'll get to that in the next section.
@@ -167,8 +167,8 @@ ActiveRecord has raw SQL escape hatches like `find_by_sql` and `exec_query`, but
167
167
  To use `find_each`/`find_in_batches` you must provide the limit and offset statements yourself; Occams will provide the values. Also, notice that the binding syntax is a bit different (it uses Ruby's built-in named string substitution).
168
168
 
169
169
  ```ruby
170
- OccamsRecord.
171
- sql("
170
+ OccamsRecord
171
+ .sql("
172
172
  SELECT * FROM orders
173
173
  WHERE order_date > %{date}
174
174
  ORDER BY order_date DESC, id
@@ -176,8 +176,8 @@ OccamsRecord.
176
176
  OFFSET %{batch_offset}
177
177
  ", {
178
178
  date: 10.years.ago
179
- }).
180
- find_each(batch_size: 1000) do |order|
179
+ })
180
+ .find_each(batch_size: 1000) do |order|
181
181
  ...
182
182
  end
183
183
  ```
@@ -187,17 +187,17 @@ OccamsRecord.
187
187
  To use `eager_load` with a raw SQL query you must tell Occams what the base model is. (That doesn't apply if you're loading an ad hoc, raw SQL association. We'll get to those next.)
188
188
 
189
189
  ```ruby
190
- orders = OccamsRecord.
191
- sql("
190
+ orders = OccamsRecord
191
+ .sql("
192
192
  SELECT * FROM orders
193
193
  WHERE order_date > %{date}
194
194
  ORDER BY order_date DESC, id
195
195
  ", {
196
196
  date: 30.days.ago
197
- }).
198
- model(Order).
199
- eager_load(:customer).
200
- run
197
+ })
198
+ .model(Order)
199
+ .eager_load(:customer)
200
+ .run
201
201
  ```
202
202
 
203
203
  ## Raw SQL eager loading
@@ -205,14 +205,14 @@ orders = OccamsRecord.
205
205
  Let's say we want to load each product with an array of all customers who've ordered it. We *could* do that by loading various nested associations:
206
206
 
207
207
  ```ruby
208
- products_with_orders = OccamsRecord.
209
- query(Product.all).
210
- eager_load(:line_items) {
208
+ products_with_orders = OccamsRecord
209
+ .query(Product.all)
210
+ .eager_load(:line_items) {
211
211
  eager_load(:order) {
212
212
  eager_load(:customer)
213
213
  }
214
- }.
215
- map { |product|
214
+ }
215
+ .map { |product|
216
216
  customers = product.line_items.map(&:order).map(&:customer).uniq
217
217
  [product, customers]
218
218
  }
@@ -221,9 +221,9 @@ products_with_orders = OccamsRecord.
221
221
  But that's very wasteful. Occams gives us better options: `eager_load_many` and `eager_load_one`.
222
222
 
223
223
  ```ruby
224
- products = OccamsRecord.
225
- query(Product.all).
226
- eager_load_many(:customers, {:id => :product_id}, "
224
+ products = OccamsRecord
225
+ .query(Product.all)
226
+ .eager_load_many(:customers, {:id => :product_id}, "
227
227
  SELECT DISTINCT product_id, customers.*
228
228
  FROM line_items
229
229
  INNER JOIN orders ON line_items.order_id = orders.id
@@ -231,8 +231,8 @@ products = OccamsRecord.
231
231
  WHERE line_items.product_id IN (%{ids})
232
232
  ", binds: {
233
233
  # additional bind values (ids will be passed in for you)
234
- }).
235
- run
234
+ })
235
+ .run
236
236
  ```
237
237
 
238
238
  `eager_load_many` is declaring an ad hoc *has_many* association called *customers*. The `{:id => :product_id}` Hash defines the mapping: *id* in the parent record maps to *product_id* in the child records.
@@ -258,12 +258,12 @@ module MyProductMethods
258
258
  end
259
259
  end
260
260
 
261
- orders = OccamsRecord.
262
- query(Order.all, use: MyOrderMethods).
263
- eager_load(:line_items) {
261
+ orders = OccamsRecord
262
+ .query(Order.all, use: MyOrderMethods)
263
+ .eager_load(:line_items) {
264
264
  eager_load(:product, use: [MyProductMethods, OtherMethods])
265
- }.
266
- run
265
+ }
266
+ .run
267
267
  ```
268
268
 
269
269
  ---
@@ -307,9 +307,12 @@ TEST_DATABASE_URL=postgres://postgres@localhost:5432/occams_record bundle exec r
307
307
  ```bash
308
308
  bundle exec appraisal install
309
309
 
310
- # test against SQLite
310
+ # test against all supported AR versions (defaults to SQLite)
311
311
  bundle exec appraisal rake test
312
312
 
313
+ # test against a specific AR version
314
+ bundle exec appraisal ar-7.0 rake test
315
+
313
316
  # test against Postgres
314
317
  TEST_DATABASE_URL=postgres://postgres@localhost:5432/occams_record bundle exec appraisal rake test
315
318
  ```
@@ -56,14 +56,14 @@ module OccamsRecord
56
56
  #
57
57
  # The %{category_ids} bind param will be provided for you, and in this case will be all the category_id values from the Widget query.
58
58
  #
59
- # res = OccamsRecord.
60
- # query(Widget.order("name")).
61
- # eager_load_one(:category, {:category_id => :id}, "
59
+ # res = OccamsRecord
60
+ # .query(Widget.order("name"))
61
+ # .eager_load_one(:category, {:category_id => :id}, "
62
62
  # SELECT * FROM categories WHERE id IN (%{category_ids}) AND name != %{bad_name}
63
63
  # ", binds: {
64
64
  # bad_name: "Bad Category"
65
- # }).
66
- # run
65
+ # })
66
+ # .run
67
67
  #
68
68
  # @param name [Symbol] name of attribute to load records into
69
69
  # @param mapping [Hash] a Hash that defines the key mapping of the parent (widgets.category_id) to the child (categories.id).
@@ -89,14 +89,14 @@ module OccamsRecord
89
89
  # The %{ids} bind param will be provided for you, and in this case will be all the id values from the Widget
90
90
  # query.
91
91
  #
92
- # res = OccamsRecord.
93
- # query(Widget.order("name")).
94
- # eager_load_many(:parts, {:id => :widget_id}, "
92
+ # res = OccamsRecord
93
+ # .query(Widget.order("name"))
94
+ # .eager_load_many(:parts, {:id => :widget_id}, "
95
95
  # SELECT * FROM parts WHERE widget_id IN (%{ids}) AND sku NOT IN (%{bad_skus})
96
96
  # ", binds: {
97
97
  # bad_skus: ["G90023ASDf0"]
98
- # }).
99
- # run
98
+ # })
99
+ # .run
100
100
  #
101
101
  # @param name [Symbol] name of attribute to load records into
102
102
  # @param mapping [Hash] a Hash that defines the key mapping of the parent (widgets.id) to the children (parts.widget_id).
@@ -9,7 +9,7 @@ module OccamsRecord
9
9
  #
10
10
  # See documentation for OccamsRecord::EagerLoaders::Base.
11
11
  #
12
- def initialize(*args)
12
+ def initialize(ref, scope = nil, use: nil, as: nil, optimizer: :select, &builder)
13
13
  super
14
14
 
15
15
  unless @ref.macro == :has_one or @ref.macro == :has_many
@@ -5,18 +5,18 @@ module OccamsRecord
5
5
  #
6
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
7
  #
8
- # orders = OccamsRecord.
9
- # query(Order.all).
10
- # eager_load(:customer).
8
+ # orders = OccamsRecord
9
+ # .query(Order.all)
10
+ # .eager_load(:customer)
11
11
  # ...
12
- # measure { |x|
12
+ # .measure { |x|
13
13
  # puts "Total time: #{x.total_time} sec"
14
14
  # x.queries.each { |q|
15
15
  # puts "Table: #{q.table_name} (#{q.time} sec)"
16
16
  # puts q.sql
17
17
  # }
18
- # }.
19
- # run
18
+ # }
19
+ # .run
20
20
  #
21
21
  module Measureable
22
22
  #
@@ -7,15 +7,15 @@ module OccamsRecord
7
7
  # Instead use OccamsRecord::Query#eager_load. Finally, call `run` (or any Enumerable method) to run
8
8
  # the query and get back an array of objects.
9
9
  #
10
- # results = OccamsRecord.
11
- # query(Widget.order("name")).
12
- # eager_load(:category).
13
- # eager_load(:order_items, ->(q) { q.select("widget_id, order_id") }) {
10
+ # results = OccamsRecord
11
+ # .query(Widget.order("name"))
12
+ # .eager_load(:category)
13
+ # .eager_load(:order_items, ->(q) { q.select("widget_id, order_id") }) {
14
14
  # eager_load(:orders) {
15
15
  # eager_load(:customer, ->(q) { q.select("name") })
16
16
  # }
17
- # }.
18
- # run
17
+ # }
18
+ # .run
19
19
  #
20
20
  # @param scope [ActiveRecord::Relation]
21
21
  # @param use [Module] optional Module to include in the result class
@@ -14,16 +14,16 @@ module OccamsRecord
14
14
  # If you want to do eager loading, you must first the define a model to pull the associations from (unless
15
15
  # you're using the raw SQL eager loaders `eager_load_one` or `eager_load_many`).
16
16
  #
17
- # results = OccamsRecord.
18
- # sql("
17
+ # results = OccamsRecord
18
+ # .sql("
19
19
  # SELECT * FROM widgets
20
20
  # WHERE category_id IN (%{cat_ids})
21
21
  # ", {
22
22
  # cat_ids: [5, 10]
23
- # }).
24
- # model(Widget).
25
- # eager_load(:category).
26
- # run
23
+ # })
24
+ # .model(Widget)
25
+ # .eager_load(:category)
26
+ # .run
27
27
  #
28
28
  # NOTE To use find_each/find_in_batches, your SQL string must include 'LIMIT %{batch_limit} OFFSET %{batch_offset}',
29
29
  # and an ORDER BY is strongly recomended. OccamsRecord will provide the bind values for you.
@@ -4,7 +4,7 @@ module OccamsRecord
4
4
  # ActiveRecord's internal type casting API changes from version to version.
5
5
  CASTER = case ActiveRecord::VERSION::MAJOR
6
6
  when 4 then :type_cast_from_database
7
- when 5, 6 then :deserialize
7
+ when 5, 6, 7 then :deserialize
8
8
  else raise "OccamsRecord::Results::CASTER does yet support this version of ActiveRecord"
9
9
  end
10
10
 
@@ -8,10 +8,10 @@ module OccamsRecord
8
8
  # The ONLY reason to use this is if you absolutely need ActiveRecord objects but still want to use Occams's
9
9
  # more advanced eager loading or find_each/find_in_batches features.
10
10
  #
11
- # OccamsRecord.
12
- # query(Order.order("created_at DESC")).
13
- # eager_load(:line_items, ->(q) { q.order("price") }).
14
- # find_each do |o|
11
+ # OccamsRecord
12
+ # .query(Order.order("created_at DESC"))
13
+ # .eager_load(:line_items, ->(q) { q.order("price") })
14
+ # .find_each do |o|
15
15
  # order = OccamsRecord::Ugly.active_record(o)
16
16
  # ...
17
17
  # end
@@ -3,5 +3,5 @@
3
3
  #
4
4
  module OccamsRecord
5
5
  # Library version
6
- VERSION = "1.2.1".freeze
6
+ VERSION = "1.3.0.rc1".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.2.1
4
+ version: 1.3.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-04 00:00:00.000000000 Z
11
+ date: 2021-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '4.2'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '6.2'
22
+ version: '7.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '4.2'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '6.2'
32
+ version: '7.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: appraisal
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -54,7 +54,6 @@ files:
54
54
  - README.md
55
55
  - lib/occams-record.rb
56
56
  - lib/occams-record/batches.rb
57
- - lib/occams-record/connection.rb
58
57
  - lib/occams-record/eager_loaders/ad_hoc_base.rb
59
58
  - lib/occams-record/eager_loaders/ad_hoc_many.rb
60
59
  - lib/occams-record/eager_loaders/ad_hoc_one.rb
@@ -81,7 +80,7 @@ homepage: https://jhollinger.github.io/occams-record/
81
80
  licenses:
82
81
  - MIT
83
82
  metadata: {}
84
- post_install_message:
83
+ post_install_message:
85
84
  rdoc_options: []
86
85
  require_paths:
87
86
  - lib
@@ -92,12 +91,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
91
  version: 2.3.0
93
92
  required_rubygems_version: !ruby/object:Gem::Requirement
94
93
  requirements:
95
- - - ">="
94
+ - - ">"
96
95
  - !ruby/object:Gem::Version
97
- version: '0'
96
+ version: 1.3.1
98
97
  requirements: []
99
- rubygems_version: 3.0.3
100
- signing_key:
98
+ rubygems_version: 3.1.6
99
+ signing_key:
101
100
  specification_version: 4
102
101
  summary: The missing high-efficiency query API for ActiveRecord
103
102
  test_files: []
@@ -1,7 +0,0 @@
1
- module OccamsRecord
2
- class Connection
3
- def initialize(model, role = nil)
4
- @model, @role = model, role
5
- end
6
- end
7
- end