activerecord-futures 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|