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.
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