activerecord-futures 0.2.0 → 0.3.0
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.
- data/README.md +34 -8
- data/lib/active_record/connection_adapters/future_enabled.rb +6 -5
- data/lib/active_record/connection_adapters/future_enabled_mysql2_adapter.rb +2 -1
- data/lib/active_record/connection_adapters/future_enabled_postgresql_adapter.rb +21 -7
- data/lib/active_record/futures.rb +11 -23
- data/lib/active_record/futures/calculation_methods.rb +34 -0
- data/lib/active_record/futures/delegation.rb +1 -1
- data/lib/active_record/futures/finder_methods.rb +59 -0
- data/lib/active_record/futures/future.rb +21 -57
- data/lib/active_record/futures/future_array.rb +23 -0
- data/lib/active_record/futures/future_registry.rb +33 -0
- data/lib/active_record/futures/future_value.rb +20 -0
- data/lib/active_record/futures/query_recording.rb +11 -4
- data/lib/activerecord-futures.rb +5 -4
- data/lib/activerecord-futures/version.rb +1 -1
- data/spec/active_record/futures/future_array_spec.rb +26 -0
- data/spec/active_record/futures/future_registry_spec.rb +86 -0
- data/spec/active_record/futures/future_spec.rb +42 -69
- data/spec/active_record/futures/future_value_spec.rb +26 -0
- data/spec/in_action/combination_of_futures_spec.rb +13 -9
- data/spec/in_action/future_all_execution_spec.rb +20 -0
- data/spec/in_action/future_count_execution_spec.rb +8 -56
- data/spec/in_action/future_exists_execution_spec.rb +22 -0
- data/spec/in_action/future_find_execution_spec.rb +68 -0
- data/spec/in_action/future_first_execution_spec.rb +20 -0
- data/spec/in_action/future_fulfillment_spec.rb +10 -7
- data/spec/in_action/future_last_execution_spec.rb +21 -0
- data/spec/in_action/future_pluck_execution_spec.rb +4 -27
- data/spec/in_action/future_relation_execution_spec.rb +8 -30
- data/spec/spec_helper.rb +2 -1
- data/spec/support/futurized_method_examples.rb +43 -0
- metadata +25 -12
- data/lib/active_record/futures/future_calculation.rb +0 -30
- data/lib/active_record/futures/future_calculation_array.rb +0 -8
- data/lib/active_record/futures/future_calculation_value.rb +0 -7
- data/lib/active_record/futures/future_relation.rb +0 -34
- data/spec/active_record/futures/future_calculation_array_spec.rb +0 -29
- data/spec/active_record/futures/future_calculation_value_spec.rb +0 -29
- data/spec/active_record/futures/future_relation_spec.rb +0 -81
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Futures
|
3
|
+
class FutureValue
|
4
|
+
attr_reader :future_execution
|
5
|
+
private :future_execution
|
6
|
+
|
7
|
+
def initialize(future_execution)
|
8
|
+
@future_execution = future_execution
|
9
|
+
end
|
10
|
+
|
11
|
+
def value
|
12
|
+
future_execution.execute
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -8,7 +8,8 @@ module ActiveRecord
|
|
8
8
|
connection = ConnectionProxy.new(@klass.connection)
|
9
9
|
@klass = KlassProxy.new(@klass, connection)
|
10
10
|
yield
|
11
|
-
|
11
|
+
@loaded = false
|
12
|
+
[connection.recorded_query, connection.recorded_binds]
|
12
13
|
ensure
|
13
14
|
@klass = orig_klass
|
14
15
|
end
|
@@ -27,11 +28,17 @@ module ActiveRecord
|
|
27
28
|
scope.instance_variable_set(:@klass, self)
|
28
29
|
scope
|
29
30
|
end
|
31
|
+
|
32
|
+
def find_by_sql(sql, binds = [])
|
33
|
+
connection.recorded_query = sanitize_sql(sql)
|
34
|
+
connection.recorded_binds = binds
|
35
|
+
[]
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
39
|
class ConnectionProxy < Proxy
|
33
40
|
attr_reader :connection
|
34
|
-
attr_accessor :recorded_query
|
41
|
+
attr_accessor :recorded_query, :recorded_binds
|
35
42
|
|
36
43
|
def initialize(connection)
|
37
44
|
super(connection)
|
@@ -39,12 +46,12 @@ module ActiveRecord
|
|
39
46
|
end
|
40
47
|
|
41
48
|
def select_value(arel, name = nil)
|
42
|
-
self.recorded_query = arel
|
49
|
+
self.recorded_query = arel
|
43
50
|
nil
|
44
51
|
end
|
45
52
|
|
46
53
|
def select_all(arel, name = nil, binds = [])
|
47
|
-
self.recorded_query = arel
|
54
|
+
self.recorded_query = arel
|
48
55
|
[]
|
49
56
|
end
|
50
57
|
end
|
data/lib/activerecord-futures.rb
CHANGED
@@ -2,14 +2,15 @@ require 'active_record'
|
|
2
2
|
require 'active_support/core_ext/module/delegation'
|
3
3
|
require "activerecord-futures/version"
|
4
4
|
|
5
|
+
require "active_record/futures/future_registry"
|
5
6
|
require "active_record/futures/future"
|
6
|
-
require "active_record/futures/
|
7
|
-
require "active_record/futures/
|
8
|
-
require "active_record/futures/future_calculation_value"
|
9
|
-
require "active_record/futures/future_calculation_array"
|
7
|
+
require "active_record/futures/future_array"
|
8
|
+
require "active_record/futures/future_value"
|
10
9
|
|
11
10
|
require "active_record/futures/proxy"
|
12
11
|
require "active_record/futures/query_recording"
|
12
|
+
require "active_record/futures/finder_methods"
|
13
|
+
require "active_record/futures/calculation_methods"
|
13
14
|
require "active_record/futures"
|
14
15
|
require "active_record/futures/delegation"
|
15
16
|
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module ActiveRecord::Futures
|
4
|
+
describe FutureArray do
|
5
|
+
let(:future) do
|
6
|
+
double(Future, execute: nil)
|
7
|
+
end
|
8
|
+
subject { FutureArray.new(future) }
|
9
|
+
|
10
|
+
describe "#to_a" do
|
11
|
+
before do
|
12
|
+
subject.to_a
|
13
|
+
end
|
14
|
+
|
15
|
+
specify { future.should have_received(:execute) }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#inspect" do
|
19
|
+
before do
|
20
|
+
subject.inspect
|
21
|
+
end
|
22
|
+
|
23
|
+
specify { future.should have_received(:execute) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ActiveRecord::Futures
|
4
|
+
describe FutureRegistry do
|
5
|
+
subject { FutureRegistry }
|
6
|
+
|
7
|
+
describe ".futures" do
|
8
|
+
context "with futures in two threads" do
|
9
|
+
let(:futures_key) { "#{subject.name}_futures" }
|
10
|
+
|
11
|
+
let(:a_thread) do
|
12
|
+
thread = double("Thread 1")
|
13
|
+
thread.stub(:[]).with(futures_key).and_return([])
|
14
|
+
thread
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:another_thread) do
|
18
|
+
thread = double("Thread 2")
|
19
|
+
thread.stub(:[]).with(futures_key).and_return([])
|
20
|
+
thread
|
21
|
+
end
|
22
|
+
|
23
|
+
before do
|
24
|
+
Thread.stub(:current).and_return(a_thread)
|
25
|
+
|
26
|
+
subject.futures << "Future 1"
|
27
|
+
subject.futures << "Future 2"
|
28
|
+
|
29
|
+
Thread.stub(:current).and_return(another_thread)
|
30
|
+
|
31
|
+
subject.futures << "Future 3"
|
32
|
+
subject.futures << "Future 4"
|
33
|
+
end
|
34
|
+
|
35
|
+
context "the futures in thread 1" do
|
36
|
+
let(:futures) { a_thread[futures_key] }
|
37
|
+
|
38
|
+
specify { futures.should include("Future 1") }
|
39
|
+
specify { futures.should include("Future 2") }
|
40
|
+
specify { futures.should_not include("Future 3") }
|
41
|
+
specify { futures.should_not include("Future 4") }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "the futures in thread 2" do
|
45
|
+
let(:futures) { another_thread[futures_key] }
|
46
|
+
|
47
|
+
specify { futures.should_not include("Future 1") }
|
48
|
+
specify { futures.should_not include("Future 2") }
|
49
|
+
specify { futures.should include("Future 3") }
|
50
|
+
specify { futures.should include("Future 4") }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".current" do
|
56
|
+
context "with currents in two threads" do
|
57
|
+
let(:current_key) { "#{subject.name}_current" }
|
58
|
+
|
59
|
+
let(:a_thread) { Hash.new }
|
60
|
+
let(:another_thread) { Hash.new }
|
61
|
+
|
62
|
+
before do
|
63
|
+
Thread.stub(:current).and_return(a_thread)
|
64
|
+
|
65
|
+
subject.current = "Future 1"
|
66
|
+
|
67
|
+
Thread.stub(:current).and_return(another_thread)
|
68
|
+
|
69
|
+
subject.current = "Future 2"
|
70
|
+
end
|
71
|
+
|
72
|
+
context "the current in thread 1" do
|
73
|
+
let(:current) { a_thread[current_key] }
|
74
|
+
|
75
|
+
specify { current.should eq "Future 1" }
|
76
|
+
end
|
77
|
+
|
78
|
+
context "the current in thread 2" do
|
79
|
+
let(:current) { another_thread[current_key] }
|
80
|
+
|
81
|
+
specify { current.should eq "Future 2" }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -1,90 +1,63 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
module ActiveRecord::Futures
|
4
4
|
describe Future do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
let(:a_thread) do
|
11
|
-
thread = double("Thread 1")
|
12
|
-
thread.stub(:[]).with(futures_key).and_return([])
|
13
|
-
thread
|
14
|
-
end
|
15
|
-
|
16
|
-
let(:another_thread) do
|
17
|
-
thread = double("Thread 2")
|
18
|
-
thread.stub(:[]).with(futures_key).and_return([])
|
19
|
-
thread
|
20
|
-
end
|
21
|
-
|
22
|
-
before do
|
23
|
-
Thread.stub(:current).and_return(a_thread)
|
24
|
-
|
25
|
-
Future.futures << "Future 1"
|
26
|
-
Future.futures << "Future 2"
|
27
|
-
|
28
|
-
Thread.stub(:current).and_return(another_thread)
|
29
|
-
|
30
|
-
Future.futures << "Future 3"
|
31
|
-
Future.futures << "Future 4"
|
32
|
-
end
|
5
|
+
let(:relation) do
|
6
|
+
double(ActiveRecord::Relation, {
|
7
|
+
connection: double("connection", supports_futures?: true)
|
8
|
+
})
|
9
|
+
end
|
33
10
|
|
34
|
-
|
35
|
-
|
11
|
+
let(:query) { double("A query") }
|
12
|
+
let(:binds) { double("Some query binds") }
|
13
|
+
let(:execution) { double("The query execution", call: nil) }
|
36
14
|
|
37
|
-
|
38
|
-
specify { futures.should include("Future 2") }
|
39
|
-
specify { futures.should_not include("Future 3") }
|
40
|
-
specify { futures.should_not include("Future 4") }
|
41
|
-
end
|
15
|
+
subject { Future.new(relation, query, binds, execution) }
|
42
16
|
|
43
|
-
|
44
|
-
|
17
|
+
describe ".new" do
|
18
|
+
before { FutureRegistry.stub(:register) }
|
19
|
+
before { subject }
|
45
20
|
|
46
|
-
|
47
|
-
|
48
|
-
specify { futures.should include("Future 3") }
|
49
|
-
specify { futures.should include("Future 4") }
|
50
|
-
end
|
51
|
-
end
|
21
|
+
it "gets registered" do
|
22
|
+
FutureRegistry.should have_received(:register).with(subject)
|
52
23
|
end
|
53
24
|
|
54
|
-
|
55
|
-
|
56
|
-
let(:current_key) { "#{Future.name}_current" }
|
57
|
-
|
58
|
-
let(:a_thread) do
|
59
|
-
thread = Hash.new
|
60
|
-
end
|
25
|
+
its(:query) { should eq query }
|
26
|
+
its(:binds) { should eq binds }
|
61
27
|
|
62
|
-
|
63
|
-
|
64
|
-
end
|
28
|
+
it { should_not be_fulfilled }
|
29
|
+
end
|
65
30
|
|
66
|
-
|
67
|
-
|
31
|
+
describe "#fulfill" do
|
32
|
+
let(:result) { "Some cool result" }
|
68
33
|
|
69
|
-
|
34
|
+
before do
|
35
|
+
subject.fulfill(result)
|
36
|
+
end
|
70
37
|
|
71
|
-
|
38
|
+
it { should be_fulfilled }
|
39
|
+
end
|
72
40
|
|
73
|
-
|
74
|
-
|
41
|
+
describe "#load" do
|
42
|
+
before do
|
43
|
+
execution.stub(:call) do
|
44
|
+
@current_future = FutureRegistry.current
|
45
|
+
nil
|
46
|
+
end
|
75
47
|
|
76
|
-
|
77
|
-
|
48
|
+
subject.load
|
49
|
+
end
|
78
50
|
|
79
|
-
|
80
|
-
|
51
|
+
it "calls the execution" do
|
52
|
+
execution.should have_received(:call)
|
53
|
+
end
|
81
54
|
|
82
|
-
|
83
|
-
|
55
|
+
it "sets the current future to itself while execution was being called in the relation" do
|
56
|
+
@current_future.should == subject
|
57
|
+
end
|
84
58
|
|
85
|
-
|
86
|
-
|
87
|
-
end
|
59
|
+
it "sets to nil the current future afterwards" do
|
60
|
+
FutureRegistry.current.should == nil
|
88
61
|
end
|
89
62
|
end
|
90
63
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module ActiveRecord::Futures
|
4
|
+
describe FutureValue do
|
5
|
+
let(:future) do
|
6
|
+
double(Future, execute: nil)
|
7
|
+
end
|
8
|
+
subject { FutureValue.new(future) }
|
9
|
+
|
10
|
+
describe "#value" do
|
11
|
+
before do
|
12
|
+
subject.value
|
13
|
+
end
|
14
|
+
|
15
|
+
specify { future.should have_received(:execute) }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#inspect" do
|
19
|
+
before do
|
20
|
+
subject.inspect
|
21
|
+
end
|
22
|
+
|
23
|
+
specify { future.should have_received(:execute) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -45,7 +45,7 @@ describe "Combination of futures" do
|
|
45
45
|
|
46
46
|
context "execs just the executed future's query", :not_supporting_adapter do
|
47
47
|
it { should exec(1).query }
|
48
|
-
it { should exec_query(
|
48
|
+
it { should exec_query(post_relation.to_sql) }
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -57,8 +57,8 @@ describe "Combination of futures" do
|
|
57
57
|
context "the user future relation" do
|
58
58
|
subject { user_future_relation }
|
59
59
|
|
60
|
-
it(nil, :supporting_adapter) { should be_fulfilled }
|
61
|
-
it(nil, :not_supporting_adapter) { should_not be_fulfilled }
|
60
|
+
it(nil, :supporting_adapter) { execution(subject).should be_fulfilled }
|
61
|
+
it(nil, :not_supporting_adapter) { execution(subject).should_not be_fulfilled }
|
62
62
|
|
63
63
|
describe "#to_a" do
|
64
64
|
let(:calling_to_a) { ->{ subject.to_a } }
|
@@ -79,8 +79,8 @@ describe "Combination of futures" do
|
|
79
79
|
context "the user future value" do
|
80
80
|
subject { user_future_value }
|
81
81
|
|
82
|
-
it(nil, :supporting_adapter) { should be_fulfilled }
|
83
|
-
it(nil, :not_supporting_adapter) { should_not be_fulfilled }
|
82
|
+
it(nil, :supporting_adapter) { execution(subject).should be_fulfilled }
|
83
|
+
it(nil, :not_supporting_adapter) { execution(subject).should_not be_fulfilled }
|
84
84
|
|
85
85
|
describe "#value" do
|
86
86
|
let(:calling_value) { ->{ subject.value } }
|
@@ -101,8 +101,8 @@ describe "Combination of futures" do
|
|
101
101
|
context "the post future relation" do
|
102
102
|
subject { post_future_relation }
|
103
103
|
|
104
|
-
it(nil, :supporting_adapter) { should be_fulfilled }
|
105
|
-
it(nil, :not_supporting_adapter) { should_not be_fulfilled }
|
104
|
+
it(nil, :supporting_adapter) { execution(subject).should be_fulfilled }
|
105
|
+
it(nil, :not_supporting_adapter) { execution(subject).should_not be_fulfilled }
|
106
106
|
|
107
107
|
describe "#to_a" do
|
108
108
|
let(:calling_to_a) { ->{ subject.to_a } }
|
@@ -124,8 +124,8 @@ describe "Combination of futures" do
|
|
124
124
|
context "the post future value" do
|
125
125
|
subject { post_future_value }
|
126
126
|
|
127
|
-
it(nil, :supporting_adapter) { should be_fulfilled }
|
128
|
-
it(nil, :not_supporting_adapter) { should_not be_fulfilled }
|
127
|
+
it(nil, :supporting_adapter) { execution(subject).should be_fulfilled }
|
128
|
+
it(nil, :not_supporting_adapter) { execution(subject).should_not be_fulfilled }
|
129
129
|
|
130
130
|
describe "#value" do
|
131
131
|
let(:calling_value) { ->{ subject.value } }
|
@@ -144,6 +144,10 @@ describe "Combination of futures" do
|
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
+
def execution(future)
|
148
|
+
future.send(:future_execution)
|
149
|
+
end
|
150
|
+
|
147
151
|
def count(relation)
|
148
152
|
arel = relation.arel
|
149
153
|
arel.projections = []
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "future_all method" do
|
4
|
+
context "with no parameters" do
|
5
|
+
let(:relation) { Post.where("published_at < ?", Time.new(2013, 1, 1)) }
|
6
|
+
let(:future) { relation.future_all }
|
7
|
+
let(:relation_result) { relation.all }
|
8
|
+
let(:future_sql) do
|
9
|
+
relation.to_sql
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
Post.create(published_at: Time.new(2012, 12, 10))
|
14
|
+
Post.create(published_at: Time.new(2012, 6, 23))
|
15
|
+
Post.create(published_at: Time.new(2013, 4, 5))
|
16
|
+
end
|
17
|
+
|
18
|
+
it_behaves_like "a futurized method", :to_a
|
19
|
+
end
|
20
|
+
end
|