ar-ondemand 1.3.1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|