activerecord-postgresql-cursors 0.0.1

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/MIT-LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2011 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.rdoc ADDED
@@ -0,0 +1,52 @@
1
+ == ActiveRecord PostgreSQL Cursors
2
+
3
+ This extension allows you to loop through record sets using cursors in an
4
+ Enumerable fashion. This allows you to cut down memory usage by only pulling
5
+ in individual records on each loop rather than pulling everything into
6
+ memory all at once.
7
+
8
+ To use a cursor, just change the first parameter to an
9
+ ActiveRecord::Base.find to :cursor instead of :first or :all or
10
+ whatever or use the ActiveRecord::Base.cursor method directly.
11
+
12
+ MyModel.find(:cursor, :conditions => 'some_column = true').each do |r|
13
+ puts r.inspect
14
+ end
15
+
16
+ MyModel.find(:cursor).collect { |r| r.foo / PI }.avg
17
+
18
+ MyModel.cursor.each do |r|
19
+ puts r.inspect
20
+ end
21
+
22
+ All ActiveRecord::Base.find options are available and should work as-is.
23
+ As a bonus, the PostgreSQLCursor object returned includes Enumerable,
24
+ so you can iterate to your heart's content.
25
+
26
+ This extension should work in both Rails 2.3 as well as Rails 3.
27
+
28
+ At the moment, this is a non-scrollable cursor -- it will only fetch
29
+ forward. Also note that these cursors are non-updateable/insensitive to
30
+ updates to the underlying data. You can write to the records themselves,
31
+ but changes outside of the cursor's transaction will not affect the
32
+ data being retrieved from the point of the cursor's creation, or rather
33
+ more specifically from the time you begin iterating.
34
+
35
+ The cursor itself is wrapped in a transaction as is required by PostgreSQL
36
+ and the cursor name is automatically generated using random numbers or
37
+ a name supplied during cursor creation. On raised SQL exceptions, the
38
+ transaction is ABORTed and the cursor CLOSEd.
39
+
40
+ Associations are handled, so you can use :include in your find options. Of
41
+ course, this requires some nonsense when moving the cursor around, but it
42
+ works all the same. In some cases, pre-loading and eager loading of
43
+ associations and whatnot creates an initial query that will grab the initial
44
+ IDs of the model being fetched and then create the actual cursor query out
45
+ of those large joins that ActiveRecord sometimes generates. This larger
46
+ query will be the query that's wrapped in the transaction and in a cursor,
47
+ while the first query is just used to build the larger joined query. This
48
+ allows for a brief window between the point that the cursor query is
49
+ created and the time it is executed. In these cases, it may be wise to wrap
50
+ your use or cursors in your own transaction to ensure that changes made to
51
+ the underlying data don't interfere with your cursor's visbility.
52
+
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'rubygems/package_task'
6
+ require 'rake/testtask'
7
+ require 'rdoc/task'
8
+
9
+ $:.push 'lib'
10
+
11
+ version = File.read('VERSION') rescue ''
12
+
13
+ begin
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ gem.name = "activerecord-postgresql-cursors"
17
+ gem.summary = "Provides some support for PostgreSQL cursors in ActiveRecord."
18
+ gem.description = gem.summary
19
+ gem.email = "code@zoocasa.com"
20
+ gem.homepage = "http://github.com/zoocasa/activerecord-postgresql-cursors"
21
+ gem.authors = [ "J Smith" ]
22
+ end
23
+ Jeweler::GemcutterTasks.new
24
+ rescue LoadError
25
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
26
+ end
27
+
28
+ desc 'Test PostgreSQL extensions'
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.pattern = 'test/**/*_test.rb'
31
+ t.verbose = false
32
+ end
33
+
34
+ desc 'Build docs'
35
+ Rake::RDocTask.new do |t|
36
+ require 'rdoc'
37
+ t.title = "ActiveRecord PostgreSQL Cursors #{version}"
38
+ t.main = 'README.rdoc'
39
+ t.rdoc_dir = 'doc'
40
+ t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb')
41
+ end
42
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,45 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{activerecord-postgresql-cursors}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{J Smith}]
12
+ s.date = %q{2011-07-11}
13
+ s.description = %q{Provides some support for PostgreSQL cursors in ActiveRecord.}
14
+ s.email = %q{code@zoocasa.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "MIT-LICENSE",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "activerecord-postgresql-cursors.gemspec",
24
+ "init.rb",
25
+ "lib/activerecord-postgresql-cursors.rb",
26
+ "lib/postgresql_cursors_2.rb",
27
+ "lib/postgresql_cursors_3.rb",
28
+ "test/cursor_test.rb",
29
+ "test/test_helper.rb"
30
+ ]
31
+ s.homepage = %q{http://github.com/zoocasa/activerecord-postgresql-cursors}
32
+ s.require_paths = [%q{lib}]
33
+ s.rubygems_version = %q{1.8.5}
34
+ s.summary = %q{Provides some support for PostgreSQL cursors in ActiveRecord.}
35
+
36
+ if s.respond_to? :specification_version then
37
+ s.specification_version = 3
38
+
39
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
40
+ else
41
+ end
42
+ else
43
+ end
44
+ end
45
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+
2
+ require File.join(File.dirname(__FILE__), 'lib', 'postgresql_cursors')
@@ -0,0 +1,109 @@
1
+
2
+ module ActiveRecord
3
+ # Exception raised when database cursors aren't supported, which they
4
+ # absolutely should be in our app.
5
+ class CursorsNotSupported < ActiveRecordError; end
6
+
7
+ module Associations
8
+ module ClassMethods
9
+ class JoinDependency
10
+ # Extra method we can use to clear out a couple of things in
11
+ # JoinDependency so we can use some of the methods for our
12
+ # cursors code.
13
+ def clear_with_cursor
14
+ @reflections = []
15
+ @base_records_hash = {}
16
+ @base_records_in_order = []
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ # PostgreSQLCursor is an Enumerable class so you can use each, map,
23
+ # any? and all of those nice Enumerable methods.
24
+ #
25
+ # At the moment, cursors aren't scrollable and are fetch forward-only
26
+ # and read-only.
27
+ #
28
+ # This class isn't really meant to be used outside of the
29
+ # ActiveRecord::Base#find method.
30
+ class PostgreSQLCursor
31
+ include Enumerable
32
+
33
+ attr_accessor :cursor_name
34
+
35
+ def initialize(model, cursor_name, query, join_dependency = nil)
36
+ @model = model
37
+ @cursor_name = if cursor_name
38
+ @model.connection.quote_table_name(cursor_name.gsub(/"/, '\"'))
39
+ end
40
+ @query = query
41
+ @join_dependency = join_dependency
42
+ end
43
+
44
+ def inspect
45
+ %{#<ActiveRecord::PostgreSQLCursor cursor_name: "#{cursor_name}", query: "#{@query}">}
46
+ end
47
+
48
+ # Calls block once for each record in the cursor, passing that
49
+ # record as a parameter.
50
+ def each
51
+ @model.transaction do
52
+ begin
53
+ declare_cursor
54
+ if @join_dependency
55
+ rows = Array.new
56
+ last_id = nil
57
+ while row = fetch_forward
58
+ current_id = row[@join_dependency.join_base.aliased_primary_key]
59
+ last_id ||= current_id
60
+ if last_id == current_id
61
+ rows << row
62
+ last_id = current_id
63
+ else
64
+ yield @join_dependency.instantiate(rows).first
65
+ @join_dependency.clear_with_cursor
66
+ rows = [ row ]
67
+ end
68
+ last_id = current_id
69
+ end
70
+
71
+ if !rows.empty?
72
+ yield @join_dependency.instantiate(rows).first
73
+ end
74
+ else
75
+ while row = fetch_forward
76
+ yield row
77
+ end
78
+ end
79
+ ensure
80
+ close_cursor
81
+ end
82
+ end
83
+ nil
84
+ end
85
+
86
+ private
87
+ def cursor_name
88
+ @cursor_name ||= "cursor_#{(rand * 1000000).ceil}"
89
+ end
90
+
91
+ def fetch_forward #:nodoc:
92
+ @model.find_by_sql(%{FETCH FORWARD FROM #{cursor_name}}).first
93
+ end
94
+
95
+ def declare_cursor #:nodoc:
96
+ @model.connection.execute(%{DECLARE #{cursor_name} CURSOR FOR #{@query}})
97
+ end
98
+
99
+ def close_cursor #:nodoc:
100
+ @model.connection.execute(%{CLOSE #{cursor_name}})
101
+ end
102
+ end
103
+ end
104
+
105
+ if ActiveRecord::VERSION::MAJOR >= 3
106
+ require File.join(File.dirname(__FILE__), 'postgresql_cursors_3')
107
+ else
108
+ require File.join(File.dirname(__FILE__), 'postgresql_cursors_2')
109
+ end
@@ -0,0 +1,75 @@
1
+
2
+ module ActiveRecord
3
+ class Base
4
+ class << self
5
+ # Override ActiveRecord::Base#find to allow for cursors in
6
+ # PostgreSQL. To use a cursor, set the first argument of
7
+ # find to :cursor. A PostgreSQLCursor object will be returned,
8
+ # which can then be used as an Enumerable to loop through the
9
+ # results.
10
+ #
11
+ # By default, cursor names are generated automatically using
12
+ # "cursor_#{rand}", where rand is a big ol' random number that
13
+ # is pretty unlikely to clash if you're using nested cursors.
14
+ # Alternatively, you can supply a specific cursor name by
15
+ # supplying a :cursor_name option.
16
+ def find_with_cursors *args
17
+ if args.first.to_s == 'cursor'
18
+ options = args.extract_options!
19
+ cursor_name = options.delete(:cursor_name)
20
+ validate_find_options(options)
21
+ set_readonly_option!(options)
22
+ find_cursor(cursor_name, options)
23
+ else
24
+ find_without_cursors(*args)
25
+ end
26
+ end
27
+ alias_method_chain :find, :cursors
28
+
29
+ def cursor(*args)
30
+ find(:cursor, *args)
31
+ end
32
+ end
33
+
34
+ private
35
+ # Find method for using cursors. This works just like the regular
36
+ # ActiveRecord::Base#find_every method, except it returns a
37
+ # PostgreSQLCursor object that can be used to loop through records.
38
+ def self.find_cursor(cursor_name, options)
39
+ unless connection.is_a? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
40
+ raise CursorsNotSupported, "#{connection.class} doesn't support cursors"
41
+ end
42
+
43
+ catch :invalid_query do
44
+ if options[:include]
45
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
46
+ return ActiveRecord::PostgreSQLCursor.new(
47
+ self,
48
+ cursor_name,
49
+ construct_finder_sql_with_included_associations(
50
+ options,
51
+ join_dependency
52
+ ),
53
+ join_dependency
54
+ )
55
+ else
56
+ return ActiveRecord::PostgreSQLCursor.new(
57
+ self,
58
+ cursor_name,
59
+ construct_finder_sql(
60
+ options
61
+ )
62
+ )
63
+ end
64
+ end
65
+ nil
66
+ end
67
+ end
68
+
69
+ class PostgreSQLCursor
70
+ def initialize_with_rails_2(model, cursor_name, query, join_dependency = nil)
71
+ initialize_without_rails_2(model, cursor_name, query, join_dependency)
72
+ end
73
+ alias_method_chain :initialize, :rails_2
74
+ end
75
+ end
@@ -0,0 +1,75 @@
1
+
2
+ module ActiveRecord
3
+ module CursorExtensions
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ alias_method_chain :find, :cursors
8
+ end
9
+
10
+ # Override ActiveRecord::Base#find to allow for cursors in
11
+ # PostgreSQL. To use a cursor, set the first argument of
12
+ # find to :cursor. A PostgreSQLCursor object will be returned,
13
+ # which can then be used as an Enumerable to loop through the
14
+ # results.
15
+ #
16
+ # By default, cursor names are generated automatically using
17
+ # "cursor_#{rand}", where rand is a big ol' random number that
18
+ # is pretty unlikely to clash if you're using nested cursors.
19
+ # Alternatively, you can supply a specific cursor name by
20
+ # supplying a :cursor_name option.
21
+ def find_with_cursors(*args)
22
+ if args.first.to_s == 'cursor'
23
+ options = args.extract_options!
24
+ cursor_name = options.delete(:cursor_name)
25
+ find_cursor(cursor_name, options)
26
+ else
27
+ find_without_cursors(*args)
28
+ end
29
+ end
30
+
31
+ def cursor(*args)
32
+ find_with_cursors('cursor', *args)
33
+ end
34
+
35
+ private
36
+ # Find method for using cursors. This works just like the regular
37
+ # ActiveRecord::Base#find_every method, except it returns a
38
+ # PostgreSQLCursor object that can be used to loop through records.
39
+ def find_cursor(cursor_name, options)
40
+ unless connection.is_a? ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
41
+ raise CursorsNotSupported, "#{connection.class} doesn't support cursors"
42
+ end
43
+
44
+ relation = apply_finder_options(options)
45
+ including = (relation.eager_load_values + relation.includes_values).uniq
46
+
47
+ if including.present?
48
+ join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil)
49
+ join_relation = relation.construct_relation_for_association_find(join_dependency)
50
+
51
+ ActiveRecord::PostgreSQLCursor.new(self, cursor_name, join_relation, join_dependency)
52
+ else
53
+ ActiveRecord::PostgreSQLCursor.new(self, cursor_name, relation)
54
+ end
55
+ end
56
+ end
57
+
58
+ class PostgreSQLCursor
59
+ def initialize_with_rails_3(model, cursor_name, relation, join_dependency = nil)
60
+ @relation = relation
61
+ initialize_without_rails_3(model, cursor_name, relation.to_sql, join_dependency)
62
+ end
63
+ alias_method_chain :initialize, :rails_3
64
+ end
65
+ end
66
+
67
+ class ActiveRecord::Relation
68
+ include ActiveRecord::CursorExtensions
69
+ end
70
+
71
+ class ActiveRecord::Base
72
+ class << self
73
+ delegate :cursor, :to => :scoped
74
+ end
75
+ end
@@ -0,0 +1,97 @@
1
+
2
+ $: << File.dirname(__FILE__)
3
+ require 'test_helper'
4
+
5
+ class PostgreSQLCursorTests < Test::Unit::TestCase
6
+ include PostgreSQLCursorTestHelper
7
+
8
+ def test_find_cursor
9
+ cursor = Foo.find(:cursor, :order => 'id')
10
+
11
+ assert(cursor.is_a?(ActiveRecord::PostgreSQLCursor))
12
+
13
+ assert_equal(%w{ one two three four five }, cursor.collect(&:name))
14
+ end
15
+
16
+ def test_cursor_scoped
17
+ cursor = Foo.cursor(:order => 'id')
18
+
19
+ assert(cursor.is_a?(ActiveRecord::PostgreSQLCursor))
20
+
21
+ assert_equal(%w{ one two three four five }, cursor.collect(&:name))
22
+ end
23
+
24
+ def test_cursor_while_updating
25
+ cursor = Foo.cursor(:order => 'id')
26
+
27
+ cursor.each do |row|
28
+ row.name = "#{row.name}_updated"
29
+ assert(row.save)
30
+ end
31
+
32
+ assert_equal(%w{ one_updated two_updated three_updated four_updated five_updated }, cursor.collect(&:name))
33
+ end
34
+
35
+ def test_with_associations
36
+ cursor = Foo.cursor(:order => 'id')
37
+
38
+ cursor.each do |row|
39
+ assert(row.is_a?(Foo))
40
+ row.bars.each do |bar|
41
+ assert(bar.is_a?(Bar))
42
+ end
43
+ end
44
+ end
45
+
46
+ def test_with_associations_eager_loading
47
+ cursor = Foo.cursor(:order => 'foos.id', :include => :bars)
48
+
49
+ cursor.each do |row|
50
+ assert(row.is_a?(Foo))
51
+ row.bars.each do |bar|
52
+ assert(bar.is_a?(Bar))
53
+ end
54
+ end
55
+ end
56
+
57
+ def test_nested_cursors
58
+ cursor = Foo.cursor(:order => 'foos.id')
59
+
60
+ cursor.each do |row|
61
+ bars_cursor = row.bars.cursor
62
+ assert(bars_cursor.is_a?(ActiveRecord::PostgreSQLCursor))
63
+
64
+ bars_cursor.each do |bar|
65
+ assert(bar.is_a?(Bar))
66
+ end
67
+ end
68
+ end
69
+
70
+ if ActiveRecord::VERSION::MAJOR >= 3
71
+ def test_as_relation
72
+ cursor = Foo.order('foos.id').where('foos.id >= 3').cursor
73
+ assert_equal(3, cursor.to_a.length)
74
+
75
+ cursor.each do |row|
76
+ assert(row.is_a?(Foo))
77
+ assert_equal(2, row.bars.length)
78
+ row.bars.each do |bar|
79
+ assert(bar.is_a?(Bar))
80
+ end
81
+ end
82
+ end
83
+
84
+ def test_as_relation_with_associations
85
+ cursor = Foo.includes(:bars).order('foos.id').where('foos.id >= 3').cursor
86
+ assert_equal(3, cursor.to_a.length)
87
+
88
+ cursor.each do |row|
89
+ assert(row.is_a?(Foo))
90
+ assert_equal(2, row.bars.length)
91
+ row.bars.each do |bar|
92
+ assert(bar.is_a?(Bar))
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,68 @@
1
+
2
+ ACTIVERECORD_GEM_VERSION = ENV['ACTIVERECORD_GEM_VERSION'] || '~> 3.0.3'
3
+
4
+ require 'rubygems'
5
+ gem 'activerecord', ACTIVERECORD_GEM_VERSION
6
+
7
+ require 'active_support'
8
+ require 'active_support/core_ext/module/aliasing'
9
+ require 'active_record'
10
+ require 'test/unit'
11
+ require 'logger'
12
+ require File.join(File.dirname(__FILE__), *%w{ .. lib activerecord-postgresql-cursors })
13
+
14
+ puts "Testing against ActiveRecord #{Gem.loaded_specs['activerecord'].version.to_s}"
15
+
16
+ ActiveRecord::Base.logger = Logger.new("debug.log")
17
+ ActiveRecord::Base.configurations = {
18
+ 'arunit' => {
19
+ :adapter => 'postgresql',
20
+ :database => 'postgresql_cursors_unit_tests',
21
+ :min_messages => 'warning',
22
+ :schema_search_path => 'public'
23
+ }
24
+ }
25
+
26
+ ActiveRecord::Base.establish_connection 'arunit'
27
+ ARBC = ActiveRecord::Base.connection
28
+
29
+ if !ARBC.table_exists?('foos')
30
+ ActiveRecord::Migration.create_table(:foos) do |t|
31
+ t.text :name
32
+ end
33
+ end
34
+
35
+ if !ARBC.table_exists?('bars')
36
+ ActiveRecord::Migration.create_table(:bars) do |t|
37
+ t.text :name
38
+ t.integer :foo_id
39
+ end
40
+ end
41
+
42
+
43
+ class Bar < ActiveRecord::Base
44
+ belongs_to :foo
45
+ end
46
+
47
+ class Foo < ActiveRecord::Base
48
+ has_many :bars
49
+ end
50
+
51
+ module PostgreSQLCursorTestHelper
52
+ def setup
53
+ Foo.delete_all
54
+ Bar.delete_all
55
+ ARBC.execute(%{select setval('foos_id_seq', 1, false)})
56
+ ARBC.execute(%{select setval('bars_id_seq', 1, false)})
57
+
58
+ %w{ six seven eight nine ten eleven twelve thirteen fourteen fifteen }.each do |name|
59
+ Bar.create(:name => name)
60
+ end
61
+
62
+ %w{ one two three four five }.each_with_index do |name, i|
63
+ foo = Foo.new(:name => name)
64
+ foo.bar_ids = [ i + 1, i + 6 ]
65
+ foo.save
66
+ end
67
+ end
68
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-postgresql-cursors
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - J Smith
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-07-11 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: Provides some support for PostgreSQL cursors in ActiveRecord.
22
+ email: code@zoocasa.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - MIT-LICENSE
31
+ - README.rdoc
32
+ - Rakefile
33
+ - VERSION
34
+ - activerecord-postgresql-cursors.gemspec
35
+ - init.rb
36
+ - lib/activerecord-postgresql-cursors.rb
37
+ - lib/postgresql_cursors_2.rb
38
+ - lib/postgresql_cursors_3.rb
39
+ - test/cursor_test.rb
40
+ - test/test_helper.rb
41
+ homepage: http://github.com/zoocasa/activerecord-postgresql-cursors
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ hash: 3
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ hash: 3
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.5
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Provides some support for PostgreSQL cursors in ActiveRecord.
74
+ test_files: []
75
+