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.
Files changed (39) hide show
  1. data/README.md +34 -8
  2. data/lib/active_record/connection_adapters/future_enabled.rb +6 -5
  3. data/lib/active_record/connection_adapters/future_enabled_mysql2_adapter.rb +2 -1
  4. data/lib/active_record/connection_adapters/future_enabled_postgresql_adapter.rb +21 -7
  5. data/lib/active_record/futures.rb +11 -23
  6. data/lib/active_record/futures/calculation_methods.rb +34 -0
  7. data/lib/active_record/futures/delegation.rb +1 -1
  8. data/lib/active_record/futures/finder_methods.rb +59 -0
  9. data/lib/active_record/futures/future.rb +21 -57
  10. data/lib/active_record/futures/future_array.rb +23 -0
  11. data/lib/active_record/futures/future_registry.rb +33 -0
  12. data/lib/active_record/futures/future_value.rb +20 -0
  13. data/lib/active_record/futures/query_recording.rb +11 -4
  14. data/lib/activerecord-futures.rb +5 -4
  15. data/lib/activerecord-futures/version.rb +1 -1
  16. data/spec/active_record/futures/future_array_spec.rb +26 -0
  17. data/spec/active_record/futures/future_registry_spec.rb +86 -0
  18. data/spec/active_record/futures/future_spec.rb +42 -69
  19. data/spec/active_record/futures/future_value_spec.rb +26 -0
  20. data/spec/in_action/combination_of_futures_spec.rb +13 -9
  21. data/spec/in_action/future_all_execution_spec.rb +20 -0
  22. data/spec/in_action/future_count_execution_spec.rb +8 -56
  23. data/spec/in_action/future_exists_execution_spec.rb +22 -0
  24. data/spec/in_action/future_find_execution_spec.rb +68 -0
  25. data/spec/in_action/future_first_execution_spec.rb +20 -0
  26. data/spec/in_action/future_fulfillment_spec.rb +10 -7
  27. data/spec/in_action/future_last_execution_spec.rb +21 -0
  28. data/spec/in_action/future_pluck_execution_spec.rb +4 -27
  29. data/spec/in_action/future_relation_execution_spec.rb +8 -30
  30. data/spec/spec_helper.rb +2 -1
  31. data/spec/support/futurized_method_examples.rb +43 -0
  32. metadata +25 -12
  33. data/lib/active_record/futures/future_calculation.rb +0 -30
  34. data/lib/active_record/futures/future_calculation_array.rb +0 -8
  35. data/lib/active_record/futures/future_calculation_value.rb +0 -7
  36. data/lib/active_record/futures/future_relation.rb +0 -34
  37. data/spec/active_record/futures/future_calculation_array_spec.rb +0 -29
  38. data/spec/active_record/futures/future_calculation_value_spec.rb +0 -29
  39. 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
- connection.recorded_query
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.to_sql
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.to_sql
54
+ self.recorded_query = arel
48
55
  []
49
56
  end
50
57
  end
@@ -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/future_relation"
7
- require "active_record/futures/future_calculation"
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
 
@@ -1,5 +1,5 @@
1
1
  module Activerecord
2
2
  module Futures
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -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 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  module ActiveRecord::Futures
4
4
  describe Future do
5
- context "class methods" do
6
- describe ".futures" do
7
- context "with futures in two threads" do
8
- let(:futures_key) { "#{Future.name}_futures" }
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
- context "the futures in thread 1" do
35
- let(:futures) { a_thread[futures_key] }
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
- specify { futures.should include("Future 1") }
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
- context "the futures in thread 2" do
44
- let(:futures) { another_thread[futures_key] }
17
+ describe ".new" do
18
+ before { FutureRegistry.stub(:register) }
19
+ before { subject }
45
20
 
46
- specify { futures.should_not include("Future 1") }
47
- specify { futures.should_not include("Future 2") }
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
- describe ".current" do
55
- context "with currents in two threads" do
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
- let(:another_thread) do
63
- thread = Hash.new
64
- end
28
+ it { should_not be_fulfilled }
29
+ end
65
30
 
66
- before do
67
- Thread.stub(:current).and_return(a_thread)
31
+ describe "#fulfill" do
32
+ let(:result) { "Some cool result" }
68
33
 
69
- Future.current = "Future 1"
34
+ before do
35
+ subject.fulfill(result)
36
+ end
70
37
 
71
- Thread.stub(:current).and_return(another_thread)
38
+ it { should be_fulfilled }
39
+ end
72
40
 
73
- Future.current = "Future 2"
74
- end
41
+ describe "#load" do
42
+ before do
43
+ execution.stub(:call) do
44
+ @current_future = FutureRegistry.current
45
+ nil
46
+ end
75
47
 
76
- context "the current in thread 1" do
77
- let(:current) { a_thread[current_key] }
48
+ subject.load
49
+ end
78
50
 
79
- specify { current.should eq "Future 1" }
80
- end
51
+ it "calls the execution" do
52
+ execution.should have_received(:call)
53
+ end
81
54
 
82
- context "the current in thread 2" do
83
- let(:current) { another_thread[current_key] }
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
- specify { current.should eq "Future 2" }
86
- end
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(post_future_relation.to_sql) }
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