ar-ondemand 1.3.1 → 1.3.2
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 +8 -8
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -1
- data/README.md +42 -16
- data/lib/ar-ondemand/fast_enumeration.rb +4 -2
- data/lib/ar-ondemand/result.rb +3 -1
- data/lib/ar-ondemand/version.rb +1 -1
- data/rails_5.2.Gemfile +4 -0
- data/solano.yml +5 -0
- data/spec/factories/audit_record.rb +2 -2
- data/spec/factories/widget.rb +1 -1
- data/spec/lib/for_enumeration_reading_spec.rb +7 -0
- data/spec/lib/for_enumeration_streaming_spec.rb +8 -0
- data/spec/lib/for_reading_spec.rb +7 -0
- data/spec/lib/for_streaming_spec.rb +20 -0
- data/spec/spec_helper.rb +3 -3
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
!binary "U0hBMQ==":
|
|
3
3
|
metadata.gz: !binary |-
|
|
4
|
-
|
|
4
|
+
ZTlmOGRjOTFkOWRjNDk3ZTgxMDY4NzJlZDMxM2UwMDdlMjY2MDczMQ==
|
|
5
5
|
data.tar.gz: !binary |-
|
|
6
|
-
|
|
6
|
+
ZmZhNDk1ZmQ2MWU3YmYxZGI0ZGU0MjNlOGRkMWJmMjMyMTJkMTQwNg==
|
|
7
7
|
SHA512:
|
|
8
8
|
metadata.gz: !binary |-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
MDRjMTk2NDEwYTBjYjc2ZjlhOTY0MjJmNWRkMTE0MTQ3YzEzNDJmODYzYjI4
|
|
10
|
+
ZDdhOTAwM2MxZTk5NjU3ODhhY2ZlY2QwYjU3MjFmNGQ3YjgzYzdmMWE4NjBm
|
|
11
|
+
NjFjYWRlZGY3MWQxYzJjM2Y0NzRjY2NlMDFlZDA1YzQ0YWUxODc=
|
|
12
12
|
data.tar.gz: !binary |-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
NDNmZDBkYzcyZmViMWE0M2IyN2ZjMDYwMzY0OGVhYTBiYzE1YzBkYjJhYzNh
|
|
14
|
+
MmQ1Njk5YmU2ZGM5ZDc1ODFiYTg1Zjc4NmMzOGNjNGMzNjgyMzgzM2ZlNDFi
|
|
15
|
+
MjllNDUzNGQ1NGVkMzU5NTQwYmEwZWQ5NzhiYmY3Nzg5ZDY2Njk=
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Please note that this library has been written for our needs, and even though it
|
|
|
15
15
|
production environments, it hasn't seen the myriad ways others use and abuse ActiveRecord, so please experiment
|
|
16
16
|
locally first.
|
|
17
17
|
|
|
18
|
-
It has been used with ActiveRecord 3.2, MRI 1.9.3, JRuby 1.7
|
|
18
|
+
It has been used with ActiveRecord 3.2, MRI 1.9.3, MRI 2.3.3, JRuby 1.7, JRuby 9, all with the MySQL adapter.
|
|
19
19
|
|
|
20
20
|
# Functionality
|
|
21
21
|
|
|
@@ -55,7 +55,7 @@ method makes it easy to get access to ActiveRecord-like functionality at 100th t
|
|
|
55
55
|
|
|
56
56
|
### Usage
|
|
57
57
|
|
|
58
|
-
Let's say you have some
|
|
58
|
+
Let's say you have some Widgets. Instead of:
|
|
59
59
|
|
|
60
60
|
```rb
|
|
61
61
|
Widget.where(customer_id: 1).each { |r| ... }
|
|
@@ -75,23 +75,15 @@ The big limitation is that you can't use `.includes` so you have to be writing a
|
|
|
75
75
|
Widget.where(customer_id: 1).for_reading(batch_size: 50000).each { |b| b.each { |r| } }
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
##
|
|
79
|
-
|
|
80
|
-
This is just a nice little helper to get the raw database results, which you'd get by calling `ActiveRecord::Base.connection.select_all`
|
|
81
|
-
but for some reason you can't call that on a model.
|
|
78
|
+
## for_enumeration_reading
|
|
82
79
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ActiveRecord::Base.connection.select_all "select * from widgets where customer_id = 1"
|
|
86
|
-
ActiveRecord::Base.connection.select_all "select * from widgets where customer_id = 1 limit 100000"
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Usage
|
|
80
|
+
This version of `for_reading` allows even faster access to the record when you just need to pull out some properties
|
|
81
|
+
while looping over the dataset. Access to the data is *only* available in the block passed to `for_enumeration_reading`.
|
|
90
82
|
|
|
91
83
|
```rb
|
|
92
|
-
Widget.
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
res = Widget.where(customer_id: 1).for_enumeration_reading.inject([]) do |i, r|
|
|
85
|
+
i << [r.id, r.name]
|
|
86
|
+
end
|
|
95
87
|
```
|
|
96
88
|
|
|
97
89
|
## for_streaming
|
|
@@ -123,6 +115,40 @@ Widget.where(customer_id: 1).for_streaming(for_reading: true).each { |r| }
|
|
|
123
115
|
Widget.where(customer_id: 1).for_streaming(for_reading: true, batch_size: 1_000_000).each { |r| }
|
|
124
116
|
```
|
|
125
117
|
|
|
118
|
+
## for_enumeration_streaming
|
|
119
|
+
|
|
120
|
+
Just as `for_reading` has an enumeration version, `for_streaming` does as well. This helper function
|
|
121
|
+
is aimed at queries of millions of records that need to stream over the results, and only need to pull
|
|
122
|
+
out values witin the supplied block.
|
|
123
|
+
|
|
124
|
+
### Usage
|
|
125
|
+
|
|
126
|
+
```rb
|
|
127
|
+
res = []
|
|
128
|
+
Widget.where(customer_id: 1).for_enumeration_streaming(batch_size: 200_000).each do |r|
|
|
129
|
+
res.add [r.id, r.name]
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## raw_results
|
|
134
|
+
|
|
135
|
+
This is just a nice little helper to get the raw database results, which you'd get by calling `ActiveRecord::Base.connection.select_all`
|
|
136
|
+
but for some reason you can't call that on a model.
|
|
137
|
+
|
|
138
|
+
```rb
|
|
139
|
+
ActiveRecord::Base.connection.select_all "select * from widgets"
|
|
140
|
+
ActiveRecord::Base.connection.select_all "select * from widgets where customer_id = 1"
|
|
141
|
+
ActiveRecord::Base.connection.select_all "select * from widgets where customer_id = 1 limit 100000"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Usage
|
|
145
|
+
|
|
146
|
+
```rb
|
|
147
|
+
Widget.raw_results
|
|
148
|
+
Widget.where(customer_id: 1).raw_results
|
|
149
|
+
Widget.where(customer_id: 1).limit(100_000).raw_results
|
|
150
|
+
```
|
|
151
|
+
|
|
126
152
|
## delete_all_by_pk
|
|
127
153
|
|
|
128
154
|
Deleting many records or even a few records in a massive table can be an expensive operation, and can even lock up
|
|
@@ -8,7 +8,9 @@ module ActiveRecord
|
|
|
8
8
|
column_model = @column_models[name]
|
|
9
9
|
|
|
10
10
|
# For AR 5.x type casting
|
|
11
|
-
ar_type =
|
|
11
|
+
ar_type = (column_model.type.is_a?(::Symbol) ?
|
|
12
|
+
ActiveRecord::Type.registry.lookup(column_model.type) :
|
|
13
|
+
column_model.type) if column_model && defined?(ActiveRecord::Type.registry.lookup)
|
|
12
14
|
self.define_singleton_method(name) do
|
|
13
15
|
raise 'Not accessible outside of enumeration' if @row.nil?
|
|
14
16
|
if column_model.respond_to?(:type_cast)
|
|
@@ -23,7 +25,7 @@ module ActiveRecord
|
|
|
23
25
|
# Rails 5+
|
|
24
26
|
ar_type.is_a?(::Symbol) ? @row[index] : ar_type.cast(@row[index])
|
|
25
27
|
else
|
|
26
|
-
|
|
28
|
+
@row[index]
|
|
27
29
|
end
|
|
28
30
|
end
|
|
29
31
|
end
|
data/lib/ar-ondemand/result.rb
CHANGED
|
@@ -11,7 +11,9 @@ module ActiveRecord
|
|
|
11
11
|
@column_types = Hash[@model.columns.map { |x| [x.name, x] }]
|
|
12
12
|
|
|
13
13
|
# For AR 5.x capture the types from registry to use for conversion
|
|
14
|
-
@ar_types = defined?(ActiveRecord::Type.registry.lookup) ? Hash[@model.columns.map { |x|
|
|
14
|
+
@ar_types = defined?(ActiveRecord::Type.registry.lookup) ? Hash[@model.columns.map { |x|
|
|
15
|
+
[x.name, (x.type.is_a?(::Symbol) ? ActiveRecord::Type.registry.lookup(x.type) : x.type)]
|
|
16
|
+
}] : nil
|
|
15
17
|
|
|
16
18
|
determine_type_cast_method
|
|
17
19
|
@col_indexes = HashWithIndifferentAccess[@results.columns.each_with_index.map { |x, i| [x,i] }]
|
data/lib/ar-ondemand/version.rb
CHANGED
data/rails_5.2.Gemfile
ADDED
data/solano.yml
CHANGED
|
@@ -18,6 +18,7 @@ pipeline:
|
|
|
18
18
|
- rails_4_1
|
|
19
19
|
- rails_4_2
|
|
20
20
|
- rails_5_1
|
|
21
|
+
- rails_5_2
|
|
21
22
|
|
|
22
23
|
# Named profile steps that can be used by build profiles and pipelines
|
|
23
24
|
profiles:
|
|
@@ -41,3 +42,7 @@ profiles:
|
|
|
41
42
|
ruby_version: ruby-2.3.4
|
|
42
43
|
environment:
|
|
43
44
|
BUNDLE_GEMFILE: rails_5.1.Gemfile
|
|
45
|
+
rails_5_2:
|
|
46
|
+
ruby_version: ruby-2.3.4
|
|
47
|
+
environment:
|
|
48
|
+
BUNDLE_GEMFILE: rails_5.2.Gemfile
|
data/spec/factories/widget.rb
CHANGED
|
@@ -79,6 +79,13 @@ describe 'ForEnumerationReading' do
|
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
+
it 'should allow reading computed fields' do
|
|
83
|
+
AuditRecord.where(customer_id: 1).select("id as id_computed, action as action_computed").for_enumeration_reading.each do |r|
|
|
84
|
+
expect(r.action_computed).to eq('create')
|
|
85
|
+
expect(r.action_computed).to be_an_instance_of(String)
|
|
86
|
+
expect(r.id_computed).to be_an_instance_of(Fixnum)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
82
89
|
end
|
|
83
90
|
end
|
|
84
91
|
end
|
|
@@ -71,6 +71,14 @@ describe 'ForEnumerationStreaming' do
|
|
|
71
71
|
end
|
|
72
72
|
}.to raise_error(ActiveRecord::StatementInvalid)
|
|
73
73
|
end
|
|
74
|
+
|
|
75
|
+
it 'should allow reading computed fields' do
|
|
76
|
+
AuditRecord.where(customer_id: 1).select(:id).select("id as id_computed, action as action_computed").for_enumeration_streaming.each do |r|
|
|
77
|
+
expect(r.action_computed).to eq('create')
|
|
78
|
+
expect(r.action_computed).to be_an_instance_of(String)
|
|
79
|
+
expect(r.id_computed).to be_an_instance_of(Fixnum)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
74
82
|
end
|
|
75
83
|
end
|
|
76
84
|
end
|
|
@@ -60,6 +60,13 @@ describe 'ForReading' do
|
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
it 'should allow reading computed fields' do
|
|
64
|
+
AuditRecord.where(customer_id: 1).select("id as id_computed, action as action_computed").for_reading.each do |r|
|
|
65
|
+
expect(r.action_computed).to eq('create')
|
|
66
|
+
expect(r.action_computed).to be_an_instance_of(String)
|
|
67
|
+
expect(r.id_computed).to be_an_instance_of(Fixnum)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
63
70
|
end
|
|
64
71
|
end
|
|
65
72
|
end
|
|
@@ -39,6 +39,14 @@ describe 'ForStreaming' do
|
|
|
39
39
|
end
|
|
40
40
|
expect(total).to be 25
|
|
41
41
|
end
|
|
42
|
+
|
|
43
|
+
it 'should allow reading computed fields' do
|
|
44
|
+
AuditRecord.where(customer_id: 1).select(:id).select("id as id_computed, action as action_computed").for_streaming.each do |r|
|
|
45
|
+
expect(r.action_computed).to eq('create')
|
|
46
|
+
expect(r.action_computed).to be_an_instance_of(String)
|
|
47
|
+
expect(r.id_computed).to be_an_instance_of(Fixnum)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
context 'For Reading' do
|
|
@@ -61,6 +69,18 @@ describe 'ForStreaming' do
|
|
|
61
69
|
end
|
|
62
70
|
expect(total).to be 25
|
|
63
71
|
end
|
|
72
|
+
|
|
73
|
+
it 'should allow reading computed fields' do
|
|
74
|
+
AuditRecord.where(customer_id: 1).select(:id).select("id as id_computed, action as action_computed, DATE(created_at)").for_streaming(for_reading: true).each do |r|
|
|
75
|
+
expect(r.action_computed).to eq('create')
|
|
76
|
+
expect(r.action_computed).to be_an_instance_of(String)
|
|
77
|
+
expect(r.id_computed).to be_an_instance_of(Fixnum)
|
|
78
|
+
|
|
79
|
+
# no date typecasting, but we have a String
|
|
80
|
+
expect(r["DATE(created_at)"]).to be_an_instance_of(String)
|
|
81
|
+
expect(r["DATE(created_at)"]).to eq(Date.today.to_s)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
64
84
|
end
|
|
65
85
|
|
|
66
86
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -8,12 +8,12 @@ I18n.enforce_available_locales = false
|
|
|
8
8
|
|
|
9
9
|
Dir['spec/support/**/*.rb'].sort.each { |f| load f }
|
|
10
10
|
|
|
11
|
-
require '
|
|
11
|
+
require 'factory_bot'
|
|
12
12
|
|
|
13
13
|
RSpec.configure do |config|
|
|
14
|
-
config.include
|
|
14
|
+
config.include FactoryBot::Syntax::Methods
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
FactoryBot.find_definitions
|
|
18
18
|
|
|
19
19
|
Dir['spec/app/models/*.rb'].sort.each { |f| load f }
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ar-ondemand
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steve Frank
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-09-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -98,6 +98,7 @@ files:
|
|
|
98
98
|
- rails_4.1.Gemfile
|
|
99
99
|
- rails_4.2.Gemfile
|
|
100
100
|
- rails_5.1.Gemfile
|
|
101
|
+
- rails_5.2.Gemfile
|
|
101
102
|
- solano.yml
|
|
102
103
|
- spec/app/models/audit_record.rb
|
|
103
104
|
- spec/app/models/widget.rb
|
|
@@ -133,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
133
134
|
version: '0'
|
|
134
135
|
requirements: []
|
|
135
136
|
rubyforge_project:
|
|
136
|
-
rubygems_version: 2.6.
|
|
137
|
+
rubygems_version: 2.6.14
|
|
137
138
|
signing_key:
|
|
138
139
|
specification_version: 4
|
|
139
140
|
summary: ActiveRecord On-demand
|