occams-record 1.4.0 → 1.6.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
2
  SHA256:
3
- metadata.gz: c1dc718c0f99f5ad039974f8db3bd1c5304e41a3b3275d26dd1d86235c7790fe
4
- data.tar.gz: 8af374148ca1f1c8342b7eb8fdb30031d6c31a1d923a6e7cab55b96712ed2602
3
+ metadata.gz: a367407e2af3e4110327d26438fcb2a590b7fbd34e6b60dd4bd9396665c495fe
4
+ data.tar.gz: 8cf6fb913ac787126a80fe38baff3a56940cea8afc80e1b05e3c864ae8368e11
5
5
  SHA512:
6
- metadata.gz: 54b539bc70b1fc92c087fbc91f7edbaa1eb4dabacfda80ca0afa4ccd4ac53e832d431f3c95e35785cfa7bd7075879c655ae54019c3f341255b4411929b5ae615
7
- data.tar.gz: c1c9aed1007bcfeb0e577d08af6f86fa340e375fede08af03bd18fbc2538cfcbd4838fc5bd07c672fca6e02ca3c0ec9e8d442b3e3275deff58215dfba873df7a
6
+ metadata.gz: 2bc45d11bc974e5a5ec6b2b9ffab8e3f069048ee8335122ad81a8b49cc852fa4bdb27116865000e4b70e4f6d617b3f870861288e00a43b273e3ec14d0dfdb6b6
7
+ data.tar.gz: af1dac7260e93ed3778ff022c3d6113819528e37d77c275b9cada6cd5e731593bbe9e4f24ae283ed572a4876672126df4cb002ab8f9c863e3de4943f95acdbc2
data/README.md CHANGED
@@ -9,7 +9,7 @@ OccamsRecord is a high-efficiency, advanced query library for use alongside Acti
9
9
  * 3x-5x faster than ActiveRecord queries, *minimum*.
10
10
  * Uses 1/3 the memory of ActiveRecord query results.
11
11
  * Eliminates the N+1 query problem. (This often exceeds the baseline 3x-5x gain.)
12
- * Support for cursors (Postgres only, new in v1.4.0-beta1)
12
+ * Support for cursors (Postgres only, new in v1.4.0)
13
13
 
14
14
  ### 2) Supercharged querying & eager loading
15
15
 
@@ -20,7 +20,9 @@ Continue using ActiveRecord's query builder, but let Occams take over running th
20
20
  ```ruby
21
21
  OccamsRecord
22
22
  .query(User.active)
23
- .eager_load(:orders, ->(q) { q.where("created_at >= ?", date).order("created_at DESC") })
23
+ .eager_load(:orders) { |l|
24
+ l.scope { |q| q.where("created_at >= ?", date).order("created_at DESC") }
25
+ }
24
26
  ```
25
27
 
26
28
  **Use `ORDER BY` with `find_each`/`find_in_batches`**
@@ -135,9 +137,9 @@ Eager loading is similiar to ActiveRecord's `preload`: each association is loade
135
137
  OccamsRecord
136
138
  .query(q)
137
139
  .eager_load(:customer)
138
- .eager_load(:line_items) {
139
- eager_load(:product)
140
- eager_load(:something_else)
140
+ .eager_load(:line_items) { |l|
141
+ l.eager_load(:product)
142
+ l.eager_load(:something_else)
141
143
  }
142
144
  .find_each { |order|
143
145
  puts order.customer.name
@@ -158,11 +160,14 @@ orders = OccamsRecord
158
160
  .query(q)
159
161
  # Only SELECT the columns you need. Your DBA will thank you.
160
162
  .eager_load(:customer, select: "id, name")
161
-
162
- # A Proc can use ActiveRecord's query builder
163
- .eager_load(:line_items, ->(q) { q.active.order("created_at") }) {
164
- eager_load(:product)
165
- eager_load(:something_else)
163
+
164
+ # Or use 'scope' to access the full power of ActiveRecord's query builder.
165
+ # Here, only 'active' line items will be returned, and in a specific order.
166
+ .eager_load(:line_items) { |l|
167
+ l.scope { |q| q.active.order("created_at") }
168
+
169
+ l.eager_load(:product)
170
+ l.eager_load(:something_else)
166
171
  }
167
172
  .run
168
173
  ```
@@ -262,9 +267,9 @@ Let's say we want to load each product with an array of all customers who've ord
262
267
  ```ruby
263
268
  products_with_orders = OccamsRecord
264
269
  .query(Product.all)
265
- .eager_load(:line_items) {
266
- eager_load(:order) {
267
- eager_load(:customer)
270
+ .eager_load(:line_items) { |l|
271
+ l.eager_load(:order) { |l|
272
+ l.eager_load(:customer)
268
273
  }
269
274
  }
270
275
  .map { |product|
@@ -354,7 +359,7 @@ bundle install
354
359
  bundle exec rake test
355
360
 
356
361
  # test against Postgres
357
- TEST_DATABASE_URL=postgres://postgres@localhost:5432/occams_record bundle exec rake test
362
+ TEST_DATABASE_URL=postgresql://postgres@localhost:5432/occams_record bundle exec rake test
358
363
 
359
364
  # test against MySQL
360
365
  TEST_DATABASE_URL=mysql2://root:@127.0.0.1:3306/occams_record bundle exec rake test
@@ -26,7 +26,13 @@ module OccamsRecord
26
26
  @name, @mapping = name.to_s, mapping
27
27
  @sql, @binds, @use, @model = sql, binds, use, model
28
28
  @eager_loaders = EagerLoaders::Context.new(@model)
29
- instance_exec(&builder) if builder
29
+ if builder
30
+ if builder.arity > 0
31
+ builder.call(self)
32
+ else
33
+ instance_exec(&builder)
34
+ end
35
+ end
30
36
  end
31
37
 
32
38
  #
@@ -19,12 +19,32 @@ module OccamsRecord
19
19
  # @yield perform eager loading on *this* association (optional)
20
20
  #
21
21
  def initialize(ref, scope = nil, use: nil, as: nil, optimizer: :select, &builder)
22
- @ref, @scope, @use, @as = ref, scope, use, as
22
+ @ref, @scopes, @use, @as = ref, Array(scope), use, as
23
23
  @model = ref.klass
24
24
  @name = (as || ref.name).to_s
25
25
  @eager_loaders = EagerLoaders::Context.new(@model)
26
26
  @optimizer = optimizer
27
- instance_exec(&builder) if builder
27
+ if builder
28
+ if builder.arity > 0
29
+ builder.call(self)
30
+ else
31
+ instance_exec(&builder)
32
+ end
33
+ end
34
+ end
35
+
36
+ #
37
+ # An alternative to passing a "scope" lambda to the constructor. Your block is passed the query
38
+ # so you can call select, where, order, etc on it.
39
+ #
40
+ # If you call scope multiple times, the results will be additive.
41
+ #
42
+ # @yield [ActiveRecord::Relation] a relation to modify with select, where, order, etc
43
+ # @return self
44
+ #
45
+ def scope(&scope)
46
+ @scopes << scope if scope
47
+ self
28
48
  end
29
49
 
30
50
  #
@@ -71,7 +91,7 @@ module OccamsRecord
71
91
  def base_scope
72
92
  q = @ref.klass.all
73
93
  q = q.instance_exec(&@ref.scope) if @ref.scope
74
- q = @scope.(q) if @scope
94
+ q = @scopes.reduce(q) { |acc, scope| scope.(acc) }
75
95
  q
76
96
  end
77
97
  end
@@ -10,6 +10,12 @@ module OccamsRecord
10
10
  # Specify an association to be eager-loaded. For maximum memory savings, only SELECT the
11
11
  # colums you actually need.
12
12
  #
13
+ # If you pass a block to nest more eager loads, you may call it with one of two forms: with an argument and without:
14
+ #
15
+ # If you ommit the block argument, the "self" inside the block will be the eager loader. You can call "eager_load" and "scope" directly.
16
+ #
17
+ # If you include the block argument, the "self" inside the block is the same as the self outside the block. The argument will be the eager loader, which you can use to make additional "eager_load" or "scope" calls.
18
+ #
13
19
  # @param assoc [Symbol] name of association
14
20
  # @param scope [Proc] a scope to apply to the query (optional). It will be passed an
15
21
  # ActiveRecord::Relation on which you may call all the normal query hethods (select, where, etc) as well as any scopes you've defined on the model.
@@ -115,7 +115,7 @@ module OccamsRecord
115
115
 
116
116
  def build_loader!(assoc, custom_name, scope, select, use, optimizer, builder)
117
117
  build_loader(assoc, custom_name, scope, select, use, optimizer, builder) ||
118
- raise("OccamsRecord: No assocation `:#{assoc}` on `#{@model.name}` or subclasses")
118
+ raise("OccamsRecord: No association `:#{assoc}` on `#{@model.name}` or subclasses")
119
119
  end
120
120
 
121
121
  def build_loader(assoc, custom_name, scope, select, use, optimizer, builder)
@@ -17,12 +17,32 @@ module OccamsRecord
17
17
  # @yield perform eager loading on *this* association (optional)
18
18
  #
19
19
  def initialize(ref, scope = nil, use: nil, as: nil, optimizer: nil, &builder)
20
- @ref, @scope, @use = ref, scope, use
20
+ @ref, @scopes, @use = ref, Array(scope), use
21
21
  @name = (as || ref.name).to_s
22
22
  @foreign_type = @ref.foreign_type.to_sym
23
23
  @foreign_key = @ref.foreign_key.to_sym
24
24
  @eager_loaders = EagerLoaders::Context.new(nil, polymorphic: true)
25
- instance_exec(&builder) if builder
25
+ if builder
26
+ if builder.arity > 0
27
+ builder.call(self)
28
+ else
29
+ instance_exec(&builder)
30
+ end
31
+ end
32
+ end
33
+
34
+ #
35
+ # An alternative to passing a "scope" lambda to the constructor. Your block is passed the query
36
+ # so you can call select, where, order, etc on it.
37
+ #
38
+ # If you call scope multiple times, the results will be additive.
39
+ #
40
+ # @yield [ActiveRecord::Relation] a relation to modify with select, where, order, etc
41
+ # @return self
42
+ #
43
+ def scope(&scope)
44
+ @scopes << scope if scope
45
+ self
26
46
  end
27
47
 
28
48
  #
@@ -83,7 +103,7 @@ module OccamsRecord
83
103
  def base_scope(model)
84
104
  q = model.all
85
105
  q = q.instance_exec(&@ref.scope) if @ref.scope
86
- q = @scope.(q) if @scope
106
+ q = @scopes.reduce(q) { |acc, scope| scope.(acc) }
87
107
  q
88
108
  end
89
109
  end
@@ -3,5 +3,5 @@
3
3
  #
4
4
  module OccamsRecord
5
5
  # @private
6
- VERSION = "1.4.0".freeze
6
+ VERSION = "1.6.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: 1.4.0
4
+ version: 1.6.0
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: 2022-05-25 00:00:00.000000000 Z
11
+ date: 2023-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -83,7 +83,7 @@ homepage: https://jhollinger.github.io/occams-record/
83
83
  licenses:
84
84
  - MIT
85
85
  metadata: {}
86
- post_install_message:
86
+ post_install_message:
87
87
  rdoc_options: []
88
88
  require_paths:
89
89
  - lib
@@ -98,8 +98,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
98
  - !ruby/object:Gem::Version
99
99
  version: '0'
100
100
  requirements: []
101
- rubygems_version: 3.0.3.1
102
- signing_key:
101
+ rubygems_version: 3.4.1
102
+ signing_key:
103
103
  specification_version: 4
104
104
  summary: The missing high-efficiency query API for ActiveRecord
105
105
  test_files: []