activerecord-futures 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in activerecord-futures.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Leonardo Andres Garcia Crespo
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # ActiveRecord::Futures
2
+
3
+ Define future queries in ActiveRecord that will get executed in a single round trip to the database.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'activerecord-futures'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install activerecord-futures
18
+
19
+ ## Usage
20
+
21
+ Currently, the only database supported is MySQL, and with a special adapter, provided by the gem.
22
+
23
+ Set your config/database.yml file to use the given adapter:
24
+
25
+ ```yml
26
+ development: &development
27
+ adapter: future_enabled_mysql2 # set this adapter for futures to work!
28
+ username: your_username
29
+ password: your_password
30
+ database: your_database
31
+ host: your_host
32
+ ```
33
+
34
+ Now let's see what this does, consider a model `User`, with a `:name` attribute:
35
+
36
+ ```ruby
37
+ # Build the queries and mark them as futures
38
+ users = User.where("name like 'John%'").future # becomes a future relation, does not execute the query.
39
+ count = User.where("name like 'John%'").future_count # becomes a future calculation, does not execute the query.
40
+
41
+ # Execute any of the futures
42
+ count = count.value # trigger the future execution, both queries will get executed in one round trip!
43
+ #=> User Load (fetching Futures) (0.6ms) SELECT `users`.* FROM `users` WHERE (name like 'John%');SELECT COUNT(*) FROM `users` WHERE (name like 'John%')
44
+
45
+ # Access the other results
46
+ users = users.to_a # does not execute the query, results from previous query get loaded
47
+ ```
48
+
49
+ Any amount of futures can be prepared, and the will get executed as soon as one of them needs to be evaluated.
50
+
51
+ ### Methods
52
+
53
+ ActiveRecord::Relation instances get a `future` method for all queries where multiple results are returned. The future gets
54
+ executed whenever `#to_a` gets executed. Note that, as ActiveRecord does, enumerable methods get delegated to `#to_a` also,
55
+ so things like `#each`, `#map`, `#collect` all trigger the future.
56
+
57
+ Also, ActiveRecord::Relation instances get all the calculation methods provided by the ActiveRecord::Calculations module
58
+ "futurized", that means, for `#count` you get `#future_count`, for `#sum` you get `#future_sum` and so on. These future
59
+ calculations are triggered by executing the `#value` method, which also return the actual result of the calculation.
60
+
61
+ ## Database support
62
+
63
+ ### SQlite
64
+
65
+ SQlite doesn't support multiple statement queries. Currently this gem doesn't fall back to the normal behavior if the
66
+ adapter does not support futures, but this is in the road map :)
67
+
68
+ ### MySQL
69
+
70
+ Multi statement queries are supported by the mysql2 gem since version 0.3.12b1, so you'll need to use that one or a newer
71
+ one.
72
+ Currently the adapter provided is the same as the built-in in Rails, but it also sets the MULTI_STATEMENTS flag to allow
73
+ multiple queries in a single command. It also has a special way to
74
+ execute the queries in order to fetch the results correctly. You
75
+ can check the code if you're curious!
76
+
77
+ ### Postgres
78
+
79
+ Coming soon!
80
+
81
+ ## Contributing
82
+
83
+ 1. Fork it
84
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
85
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
86
+ 4. Push to the branch (`git push origin my-new-feature`)
87
+ 5. Create new Pull Request
88
+
89
+ ## Roadmap
90
+
91
+ 1. Support for postgres
92
+ 2. Fallback to normal queries when adapter does not support futures
93
+ 3. Think of a way to use the normal adapters
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'activerecord-futures/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "activerecord-futures"
8
+ gem.version = Activerecord::Futures::VERSION
9
+ gem.authors = ["Leonardo Andres Garcia Crespo"]
10
+ gem.email = ["leoasis@gmail.com"]
11
+ gem.description = %q{Save unnecessary round trips to the database}
12
+ gem.summary = %q{Fetch all queries at once from the database and save round trips. }
13
+ gem.homepage = "https://github.com/leoasis/activerecord-futures"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'activerecord', '>= 3.2.13'
21
+ gem.add_development_dependency 'rspec'
22
+ gem.add_development_dependency 'rspec-spies'
23
+ end
@@ -0,0 +1,51 @@
1
+ require "active_record/connection_adapters/mysql2_adapter"
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ def self.future_enabled_mysql2_connection(config)
6
+ config = config.symbolize_keys
7
+
8
+ config[:username] = 'root' if config[:username].nil?
9
+
10
+ if Mysql2::Client.const_defined? :FOUND_ROWS
11
+ config[:flags] = Mysql2::Client::FOUND_ROWS | Mysql2::Client::MULTI_STATEMENTS
12
+ end
13
+ client = Mysql2::Client.new(config)
14
+ options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0]
15
+ ConnectionAdapters::FutureEnabledMysql2Adapter.new(client, logger, options, config)
16
+ end
17
+ end
18
+
19
+ module ConnectionAdapters
20
+ class FutureEnabledMysql2Adapter < Mysql2Adapter
21
+
22
+ def supports_futures?
23
+ true
24
+ end
25
+
26
+ def exec_query(sql, name = 'SQL', binds = [])
27
+ my_future = Futures::Future.current
28
+
29
+ # default behavior if not a current future
30
+ return super unless my_future
31
+
32
+ # return fulfilled result, if exists, to load the relation
33
+ return my_future.result if my_future.fulfilled?
34
+
35
+ futures = Futures::Future.all
36
+
37
+ futures_sql = futures.map(&:to_sql).join(';')
38
+ name = "#{name} (fetching Futures)"
39
+
40
+ result = execute(futures_sql, name)
41
+
42
+ futures.each do |future|
43
+ future.fulfill(ActiveRecord::Result.new(result.fields, result.to_a))
44
+ result = @connection.store_result if @connection.next_result
45
+ end
46
+
47
+ my_future.result
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+ module ActiveRecord
2
+ module Futures
3
+ module Delegation
4
+ delegate :future, to: :scoped
5
+ delegate *Futures.future_calculation_methods, to: :scoped
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,72 @@
1
+ module ActiveRecord
2
+ module Futures
3
+ class Future
4
+ class << self
5
+ def futures
6
+ Thread.current["#{self.name}_futures"] ||= []
7
+ end
8
+ alias_method :all, :futures
9
+
10
+ def current
11
+ Thread.current["#{self.name}_current"]
12
+ end
13
+
14
+ def current=(future)
15
+ Thread.current["#{self.name}_current"] = future
16
+ end
17
+
18
+ def clear
19
+ all.clear
20
+ end
21
+
22
+ def register(future)
23
+ self.futures << future
24
+ end
25
+
26
+ def flush
27
+ self.futures.each(&:load)
28
+ clear
29
+ end
30
+ end
31
+
32
+
33
+ attr_reader :result
34
+
35
+ def initialize
36
+ Future.register(self)
37
+ end
38
+
39
+ def fulfill(result)
40
+ @result = result
41
+ end
42
+
43
+ def fulfilled?
44
+ !result.nil?
45
+ end
46
+
47
+ def load
48
+ Future.current = self
49
+ execute
50
+ Future.current = nil
51
+ end
52
+
53
+ def inspect
54
+ to_a.inspect
55
+ end
56
+
57
+ def to_sql
58
+ end
59
+ undef_method :to_sql
60
+
61
+ private
62
+ def execute
63
+ end
64
+ undef_method :execute
65
+
66
+ def executed?
67
+ end
68
+ undef_method :executed?
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecord
2
+ module Futures
3
+ class FutureCalculation < Future
4
+ attr_reader :query, :execution
5
+ private :query, :execution
6
+
7
+ def initialize(query, execution)
8
+ super()
9
+ @query = query
10
+ @execution = execution
11
+ end
12
+
13
+ def value
14
+ Future.flush unless executed?
15
+ @value
16
+ end
17
+
18
+ def inspect
19
+ value.inspect
20
+ end
21
+
22
+ def to_sql
23
+ query
24
+ end
25
+
26
+ private
27
+
28
+ def execute
29
+ @value = execution.call
30
+ @executed = true
31
+ end
32
+
33
+ def executed?
34
+ @executed
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ module ActiveRecord
2
+ module Futures
3
+ class FutureRelation < Future
4
+ include Delegation
5
+ delegate :to_sql, to: :relation
6
+
7
+ attr_reader :relation
8
+ private :relation
9
+
10
+ def initialize(relation)
11
+ super()
12
+ @relation = relation
13
+ @klass = relation.klass
14
+ end
15
+
16
+ def to_a
17
+ # Flush all the futures upon first attempt to exec a future
18
+ Future.flush unless executed?
19
+ execute
20
+ end
21
+
22
+ private
23
+
24
+ def execute
25
+ relation.to_a
26
+ end
27
+
28
+ def executed?
29
+ relation.loaded?
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,69 @@
1
+ module ActiveRecord
2
+ module Futures
3
+ module QueryRecording
4
+
5
+ private
6
+ def record_query
7
+ orig_klass = @klass
8
+ connection = ConnectionProxy.new(@klass.connection)
9
+ @klass = KlassProxy.new(@klass, connection)
10
+ yield
11
+ connection.recorded_query
12
+ ensure
13
+ @klass = orig_klass
14
+ end
15
+
16
+ class KlassProxy
17
+ attr_reader :klass, :connection
18
+
19
+ def initialize(klass, connection)
20
+ @klass = klass
21
+ @connection = connection
22
+ end
23
+
24
+ def method_missing(method, *args, &block)
25
+ if klass.respond_to?(method)
26
+ klass.send(method, *args, &block)
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ def respond_to?(method, include_all = false)
33
+ super || klass.respond_to?(method, include_all)
34
+ end
35
+ end
36
+
37
+ class ConnectionProxy
38
+ attr_reader :connection
39
+ attr_accessor :recorded_query
40
+
41
+ def initialize(connection)
42
+ @connection = connection
43
+ end
44
+
45
+ def select_value(arel, name = nil)
46
+ self.recorded_query = arel.to_sql
47
+ nil
48
+ end
49
+
50
+ def select_all(arel, name = nil, binds = [])
51
+ self.recorded_query = arel.to_sql
52
+ []
53
+ end
54
+
55
+ def method_missing(method, *args, &block)
56
+ if connection.respond_to?(method)
57
+ connection.send(method, *args, &block)
58
+ else
59
+ super
60
+ end
61
+ end
62
+
63
+ def respond_to?(method, include_all = false)
64
+ super || connection.respond_to?(method, include_all)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ module ActiveRecord
2
+ module Futures
3
+ include QueryRecording
4
+
5
+ def self.original_calculation_methods
6
+ ActiveRecord::Calculations.public_instance_methods
7
+ end
8
+
9
+ def self.future_calculation_methods
10
+ original_calculation_methods.map { |name| "future_#{name}" }
11
+ end
12
+
13
+ def future
14
+ supports_futures = connection.respond_to?(:supports_futures?) &&
15
+ connection.supports_futures?
16
+
17
+ # simply pass through if the connection adapter does not support
18
+ # futures
19
+ supports_futures ? FutureRelation.new(self) : self
20
+ end
21
+
22
+ method_table = Hash[future_calculation_methods.zip(original_calculation_methods)]
23
+
24
+ # define a "future_" method for each calculation method
25
+ #
26
+ method_table.each do |future_method, method|
27
+ define_method(future_method) do |*args, &block|
28
+ exec = lambda { send(method, *args, &block) }
29
+ query = record_query(&exec)
30
+ FutureCalculation.new(query, exec)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module Activerecord
2
+ module Futures
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_record'
2
+ require 'active_support/core_ext/module/delegation'
3
+ require "activerecord-futures/version"
4
+
5
+ require "active_record/futures/future"
6
+ require "active_record/futures/future_relation"
7
+ require "active_record/futures/future_calculation"
8
+
9
+ require "active_record/futures/query_recording"
10
+ require "active_record/futures"
11
+ require "active_record/futures/delegation"
12
+
13
+ module ActiveRecord
14
+ class Relation
15
+ include Futures
16
+ end
17
+
18
+ class Base
19
+ extend Futures::Delegation
20
+ end
21
+ end
@@ -0,0 +1,56 @@
1
+ require "spec_helper"
2
+
3
+ module ActiveRecord::Futures
4
+ describe FutureRelation do
5
+ let(:relation) { double(ActiveRecord::Relation, klass: Class.new, to_a: nil) }
6
+ subject { FutureRelation.new(relation) }
7
+
8
+ describe ".new" do
9
+ before do
10
+ subject
11
+ end
12
+
13
+ it "gets registered" do
14
+ Future.all.should have(1).future
15
+ Future.all.first.should == subject
16
+ end
17
+
18
+ its(:relation) { should eq relation }
19
+
20
+ it { should_not be_fulfilled }
21
+ end
22
+
23
+ describe "#fulfill" do
24
+ let(:result) { "Some cool result" }
25
+
26
+ before do
27
+ subject.fulfill(result)
28
+ end
29
+
30
+ it { should be_fulfilled }
31
+ end
32
+
33
+ describe "#load" do
34
+ before do
35
+ relation.stub(:to_a) do
36
+ @current_future = Future.current
37
+ nil
38
+ end
39
+
40
+ subject.load
41
+ end
42
+
43
+ it "calls #to_a in the relation" do
44
+ relation.should have_received(:to_a)
45
+ end
46
+
47
+ it "sets the current future to itself while #to_a was being called in the relation" do
48
+ @current_future.should == subject
49
+ end
50
+
51
+ it "sets to nil the current future afterwards" do
52
+ Future.current.should == nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ module ActiveRecord::Futures
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
33
+
34
+ context "the futures in thread 1" do
35
+ let(:futures) { a_thread[futures_key] }
36
+
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
42
+
43
+ context "the futures in thread 2" do
44
+ let(:futures) { another_thread[futures_key] }
45
+
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
52
+ end
53
+
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
61
+
62
+ let(:another_thread) do
63
+ thread = Hash.new
64
+ end
65
+
66
+ before do
67
+ Thread.stub(:current).and_return(a_thread)
68
+
69
+ Future.current = "Future 1"
70
+
71
+ Thread.stub(:current).and_return(another_thread)
72
+
73
+ Future.current = "Future 2"
74
+ end
75
+
76
+ context "the current in thread 1" do
77
+ let(:current) { a_thread[current_key] }
78
+
79
+ specify { current.should eq "Future 1" }
80
+ end
81
+
82
+ context "the current in thread 2" do
83
+ let(:current) { another_thread[current_key] }
84
+
85
+ specify { current.should eq "Future 2" }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
data/spec/db/schema.rb ADDED
@@ -0,0 +1,16 @@
1
+ ActiveRecord::Schema.define(:version => 1) do
2
+
3
+ create_table "countries", :force => true do |t|
4
+ t.string "name"
5
+ t.datetime "created_at", :null => false
6
+ t.datetime "updated_at", :null => false
7
+ end
8
+
9
+ create_table "users", :force => true do |t|
10
+ t.string "name"
11
+ t.string "email"
12
+ t.datetime "created_at", :null => false
13
+ t.datetime "updated_at", :null => false
14
+ end
15
+
16
+ end
@@ -0,0 +1,73 @@
1
+ require "spec_helper"
2
+
3
+ module ActiveRecord::Futures
4
+ describe "Future fulfillment" do
5
+ subject { Future }
6
+
7
+ context "when futurizing a relation" do
8
+ let!(:future) { User.where(name: "").future }
9
+
10
+ its(:all) { should have(1).future }
11
+
12
+ context "the relation future" do
13
+ specify { future.should_not be_fulfilled }
14
+ end
15
+
16
+ context "and executing it" do
17
+ before { future.to_a }
18
+
19
+ its(:all) { should have(0).futures }
20
+
21
+ context "the future" do
22
+ subject { future }
23
+
24
+ it { should be_fulfilled }
25
+ end
26
+ end
27
+ end
28
+
29
+ context "when futurizing two relations" do
30
+ let!(:future) { User.where(name: "").future }
31
+ let!(:another_future) { Country.where(name: "").future }
32
+
33
+ its(:all) { should have(2).futures }
34
+
35
+ context "the first relation future" do
36
+ specify { future.should_not be_fulfilled }
37
+ end
38
+
39
+ context "the other relation future" do
40
+ specify { another_future.should_not be_fulfilled }
41
+ end
42
+
43
+ context "and executing one of them" do
44
+ before { future.to_a }
45
+
46
+ its(:all) { should have(0).futures }
47
+
48
+ context "the first relation future" do
49
+ specify { future.should be_fulfilled }
50
+ end
51
+
52
+ context "the other relation future" do
53
+ specify { another_future.should be_fulfilled }
54
+ end
55
+ end
56
+
57
+ context "and executing another non futurized relation" do
58
+ let!(:normal_relation) { User.where(name: "") }
59
+ before { normal_relation.to_a }
60
+
61
+ its(:all) { should have(2).futures }
62
+
63
+ context "the first relation future" do
64
+ specify { future.should_not be_fulfilled }
65
+ end
66
+
67
+ context "the other relation future" do
68
+ specify { another_future.should_not be_fulfilled }
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,2 @@
1
+ class Country < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,33 @@
1
+ require 'activerecord-futures'
2
+
3
+ config = {
4
+ adapter: "future_enabled_mysql2",
5
+ database: "test",
6
+ username: "root",
7
+ password: "root",
8
+ database: "activerecord_futures_test",
9
+ host: "localhost"
10
+ }
11
+
12
+ ActiveRecord::Base.establish_connection(config)
13
+ require 'db/schema'
14
+
15
+ Dir[File.join(File.dirname(__FILE__), 'models/**/*')].each { |f| require f }
16
+
17
+ require 'rspec-spies'
18
+
19
+ RSpec.configure do |config|
20
+ config.treat_symbols_as_metadata_keys_with_true_values = true
21
+ config.run_all_when_everything_filtered = true
22
+ config.filter_run :focus
23
+
24
+ # Run specs in random order to surface order dependencies. If you find an
25
+ # order dependency and want to debug it, you can fix the order by providing
26
+ # the seed, which is printed after each run.
27
+ # --seed 1234
28
+ config.order = 'random'
29
+
30
+ config.after do
31
+ ActiveRecord::Futures::Future.clear
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-futures
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Leonardo Andres Garcia Crespo
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.13
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.13
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec-spies
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Save unnecessary round trips to the database
63
+ email:
64
+ - leoasis@gmail.com
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - .gitignore
70
+ - .rspec
71
+ - Gemfile
72
+ - LICENSE.txt
73
+ - README.md
74
+ - Rakefile
75
+ - activerecord-futures.gemspec
76
+ - lib/active_record/connection_adapters/future_enabled_mysql2_adapter.rb
77
+ - lib/active_record/futures.rb
78
+ - lib/active_record/futures/delegation.rb
79
+ - lib/active_record/futures/future.rb
80
+ - lib/active_record/futures/future_calculation.rb
81
+ - lib/active_record/futures/future_relation.rb
82
+ - lib/active_record/futures/query_recording.rb
83
+ - lib/activerecord-futures.rb
84
+ - lib/activerecord-futures/version.rb
85
+ - spec/active_record/futures/future_relation_spec.rb
86
+ - spec/active_record/futures/future_spec.rb
87
+ - spec/db/schema.rb
88
+ - spec/in_action/future_fulfillment_spec.rb
89
+ - spec/models/country.rb
90
+ - spec/models/user.rb
91
+ - spec/spec_helper.rb
92
+ homepage: https://github.com/leoasis/activerecord-futures
93
+ licenses: []
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 1.8.24
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: Fetch all queries at once from the database and save round trips.
116
+ test_files:
117
+ - spec/active_record/futures/future_relation_spec.rb
118
+ - spec/active_record/futures/future_spec.rb
119
+ - spec/db/schema.rb
120
+ - spec/in_action/future_fulfillment_spec.rb
121
+ - spec/models/country.rb
122
+ - spec/models/user.rb
123
+ - spec/spec_helper.rb
124
+ has_rdoc: