New Upstream Release - ruby-will-paginate

Ready changes

Summary

Merged new upstream version: 4.0.0 (was: 3.3.1).

Resulting package

Built on 2023-06-20T15:42 (took 7m29s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases ruby-will-paginate

Lintian Result

Diff

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 16385b4..37184af 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -14,24 +14,45 @@ jobs:
         - '2.6'
         - '2.7'
         - '3.0'
+        - '3.1'
+        - '3.2'
         gemfile:
         - Gemfile
         - environments/Gemfile.rails5.0.rb
         - environments/Gemfile.rails5.1.rb
         - environments/Gemfile.rails5.2.rb
         - environments/Gemfile.rails6.0.rb
+        - environments/Gemfile.rails6.1.rb
         - environments/Gemfile.rails-edge.rb
         exclude:
         - ruby: '2.4'
           gemfile: Gemfile
+        - ruby: '2.5'
+          gemfile: Gemfile
+        - ruby: '2.6'
+          gemfile: Gemfile
         - ruby: '3.0'
           gemfile: environments/Gemfile.rails5.0.rb
+        - ruby: '3.1'
+          gemfile: environments/Gemfile.rails5.0.rb
+        - ruby: '3.2'
+          gemfile: environments/Gemfile.rails5.0.rb
         - ruby: '3.0'
           gemfile: environments/Gemfile.rails5.1.rb
+        - ruby: '3.1'
+          gemfile: environments/Gemfile.rails5.1.rb
+        - ruby: '3.2'
+          gemfile: environments/Gemfile.rails5.1.rb
         - ruby: '3.0'
           gemfile: environments/Gemfile.rails5.2.rb
+        - ruby: '3.1'
+          gemfile: environments/Gemfile.rails5.2.rb
+        - ruby: '3.2'
+          gemfile: environments/Gemfile.rails5.2.rb
         - ruby: '2.4'
           gemfile: environments/Gemfile.rails6.0.rb
+        - ruby: '2.4'
+          gemfile: environments/Gemfile.rails6.1.rb
         - ruby: '2.4'
           gemfile: environments/Gemfile.rails-edge.rb
         - ruby: '2.5'
@@ -57,7 +78,7 @@ jobs:
         ports:
         - 5432:5432
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - uses: ruby/setup-ruby@v1
       with:
         ruby-version: "${{ matrix.ruby }}"
@@ -92,6 +113,8 @@ jobs:
         - '2.6'
         - '2.7'
         - '3.0'
+        - '3.1'
+        - '3.2'
     runs-on: ubuntu-latest
     env:
       BUNDLE_GEMFILE: environments/Gemfile.non-rails.rb
@@ -101,7 +124,7 @@ jobs:
         ports:
         - 27017:27017
     steps:
-    - uses: actions/checkout@v2
+    - uses: actions/checkout@v3
     - uses: ruby/setup-ruby@v1
       with:
         ruby-version: "${{ matrix.ruby }}"
diff --git a/Gemfile b/Gemfile
index a8555b5..4309692 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,13 +1,13 @@
 source 'https://rubygems.org'
 
 # We test against other Rails versions, too. See `environments/`
-rails_version = '~> 6.1.2'
+rails_version = '~> 7.0.2'
 
 gem 'activerecord', rails_version
 gem 'actionpack',   rails_version
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.4.0'
 gem 'mysql2', '~> 0.5.2', :group => :mysql
diff --git a/README.md b/README.md
index d867456..a7d788f 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
 # will_paginate
 
-will_paginate is a pagination library that integrates with Ruby on Rails, Sinatra, Hanami::View, Merb, DataMapper and Sequel.
+will_paginate is a pagination library that integrates with Ruby on Rails, Sinatra, Hanami::View, and Sequel.
 
 ``` ruby
-gem 'will_paginate', '~> 3.1.0'
+gem 'will_paginate', '~> 3.3'
 ```
 
 See [installation instructions][install] on the wiki for more info.
diff --git a/debian/changelog b/debian/changelog
index 5d71a72..79393bb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ruby-will-paginate (4.0.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 20 Jun 2023 15:35:59 -0000
+
 ruby-will-paginate (3.3.1-1) unstable; urgency=medium
 
   * Team upload.
diff --git a/environments/Gemfile.non-rails.rb b/environments/Gemfile.non-rails.rb
index a46ba60..dea8b8b 100644
--- a/environments/Gemfile.non-rails.rb
+++ b/environments/Gemfile.non-rails.rb
@@ -1,13 +1,9 @@
 source 'https://rubygems.org'
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.4.0'
 
 gem 'sequel', '~> 5.29'
-gem 'dm-core'
-gem 'dm-aggregates'
-gem 'dm-migrations'
-gem 'dm-sqlite-adapter'
 gem 'mongoid', '~> 7.2.0'
diff --git a/environments/Gemfile.rails-edge.rb b/environments/Gemfile.rails-edge.rb
index f933bb8..4abfd04 100644
--- a/environments/Gemfile.rails-edge.rb
+++ b/environments/Gemfile.rails-edge.rb
@@ -5,8 +5,8 @@ gem 'actionpack',   git: 'https://github.com/rails/rails.git', branch: 'main'
 
 gem 'thread_safe'
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.4.0'
 
diff --git a/environments/Gemfile.rails5.0.rb b/environments/Gemfile.rails5.0.rb
index a42e5c0..0a01c38 100644
--- a/environments/Gemfile.rails5.0.rb
+++ b/environments/Gemfile.rails5.0.rb
@@ -6,10 +6,13 @@ gem 'activerecord', rails_version
 gem 'actionpack',   rails_version
 gem 'rails-dom-testing'
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.3.6'
 
 gem 'mysql2', '~> 0.5.2', :group => :mysql
 gem 'pg', '~> 1.2.3', :group => :pg
+
+# ruby 2.4 compat re: nokogiri
+gem 'loofah', '< 2.21.0'
diff --git a/environments/Gemfile.rails5.1.rb b/environments/Gemfile.rails5.1.rb
index 6c06a33..a105adc 100644
--- a/environments/Gemfile.rails5.1.rb
+++ b/environments/Gemfile.rails5.1.rb
@@ -5,10 +5,13 @@ rails_version = '~> 5.1.7'
 gem 'activerecord', rails_version
 gem 'actionpack',   rails_version
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.3.6'
 
 gem 'mysql2', '~> 0.5.2', :group => :mysql
 gem 'pg', '~> 1.2.3', :group => :pg
+
+# ruby 2.4 compat re: nokogiri
+gem 'loofah', '< 2.21.0'
diff --git a/environments/Gemfile.rails5.2.rb b/environments/Gemfile.rails5.2.rb
index ad8755c..c3d08a8 100644
--- a/environments/Gemfile.rails5.2.rb
+++ b/environments/Gemfile.rails5.2.rb
@@ -5,9 +5,12 @@ rails_version = '~> 5.2.4'
 gem 'activerecord', rails_version
 gem 'actionpack',   rails_version
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.3.6'
 gem 'mysql2', '~> 0.5.2', :group => :mysql
 gem 'pg', '~> 1.2.3', :group => :pg
+
+# ruby 2.4 compat re: nokogiri
+gem 'loofah', '< 2.21.0'
diff --git a/environments/Gemfile.rails6.0.rb b/environments/Gemfile.rails6.0.rb
index 442a65b..07df692 100644
--- a/environments/Gemfile.rails6.0.rb
+++ b/environments/Gemfile.rails6.0.rb
@@ -5,8 +5,8 @@ rails_version = '~> 6.0.0'
 gem 'activerecord', rails_version
 gem 'actionpack',   rails_version
 
-gem 'rspec', '~> 2.99'
-gem 'mocha', '~> 0.9.8'
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
 
 gem 'sqlite3', '~> 1.4.0'
 
diff --git a/environments/Gemfile.rails6.1.rb b/environments/Gemfile.rails6.1.rb
new file mode 100644
index 0000000..3a7f2f6
--- /dev/null
+++ b/environments/Gemfile.rails6.1.rb
@@ -0,0 +1,14 @@
+source 'https://rubygems.org'
+
+rails_version = '~> 6.1.0'
+
+gem 'activerecord', rails_version
+gem 'actionpack',   rails_version
+
+gem 'rspec', '~> 3.12'
+gem 'mocha', '~> 2.0'
+
+gem 'sqlite3', '~> 1.4.0'
+
+gem 'mysql2', '~> 0.5.2', :group => :mysql
+gem 'pg', '~> 1.2', :group => :pg
diff --git a/lib/will_paginate.rb b/lib/will_paginate.rb
index 5821d34..c2f9bc8 100644
--- a/lib/will_paginate.rb
+++ b/lib/will_paginate.rb
@@ -8,18 +8,6 @@ elsif defined?(Rails::Initializer)
   raise "will_paginate 3.0 is not compatible with Rails 2.3 or older"
 end
 
-if defined?(Merb::AbstractController)
-  require 'will_paginate/view_helpers/merb'
-
-  Merb::BootLoader.before_app_loads do
-    adapters = { :datamapper => 'data_mapper', :activerecord => 'active_record', :sequel => 'sequel' }
-    # auto-load the right ORM adapter
-    if adapter = adapters[Merb.orm]
-      require "will_paginate/#{adapter}"
-    end
-  end
-end
-
 if defined?(Sinatra) and Sinatra.respond_to? :register
   require 'will_paginate/view_helpers/sinatra'
 end
diff --git a/lib/will_paginate/data_mapper.rb b/lib/will_paginate/data_mapper.rb
deleted file mode 100644
index 6156214..0000000
--- a/lib/will_paginate/data_mapper.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-require 'dm-core'
-require 'dm-aggregates'
-require 'will_paginate/per_page'
-require 'will_paginate/page_number'
-require 'will_paginate/collection'
-
-module WillPaginate
-  module DataMapper
-    module Pagination
-      def page(num)
-        pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
-        per_page = query.limit || self.per_page
-        options = {:offset => pagenum.to_offset(per_page).to_i}
-        options[:limit] = per_page unless query.limit
-        col = new_collection(query.merge(options))
-        col.current_page = pagenum
-        col
-      end
-
-      def paginate(options)
-        options  = options.dup
-        pagenum  = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
-        per_page = options.delete(:per_page) || self.per_page
-        total    = options.delete(:total_entries)
-
-        options.delete(:page)
-        options[:limit] = per_page.to_i
-
-
-        col = all(options).page(pagenum)
-        col.total_entries = total.to_i unless total.nil? || (total.kind_of?(String) && total.strip.empty?)
-        col
-      end
-    end
-
-    module CollectionMethods
-      include WillPaginate::CollectionMethods
-
-      attr_accessor :current_page
-      attr_writer :total_entries
-
-      def paginated?
-        !current_page.nil?
-      end
-
-      def per_page
-        query.limit || model.per_page
-      end
-
-      def offset
-        query.offset
-      end
-
-      def total_entries
-        @total_entries ||= begin
-          if loaded? and @array.size < per_page and (current_page == 1 or @array.size > 0)
-            offset + @array.size
-          else
-            # :reload prevents Collection.filter from being run, which
-            # would cause a stack overflow
-            clean_query = query.merge(:reload => true)
-            # seems like the only way
-            clean_query.instance_variable_set('@limit', nil)
-            clean_query.instance_variable_set('@offset', 0)
-            new_collection(clean_query).count
-          end
-        end
-      end
-
-      def to_a
-        if paginated?
-          ::WillPaginate::Collection.create(current_page, per_page) do |col|
-            col.replace super
-            col.total_entries ||= total_entries
-          end
-        else
-          super
-        end
-      end
-
-      private
-
-      def new_collection(query, resources = nil)
-        col = super
-        col.current_page = self.current_page
-        col
-      end
-
-      def initialize_copy(original)
-        super
-        @total_entries = nil
-      end
-    end
-
-    ::DataMapper::Model.append_extensions PerPage
-    ::DataMapper::Model.append_extensions Pagination
-    ::DataMapper::Collection.send(:include, Pagination)
-    ::DataMapper::Collection.send(:include, CollectionMethods)
-  end
-end
diff --git a/lib/will_paginate/locale/en.yml b/lib/will_paginate/locale/en.yml
index bf24f00..fd0312b 100644
--- a/lib/will_paginate/locale/en.yml
+++ b/lib/will_paginate/locale/en.yml
@@ -1,7 +1,9 @@
 en:
   will_paginate:
     previous_label: "&#8592; Previous"
+    previous_aria_label: "Previous page"
     next_label: "Next &#8594;"
+    next_aria_label: "Next page"
     page_gap: "&hellip;"
     container_aria_label: "Pagination"
     page_aria_label: "Page %{page}"
diff --git a/lib/will_paginate/version.rb b/lib/will_paginate/version.rb
index bec68bf..25cacfe 100644
--- a/lib/will_paginate/version.rb
+++ b/lib/will_paginate/version.rb
@@ -1,8 +1,8 @@
 module WillPaginate #:nodoc:
   module VERSION #:nodoc:
-    MAJOR = 3
-    MINOR = 3
-    TINY  = 1
+    MAJOR = 4
+    MINOR = 0
+    TINY  = 0
 
     STRING = [MAJOR, MINOR, TINY].join('.')
   end
diff --git a/lib/will_paginate/view_helpers/action_view.rb b/lib/will_paginate/view_helpers/action_view.rb
index b583a9a..e12f837 100644
--- a/lib/will_paginate/view_helpers/action_view.rb
+++ b/lib/will_paginate/view_helpers/action_view.rb
@@ -119,8 +119,12 @@ module WillPaginate
       end
 
       def merge_get_params(url_params)
-        if @template.respond_to? :request and @template.request and @template.request.get?
-          symbolized_update(url_params, @template.params, GET_PARAMS_BLACKLIST)
+        if @template.respond_to?(:request) and @template.request
+          if @template.request.get?
+            symbolized_update(url_params, @template.params, GET_PARAMS_BLACKLIST)
+          elsif @template.request.respond_to?(:query_parameters)
+            symbolized_update(url_params, @template.request.query_parameters, GET_PARAMS_BLACKLIST)
+          end
         end
         url_params
       end
diff --git a/lib/will_paginate/view_helpers/link_renderer.rb b/lib/will_paginate/view_helpers/link_renderer.rb
index c35fb7c..f430492 100644
--- a/lib/will_paginate/view_helpers/link_renderer.rb
+++ b/lib/will_paginate/view_helpers/link_renderer.rb
@@ -59,19 +59,21 @@ module WillPaginate
       
       def previous_page
         num = @collection.current_page > 1 && @collection.current_page - 1
-        previous_or_next_page(num, @options[:previous_label], 'previous_page')
+        aria_label = @template.will_paginate_translate(:previous_aria_label) { "Previous page" }
+        previous_or_next_page(num, @options[:previous_label], 'previous_page', aria_label)
       end
       
       def next_page
         num = @collection.current_page < total_pages && @collection.current_page + 1
-        previous_or_next_page(num, @options[:next_label], 'next_page')
+        aria_label = @template.will_paginate_translate(:next_aria_label) { "Next page" }
+        previous_or_next_page(num, @options[:next_label], 'next_page', aria_label)
       end
       
-      def previous_or_next_page(page, text, classname)
+      def previous_or_next_page(page, text, classname, aria_label = nil)
         if page
-          link(text, page, :class => classname)
+          link(text, page, :class => classname, :'aria-label' => aria_label)
         else
-          tag(:span, text, :class => classname + ' disabled', :"aria-disabled" => true)
+          tag(:span, text, :class => classname + ' disabled', :'aria-label' => aria_label)
         end
       end
       
diff --git a/lib/will_paginate/view_helpers/merb.rb b/lib/will_paginate/view_helpers/merb.rb
deleted file mode 100644
index d9b3a6b..0000000
--- a/lib/will_paginate/view_helpers/merb.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-require 'will_paginate/core_ext'
-require 'will_paginate/view_helpers'
-require 'will_paginate/view_helpers/link_renderer'
-
-module WillPaginate
-  module Merb
-    include ViewHelpers
-
-    def will_paginate(collection, options = {}) #:nodoc:
-      options = options.merge(:renderer => LinkRenderer) unless options[:renderer]
-      super(collection, options)
-    end
-
-    class LinkRenderer < ViewHelpers::LinkRenderer
-      protected
-
-      def url(page)
-        params = @template.request.params.except(:action, :controller).merge(param_name => page)
-        @template.url(:this, params)
-      end
-    end
-
-    ::Merb::AbstractController.send(:include, self)
-  end
-end
-
diff --git a/script/ci-matrix b/script/ci-matrix
index 703bbe4..f773f80 100755
--- a/script/ci-matrix
+++ b/script/ci-matrix
@@ -12,7 +12,8 @@ requirements = {
   'environments/Gemfile.rails5.1.rb'    => ['>= 2.2', '< 3.0'],
   'environments/Gemfile.rails5.2.rb'    => ['>= 2.2', '< 3.0'],
   'environments/Gemfile.rails6.0.rb'    => '>= 2.5',
-  'Gemfile'                             => '>= 2.5',
+  'environments/Gemfile.rails6.1.rb'    => '>= 2.5',
+  'Gemfile'                             => '>= 2.7',
   'environments/Gemfile.rails-edge.rb'  => '>= 2.7',
 }
 
diff --git a/script/release b/script/release
index bf0f750..67a657d 100755
--- a/script/release
+++ b/script/release
@@ -1,14 +1,6 @@
 #!/bin/sh
 set -euo pipefail
 
-bin/rspec spec
-BUNDLE_GEMFILE=environments/Gemfile.non-rails.rb bin/rspec spec-non-rails
-
-changelog() {
-  local previous_tag="$(git describe --tags HEAD^ --abbrev=0)"
-  git log --first-parent --format='* %s%n%w(0,2,2)%+b' --reverse "${previous_tag}..HEAD^" "$@"
-}
-
 eval "$(gem build *.gemspec | awk '/(Name|Version|File): /{print tolower($1) $2}' | sed 's/:/=/')"
 
 git commit -m "${name} ${version}" -- lib/will_paginate/version.rb
@@ -18,7 +10,4 @@ git push origin HEAD "v${version}"
 gem push "$file"
 rm -rf "$file"
 
-{ echo "${name} ${version}"
-  echo
-  changelog -- lib
-} | hub release create -F- --edit "v${version}"
+gh release create "v${version}" --title "${name} ${version}" --generate-notes
diff --git a/spec-non-rails/data_mapper_spec.rb b/spec-non-rails/data_mapper_spec.rb
deleted file mode 100644
index 0496f48..0000000
--- a/spec-non-rails/data_mapper_spec.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-require_relative './spec_helper'
-require 'will_paginate/data_mapper'
-require_relative './data_mapper_test_connector'
-
-describe "WillPaginate::DataMapper" do
-
-  before(:all) do
-    DataMapper.setup :default, 'sqlite3::memory:'
-    [Animal, Ownership, Human].each do |klass|
-      klass.auto_migrate!
-    end
-
-    Animal.create(:name => 'Dog', :notes => 'a friend of all')
-    Animal.create(:name => 'Cat', :notes => 'a friend or foe')
-    Animal.create(:name => 'Lion', :notes => 'some roar')
-  end
-
-  it "has per_page" do
-    Animal.per_page.should == 30
-    begin
-      Animal.per_page = 10
-      Animal.per_page.should == 10
-
-      subclass = Class.new(Animal)
-      subclass.per_page.should == 10
-    ensure
-      Animal.per_page = 30
-    end
-  end
-
-  it "doesn't make normal collections appear paginated" do
-    Animal.all.should_not be_paginated
-  end
-
-  it "paginates to first page by default" do
-    animals = Animal.paginate(:page => nil)
-
-    animals.should be_paginated
-    animals.current_page.should == 1
-    animals.per_page.should == 30
-    animals.offset.should == 0
-    animals.total_entries.should == 3
-    animals.total_pages.should == 1
-  end
-
-  it "paginates to first page, explicit limit" do
-    animals = Animal.paginate(:page => 1, :per_page => 2)
-
-    animals.current_page.should == 1
-    animals.per_page.should == 2
-    animals.total_entries.should == 3
-    animals.total_pages.should == 2
-    animals.map {|a| a.name }.should == %w[ Dog Cat ]
-  end
-
-  it "paginates to second page" do
-    animals = Animal.paginate(:page => 2, :per_page => 2)
-
-    animals.current_page.should == 2
-    animals.offset.should == 2
-    animals.map {|a| a.name }.should == %w[ Lion ]
-  end
-
-  it "paginates a collection" do
-    friends = Animal.all(:notes.like => '%friend%')
-    friends.paginate(:page => 1).per_page.should == 30
-    friends.paginate(:page => 1, :per_page => 1).total_entries.should == 2
-  end
-
-  it "paginates a limited collection" do
-    animals = Animal.all(:limit => 2).paginate(:page => 1)
-    animals.per_page.should == 2
-  end
-
-  it "has page() method" do
-    Animal.page(2).per_page.should == 30
-    Animal.page(2).offset.should == 30
-    Animal.page(2).current_page.should == 2
-    Animal.all(:limit => 2).page(2).per_page.should == 2
-  end
-
-  it "has total_pages at 1 for empty collections" do
-    Animal.all(:conditions => ['1=2']).page(1).total_pages.should == 1
-  end
-
-  it "overrides total_entries count with a fixed value" do
-    animals = Animal.paginate :page => 1, :per_page => 3, :total_entries => 999
-    animals.total_entries.should == 999
-  end
-
-  it "supports a non-int for total_entries" do
-    topics = Animal.paginate :page => 1, :per_page => 3, :total_entries => "999"
-    topics.total_entries.should == 999
-  end
-
-
-  it "can iterate and then call WP methods" do
-    animals = Animal.all(:limit => 2).page(1)
-    animals.each { |a| }
-    animals.total_entries.should == 3
-  end
-
-  it "augments to_a to return a WP::Collection" do
-    animals = Animal.all(:limit => 2).page(1)
-    array = animals.to_a
-    array.size.should == 2
-    array.should be_kind_of(WillPaginate::Collection)
-    array.current_page.should == 1
-    array.per_page.should == 2
-  end
-
-  it "doesn't have a problem assigning has-one-through relationship" do
-    human = Human.create :name => "Mislav"
-    human.pet = Animal.first
-  end
-
-end
diff --git a/spec-non-rails/data_mapper_test_connector.rb b/spec-non-rails/data_mapper_test_connector.rb
deleted file mode 100644
index 1c9f724..0000000
--- a/spec-non-rails/data_mapper_test_connector.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'sqlite3'
-require 'dm-core'
-require 'dm-core/support/logger'
-require 'dm-migrations'
-
-class Animal
-  include DataMapper::Resource
-
-  property :id, Serial
-  property :name, String
-  property :notes, Text
-end
-
-class Ownership
-  include DataMapper::Resource
-
-  belongs_to :animal, :key => true
-  belongs_to :human, :key => true
-end
-
-class Human
-  include DataMapper::Resource
-
-  property :id, Serial
-  property :name, String
-
-  has n, :ownerships
-  has 1, :pet, :model => 'Animal', :through => :ownerships, :via => :animal
-end
-
-if 'irb' == $0
-  DataMapper.logger.set_log($stdout, :debug)
-  DataMapper.logger.auto_flush = true
-end
diff --git a/spec-non-rails/mongoid_spec.rb b/spec-non-rails/mongoid_spec.rb
index b5dc6e3..f8c0ce3 100644
--- a/spec-non-rails/mongoid_spec.rb
+++ b/spec-non-rails/mongoid_spec.rb
@@ -1,7 +1,7 @@
 require_relative './spec_helper'
 require 'will_paginate/mongoid'
 
-describe WillPaginate::Mongoid do
+RSpec.describe WillPaginate::Mongoid do
 
   class MongoidModel
     include Mongoid::Document
@@ -27,113 +27,113 @@ describe WillPaginate::Mongoid do
   describe "#page" do
     it "should forward to the paginate method" do
       criteria.expects(:paginate).with(:page => 2).returns("itself")
-      criteria.page(2).should == "itself"
+      expect(criteria.page(2)).to eq("itself")
     end
 
     it "should not override per_page if set earlier in the chain" do
-      criteria.paginate(:per_page => 10).page(1).per_page.should == 10
-      criteria.paginate(:per_page => 20).page(1).per_page.should == 20
+      expect(criteria.paginate(:per_page => 10).page(1).per_page).to eq(10)
+      expect(criteria.paginate(:per_page => 20).page(1).per_page).to eq(20)
     end
   end
 
   describe "#per_page" do
     it "should set the limit if given an argument" do
-      criteria.per_page(10).options[:limit].should == 10
+      expect(criteria.per_page(10).options[:limit]).to eq(10)
     end
 
     it "should return the current limit if no argument is given" do
-      criteria.per_page.should == nil
-      criteria.per_page(10).per_page.should == 10
+      expect(criteria.per_page).to eq(nil)
+      expect(criteria.per_page(10).per_page).to eq(10)
     end
 
     it "should be interchangable with limit" do
-      criteria.limit(15).per_page.should == 15
+      expect(criteria.limit(15).per_page).to eq(15)
     end
 
     it "should be nil'able" do
-      criteria.per_page(nil).per_page.should be_nil
+      expect(criteria.per_page(nil).per_page).to be_nil
     end
   end
 
   describe "#paginate" do
     it "should use criteria" do
-      criteria.paginate.should be_instance_of(::Mongoid::Criteria)
+      expect(criteria.paginate).to be_instance_of(::Mongoid::Criteria)
     end
 
     it "should not override page number if set earlier in the chain" do
-      criteria.page(3).paginate.current_page.should == 3
+      expect(criteria.page(3).paginate.current_page).to eq(3)
     end
 
     it "should limit according to per_page parameter" do
-      criteria.paginate(:per_page => 10).options.should include(:limit => 10)
+      expect(criteria.paginate(:per_page => 10).options).to include(:limit => 10)
     end
 
     it "should skip according to page and per_page parameters" do
-      criteria.paginate(:page => 2, :per_page => 5).options.should include(:skip => 5)
+      expect(criteria.paginate(:page => 2, :per_page => 5).options).to include(:skip => 5)
     end
 
     specify "first fallback value for per_page option is the current limit" do
-      criteria.limit(12).paginate.options.should include(:limit => 12)
+      expect(criteria.limit(12).paginate.options).to include(:limit => 12)
     end
 
     specify "second fallback value for per_page option is WillPaginate.per_page" do
-      criteria.paginate.options.should include(:limit => WillPaginate.per_page)
+      expect(criteria.paginate.options).to include(:limit => WillPaginate.per_page)
     end
 
     specify "page should default to 1" do
-      criteria.paginate.options.should include(:skip => 0)
+      expect(criteria.paginate.options).to include(:skip => 0)
     end
 
     it "should convert strings to integers" do
-      criteria.paginate(:page => "2", :per_page => "3").options.should include(:limit => 3)
+      expect(criteria.paginate(:page => "2", :per_page => "3").options).to include(:limit => 3)
     end
 
     describe "collection compatibility" do
       describe "#total_count" do
         it "should be calculated correctly" do
-          criteria.paginate(:per_page => 1).total_entries.should == 4
-          criteria.paginate(:per_page => 3).total_entries.should == 4
+          expect(criteria.paginate(:per_page => 1).total_entries).to eq(4)
+          expect(criteria.paginate(:per_page => 3).total_entries).to eq(4)
         end
 
         it "should be cached" do
           criteria.expects(:count).once.returns(123)
           criteria.paginate
-          2.times { criteria.total_entries.should == 123 }
+          2.times { expect(criteria.total_entries).to eq(123) }
         end
       end
 
       it "should calculate total_pages" do
-        criteria.paginate(:per_page => 1).total_pages.should == 4
-        criteria.paginate(:per_page => 3).total_pages.should == 2
-        criteria.paginate(:per_page => 10).total_pages.should == 1
+        expect(criteria.paginate(:per_page => 1).total_pages).to eq(4)
+        expect(criteria.paginate(:per_page => 3).total_pages).to eq(2)
+        expect(criteria.paginate(:per_page => 10).total_pages).to eq(1)
       end
 
       it "should return per_page" do
-        criteria.paginate(:per_page => 1).per_page.should == 1
-        criteria.paginate(:per_page => 5).per_page.should == 5
+        expect(criteria.paginate(:per_page => 1).per_page).to eq(1)
+        expect(criteria.paginate(:per_page => 5).per_page).to eq(5)
       end
 
       describe "#current_page" do
         it "should return current_page" do
-          criteria.paginate(:page => 1).current_page.should == 1
-          criteria.paginate(:page => 3).current_page.should == 3
+          expect(criteria.paginate(:page => 1).current_page).to eq(1)
+          expect(criteria.paginate(:page => 3).current_page).to eq(3)
         end
 
         it "should be casted to PageNumber" do
           page = criteria.paginate(:page => 1).current_page
-          (page.instance_of? WillPaginate::PageNumber).should be
+          expect(page.instance_of? WillPaginate::PageNumber).to be
         end
       end
 
       it "should return offset" do
-        criteria.paginate(:page => 1).offset.should == 0
-        criteria.paginate(:page => 2, :per_page => 5).offset.should == 5
-        criteria.paginate(:page => 3, :per_page => 10).offset.should == 20
+        expect(criteria.paginate(:page => 1).offset).to eq(0)
+        expect(criteria.paginate(:page => 2, :per_page => 5).offset).to eq(5)
+        expect(criteria.paginate(:page => 3, :per_page => 10).offset).to eq(20)
       end
 
       it "should not pollute plain mongoid criterias" do
         %w(total_entries total_pages current_page).each do |method|
-          criteria.should_not respond_to(method)
+          expect(criteria).not_to respond_to(method)
         end
       end
     end
diff --git a/spec-non-rails/sequel_spec.rb b/spec-non-rails/sequel_spec.rb
index 13ab05d..c2bbf21 100644
--- a/spec-non-rails/sequel_spec.rb
+++ b/spec-non-rails/sequel_spec.rb
@@ -8,18 +8,18 @@ Sequel.sqlite.create_table :cars do
   column :notes, :text
 end
 
-describe Sequel::Dataset::Pagination, 'extension' do
+RSpec.describe Sequel::Dataset::Pagination, 'extension' do
 
   class Car < Sequel::Model
     self.dataset = dataset.extension(:pagination)
   end
 
   it "should have the #paginate method" do
-    Car.dataset.should respond_to(:paginate)
+    expect(Car.dataset).to respond_to(:paginate)
   end
 
   it "should NOT have the #paginate_by_sql method" do
-    Car.dataset.should_not respond_to(:paginate_by_sql)
+    expect(Car.dataset).not_to respond_to(:paginate_by_sql)
   end
 
   describe 'pagination' do
@@ -32,34 +32,34 @@ describe Sequel::Dataset::Pagination, 'extension' do
     it "should imitate WillPaginate::Collection" do
       result = Car.dataset.paginate(1, 2)
       
-      result.should_not be_empty
-      result.size.should == 2
-      result.length.should == 2
-      result.total_entries.should == 3
-      result.total_pages.should == 2
-      result.per_page.should == 2
-      result.current_page.should == 1
+      expect(result).not_to be_empty
+      expect(result.size).to eq(2)
+      expect(result.length).to eq(2)
+      expect(result.total_entries).to eq(3)
+      expect(result.total_pages).to eq(2)
+      expect(result.per_page).to eq(2)
+      expect(result.current_page).to eq(1)
     end
     
     it "should perform" do
-      Car.dataset.paginate(1, 2).all.should == [Car[1], Car[2]]
+      expect(Car.dataset.paginate(1, 2).all).to eq([Car[1], Car[2]])
     end
 
     it "should be empty" do
       result = Car.dataset.paginate(3, 2)
-      result.should be_empty
+      expect(result).to be_empty
     end
     
     it "should perform with #select and #order" do
       result = Car.select(Sequel.lit("name as foo")).order(:name).paginate(1, 2).all
-      result.size.should == 2
-      result.first.values[:foo].should == "Aston Martin"
+      expect(result.size).to eq(2)
+      expect(result.first.values[:foo]).to eq("Aston Martin")
     end
 
     it "should perform with #filter" do
       results = Car.filter(:name => 'Shelby').paginate(1, 2).all
-      results.size.should == 1
-      results.first.should == Car.find(:name => 'Shelby')
+      expect(results.size).to eq(1)
+      expect(results.first).to eq(Car.find(:name => 'Shelby'))
     end
   end
 
diff --git a/spec-non-rails/spec_helper.rb b/spec-non-rails/spec_helper.rb
index 8f4564f..55b5450 100644
--- a/spec-non-rails/spec_helper.rb
+++ b/spec-non-rails/spec_helper.rb
@@ -2,4 +2,10 @@ require 'rspec'
 
 RSpec.configure do |config|
   config.mock_with :mocha
+  config.expose_dsl_globally = false
+  config.expect_with :rspec do |expectations|
+    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+  end
+  config.shared_context_metadata_behavior = :apply_to_host_groups
+  config.disable_monkey_patching!
 end
diff --git a/spec/collection_spec.rb b/spec/collection_spec.rb
index d561e83..0eb4a0b 100644
--- a/spec/collection_spec.rb
+++ b/spec/collection_spec.rb
@@ -1,126 +1,126 @@
 require 'will_paginate/array'
 require 'spec_helper'
 
-describe WillPaginate::Collection do
+RSpec.describe WillPaginate::Collection do
 
   before :all do
     @simple = ('a'..'e').to_a
   end
 
   it "should be a subset of original collection" do
-    @simple.paginate(:page => 1, :per_page => 3).should == %w( a b c )
+    expect(@simple.paginate(:page => 1, :per_page => 3)).to eq(%w( a b c ))
   end
 
   it "can be shorter than per_page if on last page" do
-    @simple.paginate(:page => 2, :per_page => 3).should == %w( d e )
+    expect(@simple.paginate(:page => 2, :per_page => 3)).to eq(%w( d e ))
   end
 
   it "should include whole collection if per_page permits" do
-    @simple.paginate(:page => 1, :per_page => 5).should == @simple
+    expect(@simple.paginate(:page => 1, :per_page => 5)).to eq(@simple)
   end
 
   it "should be empty if out of bounds" do
-    @simple.paginate(:page => 2, :per_page => 5).should be_empty
+    expect(@simple.paginate(:page => 2, :per_page => 5)).to be_empty
   end
   
   it "should default to 1 as current page and 30 per-page" do
     result = (1..50).to_a.paginate
-    result.current_page.should == 1
-    result.size.should == 30
+    expect(result.current_page).to eq(1)
+    expect(result.size).to eq(30)
   end
 
   it "should give total_entries precedence over actual size" do
-    %w(a b c).paginate(:total_entries => 5).total_entries.should == 5
+    expect(%w(a b c).paginate(:total_entries => 5).total_entries).to eq(5)
   end
 
   it "should be an augmented Array" do
     entries = %w(a b c)
     collection = create(2, 3, 10) do |pager|
-      pager.replace(entries).should == entries
+      expect(pager.replace(entries)).to eq(entries)
     end
 
-    collection.should == entries
+    expect(collection).to eq(entries)
     for method in %w(total_pages each offset size current_page per_page total_entries)
-      collection.should respond_to(method)
+      expect(collection).to respond_to(method)
     end
-    collection.should be_kind_of(Array)
-    collection.entries.should be_instance_of(Array)
+    expect(collection).to be_kind_of(Array)
+    expect(collection.entries).to be_instance_of(Array)
     # TODO: move to another expectation:
-    collection.offset.should == 3
-    collection.total_pages.should == 4
-    collection.should_not be_out_of_bounds
+    expect(collection.offset).to eq(3)
+    expect(collection.total_pages).to eq(4)
+    expect(collection).not_to be_out_of_bounds
   end
 
   describe "previous/next pages" do
     it "should have previous_page nil when on first page" do
       collection = create(1, 1, 3)
-      collection.previous_page.should be_nil
-      collection.next_page.should == 2
+      expect(collection.previous_page).to be_nil
+      expect(collection.next_page).to eq(2)
     end
     
     it "should have both prev/next pages" do
       collection = create(2, 1, 3)
-      collection.previous_page.should == 1
-      collection.next_page.should == 3
+      expect(collection.previous_page).to eq(1)
+      expect(collection.next_page).to eq(3)
     end
     
     it "should have next_page nil when on last page" do
       collection = create(3, 1, 3)
-      collection.previous_page.should == 2
-      collection.next_page.should be_nil
+      expect(collection.previous_page).to eq(2)
+      expect(collection.next_page).to be_nil
     end
   end
 
   describe "out of bounds" do
     it "is out of bounds when page number is too high" do
-      create(2, 3, 2).should be_out_of_bounds
+      expect(create(2, 3, 2)).to be_out_of_bounds
     end
 
     it "isn't out of bounds when inside collection" do
-      create(1, 3, 2).should_not be_out_of_bounds
+      expect(create(1, 3, 2)).not_to be_out_of_bounds
     end
 
     it "isn't out of bounds when the collection is empty" do
       collection = create(1, 3, 0)
-      collection.should_not be_out_of_bounds
-      collection.total_pages.should == 1
+      expect(collection).not_to be_out_of_bounds
+      expect(collection.total_pages).to eq(1)
     end
   end
 
   describe "guessing total count" do
     it "can guess when collection is shorter than limit" do
       collection = create { |p| p.replace array }
-      collection.total_entries.should == 8
+      expect(collection.total_entries).to eq(8)
     end
     
     it "should allow explicit total count to override guessed" do
       collection = create(2, 5, 10) { |p| p.replace array }
-      collection.total_entries.should == 10
+      expect(collection.total_entries).to eq(10)
     end
     
     it "should not be able to guess when collection is same as limit" do
       collection = create { |p| p.replace array(5) }
-      collection.total_entries.should be_nil
+      expect(collection.total_entries).to be_nil
     end
     
     it "should not be able to guess when collection is empty" do
       collection = create { |p| p.replace array(0) }
-      collection.total_entries.should be_nil
+      expect(collection.total_entries).to be_nil
     end
     
     it "should be able to guess when collection is empty and this is the first page" do
       collection = create(1) { |p| p.replace array(0) }
-      collection.total_entries.should == 0
+      expect(collection.total_entries).to eq(0)
     end
   end
 
   it "should not respond to page_count anymore" do
-    Proc.new { create.page_count }.should raise_error(NoMethodError)
+    expect { create.page_count }.to raise_error(NoMethodError)
   end
 
   it "inherits per_page from global value" do
     collection = described_class.new(1)
-    collection.per_page.should == 30
+    expect(collection.per_page).to eq(30)
   end
 
   private
diff --git a/spec/console b/spec/console
index 4c64785..075f5aa 100755
--- a/spec/console
+++ b/spec/console
@@ -1,9 +1,7 @@
 #!/usr/bin/env ruby
 irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
 opts = %w[ --simple-prompt -rirb/completion ]
-if ARGV.include? '-dm'
-  opts << '-rwill_paginate/data_mapper' << '-rfinders/data_mapper_test_connector'
-elsif ARGV.include? '-seq'
+if ARGV.include? '-seq'
   opts << '-rwill_paginate/sequel' << '-rfinders/sequel_test_connector'
 else
   opts << '-rconsole_fixtures'
diff --git a/spec/finders/active_record_spec.rb b/spec/finders/active_record_spec.rb
index bb75a64..24033f8 100644
--- a/spec/finders/active_record_spec.rb
+++ b/spec/finders/active_record_spec.rb
@@ -4,258 +4,258 @@ require File.expand_path('../activerecord_test_connector', __FILE__)
 
 ActiverecordTestConnector.setup
 
-describe WillPaginate::ActiveRecord do
+RSpec.describe WillPaginate::ActiveRecord do
   
   extend ActiverecordTestConnector::FixtureSetup
   
   fixtures :topics, :replies, :users, :projects, :developers_projects
   
   it "should integrate with ActiveRecord::Base" do
-    ActiveRecord::Base.should respond_to(:paginate)
+    expect(ActiveRecord::Base).to respond_to(:paginate)
   end
   
   it "should paginate" do
-    lambda {
+    expect {
       users = User.paginate(:page => 1, :per_page => 5).to_a
-      users.length.should == 5
-    }.should run_queries(2)
+      expect(users.length).to eq(5)
+    }.to execute(2).queries
   end
   
   it "should fail when encountering unknown params" do
-    lambda {
+    expect {
       User.paginate :foo => 'bar', :page => 1, :per_page => 4
-    }.should raise_error(ArgumentError)
+    }.to raise_error(ArgumentError)
   end
 
   describe "relation" do
     it "should return a relation" do
       rel = nil
-      lambda {
+      expect {
         rel = Developer.paginate(:page => 1)
-        rel.per_page.should == 10
-        rel.current_page.should == 1
-      }.should run_queries(0)
+        expect(rel.per_page).to eq(10)
+        expect(rel.current_page).to eq(1)
+      }.to execute(0).queries
 
-      lambda {
-        rel.total_pages.should == 2
-      }.should run_queries(1)
+      expect {
+        expect(rel.total_pages).to eq(2)
+      }.to execute(1).queries
     end
 
     it "should keep per-class per_page number" do
       rel = Developer.order('id').paginate(:page => 1)
-      rel.per_page.should == 10
+      expect(rel.per_page).to eq(10)
     end
 
     it "should be able to change per_page number" do
       rel = Developer.order('id').paginate(:page => 1).limit(5)
-      rel.per_page.should == 5
+      expect(rel.per_page).to eq(5)
     end
 
     it "remembers pagination in sub-relations" do
       rel = Topic.paginate(:page => 2, :per_page => 3)
-      lambda {
-        rel.total_entries.should == 4
-      }.should run_queries(1)
+      expect {
+        expect(rel.total_entries).to eq(4)
+      }.to execute(1).queries
       rel = rel.mentions_activerecord
-      rel.current_page.should == 2
-      rel.per_page.should == 3
-      lambda {
-        rel.total_entries.should == 1
-      }.should run_queries(1)
+      expect(rel.current_page).to eq(2)
+      expect(rel.per_page).to eq(3)
+      expect {
+        expect(rel.total_entries).to eq(1)
+      }.to execute(1).queries
     end
 
     it "supports the page() method" do
       rel = Developer.page('1').order('id')
-      rel.current_page.should == 1
-      rel.per_page.should == 10
-      rel.offset.should == 0
+      expect(rel.current_page).to eq(1)
+      expect(rel.per_page).to eq(10)
+      expect(rel.offset).to eq(0)
 
       rel = rel.limit(5).page(2)
-      rel.per_page.should == 5
-      rel.offset.should == 5
+      expect(rel.per_page).to eq(5)
+      expect(rel.offset).to eq(5)
     end
 
     it "raises on invalid page number" do
-      lambda {
+      expect {
         Developer.page('foo')
-      }.should raise_error(ArgumentError)
+      }.to raise_error(ArgumentError)
     end
 
     it "supports first limit() then page()" do
       rel = Developer.limit(3).page(3)
-      rel.offset.should == 6
+      expect(rel.offset).to eq(6)
     end
 
     it "supports first page() then limit()" do
       rel = Developer.page(3).limit(3)
-      rel.offset.should == 6
+      expect(rel.offset).to eq(6)
     end
 
     it "supports #first" do
       rel = Developer.order('id').page(2).per_page(4)
-      rel.first.should == users(:dev_5)
-      rel.first(2).should == users(:dev_5, :dev_6)
+      expect(rel.first).to eq(users(:dev_5))
+      expect(rel.first(2)).to eq(users(:dev_5, :dev_6))
     end
 
     it "supports #last" do
       rel = Developer.order('id').page(2).per_page(4)
-      rel.last.should == users(:dev_8)
-      rel.last(2).should == users(:dev_7, :dev_8)
-      rel.page(3).last.should == users(:poor_jamis)
+      expect(rel.last).to eq(users(:dev_8))
+      expect(rel.last(2)).to eq(users(:dev_7, :dev_8))
+      expect(rel.page(3).last).to eq(users(:poor_jamis))
     end
   end
 
   describe "counting" do
     it "should guess the total count" do
-      lambda {
+      expect {
         topics = Topic.paginate :page => 2, :per_page => 3
-        topics.total_entries.should == 4
-      }.should run_queries(1)
+        expect(topics.total_entries).to eq(4)
+      }.to execute(1).queries
     end
 
     it "should guess that there are no records" do
-      lambda {
+      expect {
         topics = Topic.where(:project_id => 999).paginate :page => 1, :per_page => 3
-        topics.total_entries.should == 0
-      }.should run_queries(1)
+        expect(topics.total_entries).to eq(0)
+      }.to execute(1).queries
     end
 
     it "forgets count in sub-relations" do
-      lambda {
+      expect {
         topics = Topic.paginate :page => 1, :per_page => 3
-        topics.total_entries.should == 4
-        topics.where('1 = 1').total_entries.should == 4
-      }.should run_queries(2)
+        expect(topics.total_entries).to eq(4)
+        expect(topics.where('1 = 1').total_entries).to eq(4)
+      }.to execute(2).queries
     end
 
     it "supports empty? method" do
       topics = Topic.paginate :page => 1, :per_page => 3
-      lambda {
-        topics.should_not be_empty
-      }.should run_queries(1)
+      expect {
+        expect(topics).not_to be_empty
+      }.to execute(1).queries
     end
     
     it "support empty? for grouped queries" do
       topics = Topic.group(:project_id).paginate :page => 1, :per_page => 3
-      lambda {
-        topics.should_not be_empty
-      }.should run_queries(1)
+      expect {
+        expect(topics).not_to be_empty
+      }.to execute(1).queries
     end
 
     it "supports `size` for grouped queries" do
       topics = Topic.group(:project_id).paginate :page => 1, :per_page => 3
-      lambda {
-        topics.size.should == {nil=>2, 1=>2}
-      }.should run_queries(1)
+      expect {
+        expect(topics.size).to eq({nil=>2, 1=>2})
+      }.to execute(1).queries
     end
 
     it "overrides total_entries count with a fixed value" do
-      lambda {
+      expect {
         topics = Topic.paginate :page => 1, :per_page => 3, :total_entries => 999
-        topics.total_entries.should == 999
+        expect(topics.total_entries).to eq(999)
         # value is kept even in sub-relations
-        topics.where('1 = 1').total_entries.should == 999
-      }.should run_queries(0)
+        expect(topics.where('1 = 1').total_entries).to eq(999)
+      }.to execute(0).queries
     end
 
     it "supports a non-int for total_entries" do
       topics = Topic.paginate :page => 1, :per_page => 3, :total_entries => "999"
-      topics.total_entries.should == 999
+      expect(topics.total_entries).to eq(999)
     end
 
     it "overrides empty? count call with a total_entries fixed value" do
-      lambda {
+      expect {
         topics = Topic.paginate :page => 1, :per_page => 3, :total_entries => 999
-        topics.should_not be_empty
-      }.should run_queries(0)
+        expect(topics).not_to be_empty
+      }.to execute(0).queries
     end
 
     it "removes :include for count" do
-      lambda {
+      expect {
         developers = Developer.paginate(:page => 1, :per_page => 1).includes(:projects)
-        developers.total_entries.should == 11
-        $query_sql.last.should_not =~ /\bJOIN\b/
-      }.should run_queries(1)
+        expect(developers.total_entries).to eq(11)
+        expect($query_sql.last).not_to match(/\bJOIN\b/)
+      }.to execute(1).queries
     end
 
     it "keeps :include for count when they are referenced in :conditions" do
       developers = Developer.paginate(:page => 1, :per_page => 1).includes(:projects)
       with_condition = developers.where('projects.id > 1')
       with_condition = with_condition.references(:projects) if with_condition.respond_to?(:references)
-      with_condition.total_entries.should == 1
+      expect(with_condition.total_entries).to eq(1)
 
-      $query_sql.last.should =~ /\bJOIN\b/
+      expect($query_sql.last).to match(/\bJOIN\b/)
     end
 
     it "should count with group" do
-      Developer.group(:salary).page(1).total_entries.should == 4
+      expect(Developer.group(:salary).page(1).total_entries).to eq(4)
     end
     
     it "should count with select" do
-      Topic.select('title, content').page(1).total_entries.should == 4
+      expect(Topic.select('title, content').page(1).total_entries).to eq(4)
     end
 
     it "removes :reorder for count with group" do
       Project.group(:id).reorder(:id).page(1).total_entries
-      $query_sql.last.should_not =~ /\ORDER\b/
+      expect($query_sql.last).not_to match(/\ORDER\b/)
     end
 
     it "should not have zero total_pages when the result set is empty" do
-      Developer.where("1 = 2").page(1).total_pages.should == 1
+      expect(Developer.where("1 = 2").page(1).total_pages).to eq(1)
     end
   end
   
   it "should not ignore :select parameter when it says DISTINCT" do
     users = User.select('DISTINCT salary').paginate :page => 2
-    users.total_entries.should == 5
+    expect(users.total_entries).to eq(5)
   end
 
   describe "paginate_by_sql" do
     it "should respond" do
-      User.should respond_to(:paginate_by_sql)
+      expect(User).to respond_to(:paginate_by_sql)
     end
 
     it "should paginate" do
-      lambda {
+      expect {
         sql = "select content from topics where content like '%futurama%'"
         topics = Topic.paginate_by_sql sql, :page => 1, :per_page => 1
-        topics.total_entries.should == 1
-        topics.first.attributes.has_key?('title').should be(false)
-      }.should run_queries(2)
+        expect(topics.total_entries).to eq(1)
+        expect(topics.first.attributes.has_key?('title')).to be(false)
+      }.to execute(2).queries
     end
 
     it "should respect total_entries setting" do
-      lambda {
+      expect {
         sql = "select content from topics"
         topics = Topic.paginate_by_sql sql, :page => 1, :per_page => 1, :total_entries => 999
-        topics.total_entries.should == 999
-      }.should run_queries(1)
+        expect(topics.total_entries).to eq(999)
+      }.to execute(1).queries
     end
 
     it "defaults to page 1" do
       sql = "select content from topics"
       topics = Topic.paginate_by_sql sql, :page => nil, :per_page => 1
-      topics.current_page.should == 1
-      topics.size.should == 1
+      expect(topics.current_page).to eq(1)
+      expect(topics.size).to eq(1)
     end
 
     it "should strip the order when counting" do
       expected = topics(:ar)
-      lambda {
+      expect {
         sql = "select id, title, content from topics order by topics.title"
         topics = Topic.paginate_by_sql sql, :page => 1, :per_page => 2
-        topics.first.should == expected
-      }.should run_queries(2)
+        expect(topics.first).to eq(expected)
+      }.to execute(2).queries
 
-      $query_sql.last.should include('COUNT')
-      $query_sql.last.should_not include('order by topics.title')
+      expect($query_sql.last).to include('COUNT')
+      expect($query_sql.last).not_to include('order by topics.title')
     end
 
     it "shouldn't change the original query string" do
       query = 'select * from topics where 1 = 2'
       original_query = query.dup
       Topic.paginate_by_sql(query, :page => 1)
-      query.should == original_query
+      expect(query).to eq(original_query)
     end
   end
 
@@ -265,25 +265,25 @@ describe WillPaginate::ActiveRecord do
     options_before = options.dup
     
     Topic.paginate(options)
-    options.should == options_before
+    expect(options).to eq(options_before)
   end
   
   it "should get first page of Topics with a single query" do
-    lambda {
+    expect {
       result = Topic.paginate :page => nil
       result.to_a # trigger loading of records
-      result.current_page.should == 1
-      result.total_pages.should == 1
-      result.size.should == 4
-    }.should run_queries(1)
+      expect(result.current_page).to eq(1)
+      expect(result.total_pages).to eq(1)
+      expect(result.size).to eq(4)
+    }.to execute(1).queries
   end
   
   it "should get second (inexistent) page of Topics, requiring 1 query" do
-    lambda {
+    expect {
       result = Topic.paginate :page => 2
-      result.total_pages.should == 1
-      result.should be_empty
-    }.should run_queries(1)
+      expect(result.total_pages).to eq(1)
+      expect(result).to be_empty
+    }.to execute(1).queries
   end
   
   describe "associations" do
@@ -292,43 +292,43 @@ describe WillPaginate::ActiveRecord do
       expected_name_ordered = projects(:action_controller, :active_record)
       expected_id_ordered   = projects(:active_record, :action_controller)
 
-      lambda {
+      expect {
         # with association-specified order
         result = ignore_deprecation {
           dhh.projects.includes(:topics).order('projects.name').paginate(:page => 1)
         }
-        result.to_a.should == expected_name_ordered
-        result.total_entries.should == 2
-      }.should run_queries(2)
+        expect(result.to_a).to eq(expected_name_ordered)
+        expect(result.total_entries).to eq(2)
+      }.to execute(2).queries
 
       # with explicit order
       result = dhh.projects.paginate(:page => 1).reorder('projects.id')
-      result.should == expected_id_ordered
-      result.total_entries.should == 2
+      expect(result).to eq(expected_id_ordered)
+      expect(result.total_entries).to eq(2)
 
-      lambda {
+      expect {
         dhh.projects.order('projects.id').limit(4).to_a
-      }.should_not raise_error
+      }.not_to raise_error
       
       result = dhh.projects.paginate(:page => 1, :per_page => 4).reorder('projects.id')
-      result.should == expected_id_ordered
+      expect(result).to eq(expected_id_ordered)
 
       # has_many with implicit order
       topic = Topic.find(1)
       expected = replies(:spam, :witty_retort)
       # FIXME: wow, this is ugly
-      topic.replies.paginate(:page => 1).map(&:id).sort.should == expected.map(&:id).sort
-      topic.replies.paginate(:page => 1).reorder('replies.id ASC').should == expected.reverse
+      expect(topic.replies.paginate(:page => 1).map(&:id).sort).to eq(expected.map(&:id).sort)
+      expect(topic.replies.paginate(:page => 1).reorder('replies.id ASC')).to eq(expected.reverse)
     end
 
     it "should paginate through association extension" do
       project = Project.order('id').first
       expected = [replies(:brave)]
 
-      lambda {
+      expect {
         result = project.replies.only_recent.paginate(:page => 1)
-        result.should == expected
-      }.should run_queries(1)
+        expect(result).to eq(expected)
+      }.to execute(1).queries
     end
   end
   
@@ -336,89 +336,89 @@ describe WillPaginate::ActiveRecord do
     result = nil
     join_sql = 'LEFT JOIN developers_projects ON users.id = developers_projects.developer_id'
 
-    lambda {
+    expect {
       result = Developer.where('developers_projects.project_id = 1').joins(join_sql).paginate(:page => 1)
       result.to_a # trigger loading of records
-      result.size.should == 2
+      expect(result.size).to eq(2)
       developer_names = result.map(&:name)
-      developer_names.should include('David')
-      developer_names.should include('Jamis')
-    }.should run_queries(1)
+      expect(developer_names).to include('David')
+      expect(developer_names).to include('Jamis')
+    }.to execute(1).queries
 
-    lambda {
+    expect {
       expected = result.to_a
       result = Developer.where('developers_projects.project_id = 1').joins(join_sql).paginate(:page => 1)
-      result.should == expected
-      result.total_entries.should == 2
-    }.should run_queries(1)
+      expect(result).to eq(expected)
+      expect(result.total_entries).to eq(2)
+    }.to execute(1).queries
   end
 
   it "should paginate with group" do
     result = nil
-    lambda {
+    expect {
       result = Developer.select('salary').order('salary').group('salary').
         paginate(:page => 1, :per_page => 10).to_a
-    }.should run_queries(1)
+    }.to execute(1).queries
 
     expected = users(:david, :jamis, :dev_10, :poor_jamis).map(&:salary).sort
-    result.map(&:salary).should == expected
+    expect(result.map(&:salary)).to eq(expected)
   end
 
   it "should not paginate with dynamic finder" do
-    lambda {
+    expect {
       Developer.paginate_by_salary(100000, :page => 1, :per_page => 5)
-    }.should raise_error(NoMethodError)
+    }.to raise_error(NoMethodError)
   end
 
   describe "scopes" do
     it "should paginate" do
       result = Developer.poor.paginate :page => 1, :per_page => 1
-      result.size.should == 1
-      result.total_entries.should == 2
+      expect(result.size).to eq(1)
+      expect(result.total_entries).to eq(2)
     end
 
     it "should paginate on habtm association" do
       project = projects(:active_record)
-      lambda {
+      expect {
         result = ignore_deprecation { project.developers.poor.paginate :page => 1, :per_page => 1 }
-        result.size.should == 1
-        result.total_entries.should == 1
-      }.should run_queries(2)
+        expect(result.size).to eq(1)
+        expect(result.total_entries).to eq(1)
+      }.to execute(2).queries
     end
 
     it "should paginate on hmt association" do
       project = projects(:active_record)
       expected = [replies(:brave)]
 
-      lambda {
+      expect {
         result = project.replies.recent.paginate :page => 1, :per_page => 1
-        result.should == expected
-        result.total_entries.should == 1
-      }.should run_queries(2)
+        expect(result).to eq(expected)
+        expect(result.total_entries).to eq(1)
+      }.to execute(2).queries
     end
 
     it "should paginate on has_many association" do
       project = projects(:active_record)
       expected = [topics(:ar)]
 
-      lambda {
+      expect {
         result = project.topics.mentions_activerecord.paginate :page => 1, :per_page => 1
-        result.should == expected
-        result.total_entries.should == 1
-      }.should run_queries(2)
+        expect(result).to eq(expected)
+        expect(result.total_entries).to eq(1)
+      }.to execute(2).queries
     end
   end
 
   it "should not paginate an array of IDs" do
-    lambda {
+    expect {
       Developer.paginate((1..8).to_a, :per_page => 3, :page => 2, :order => 'id')
-    }.should raise_error(ArgumentError)
+    }.to raise_error(ArgumentError)
   end
 
   it "errors out for invalid values" do |variable|
-    lambda {
+    expect {
       # page that results in an offset larger than BIGINT
       Project.page(307445734561825862)
-    }.should raise_error(WillPaginate::InvalidPage, "invalid offset: 9223372036854775830")
+    }.to raise_error(WillPaginate::InvalidPage, "invalid offset: 9223372036854775830")
   end
  end
diff --git a/spec/finders/activerecord_test_connector.rb b/spec/finders/activerecord_test_connector.rb
index d8c31fb..69afec3 100644
--- a/spec/finders/activerecord_test_connector.rb
+++ b/spec/finders/activerecord_test_connector.rb
@@ -2,6 +2,24 @@ require 'active_record'
 require 'active_record/fixtures'
 require 'stringio'
 require 'erb'
+require 'time'
+require 'date'
+require 'yaml'
+
+# forward compatibility with Rails 7 (needed for time expressions within fixtures)
+class Time
+  alias_method :to_fs, :to_s
+end unless Time.new.respond_to?(:to_fs)
+
+# monkeypatch needed for Ruby 3.1 & Rails 6.0
+YAML.module_eval do
+  class << self
+    alias_method :_load_orig, :load
+    def load(yaml_str)
+      _load_orig(yaml_str, permitted_classes: [Symbol, Date, Time])
+    end
+  end
+end if YAML.method(:load).parameters.include?([:key, :permitted_classes])
 
 $query_count = 0
 $query_sql = []
@@ -43,10 +61,28 @@ module ActiverecordTestConnector
   end
 
   private
+
+  module Autoloader
+    def const_missing(name)
+      super
+    rescue NameError
+      file = File.join(FIXTURES_PATH, name.to_s.underscore)
+      if File.exist?("#{file}.rb")
+        require file
+        const_get(name)
+      else
+        raise $!
+      end
+    end
+  end
   
   def add_load_path(path)
-    dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
-    dep.autoload_paths.unshift path
+    if ActiveSupport::Dependencies.respond_to?(:autoloader=)
+      Object.singleton_class.include Autoloader
+    else
+      dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
+      dep.autoload_paths.unshift path
+    end
   end
 
   def setup_connection
@@ -62,7 +98,11 @@ module ActiverecordTestConnector
     
     ActiveRecord::Base.configurations = { db => configuration }
     ActiveRecord::Base.establish_connection(db.to_sym)
-    ActiveRecord::Base.default_timezone = :utc
+    if ActiveRecord.respond_to?(:default_timezone=)
+      ActiveRecord.default_timezone = :utc
+    else
+      ActiveRecord::Base.default_timezone = :utc
+    end
   end
 
   def load_schema
diff --git a/spec/fixtures/replies.yml b/spec/fixtures/replies.yml
index 25fff58..cf5a5b3 100644
--- a/spec/fixtures/replies.yml
+++ b/spec/fixtures/replies.yml
@@ -2,28 +2,28 @@ witty_retort:
   id: 1
   topic_id: 1
   content: Birdman is better!
-  created_at: <%= 6.hours.ago.utc.to_s(:db) %>
+  created_at: <%= 6.hours.ago.utc.to_fs(:db) %>
   
 another:
   id: 2
   topic_id: 2
   content: Nuh uh!
-  created_at: <%= 1.hour.ago.utc.to_s(:db) %>
+  created_at: <%= 1.hour.ago.utc.to_fs(:db) %>
   
 spam:
   id: 3
   topic_id: 1
   content: Nice site!
-  created_at: <%= 1.hour.ago.utc.to_s(:db) %>
+  created_at: <%= 1.hour.ago.utc.to_fs(:db) %>
 
 decisive:
   id: 4
   topic_id: 4
   content: "I'm getting to the bottom of this"
-  created_at: <%= 30.minutes.ago.utc.to_s(:db) %>
+  created_at: <%= 30.minutes.ago.utc.to_fs(:db) %>
 
 brave:
   id: 5
   topic_id: 4
   content: "AR doesn't scare me a bit"
-  created_at: <%= 10.minutes.ago.utc.to_s(:db) %>
+  created_at: <%= 10.minutes.ago.utc.to_fs(:db) %>
diff --git a/spec/fixtures/topics.yml b/spec/fixtures/topics.yml
index f93cf48..ee45b21 100644
--- a/spec/fixtures/topics.yml
+++ b/spec/fixtures/topics.yml
@@ -3,7 +3,7 @@ futurama:
   title: Isnt futurama awesome?
   subtitle: It really is, isnt it.
   content: I like futurama
-  created_at: <%= 1.day.ago.utc.to_s(:db) %>
+  created_at: <%= 1.day.ago.utc.to_fs(:db) %>
   updated_at:
   
 harvey_birdman:
@@ -11,7 +11,7 @@ harvey_birdman:
   title: Harvey Birdman is the king of all men
   subtitle: yup
   content: He really is
-  created_at: <%= 2.hours.ago.utc.to_s(:db) %>
+  created_at: <%= 2.hours.ago.utc.to_fs(:db) %>
   updated_at:
 
 rails:
@@ -20,11 +20,11 @@ rails:
   title: Rails is nice
   subtitle: It makes me happy
   content: except when I have to hack internals to fix pagination. even then really.
-  created_at: <%= 20.minutes.ago.utc.to_s(:db) %>
+  created_at: <%= 20.minutes.ago.utc.to_fs(:db) %>
 
 ar:
   id: 4
   project_id: 1
   title: ActiveRecord sometimes freaks me out
   content: "I mean, what's the deal with eager loading?"
-  created_at: <%= 15.minutes.ago.utc.to_s(:db) %>
+  created_at: <%= 15.minutes.ago.utc.to_fs(:db) %>
diff --git a/spec/matchers/deprecation_matcher.rb b/spec/matchers/deprecation_matcher.rb
deleted file mode 100644
index 27a8005..0000000
--- a/spec/matchers/deprecation_matcher.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require 'stringio'
-
-class DeprecationMatcher
-  def initialize(message)
-    @message = message
-  end
-
-  def matches?(block)
-    @actual = hijack_stderr(&block)
-    PhraseMatcher.new("DEPRECATION WARNING: #{@message}").matches?(@actual)
-  end
-
-  def failure_message
-    "expected deprecation warning #{@message.inspect}, got #{@actual.inspect}"
-  end
-
-  private
-
-  def hijack_stderr
-    err = $stderr
-    $stderr = StringIO.new
-    yield
-    $stderr.string.rstrip
-  ensure
-    $stderr = err
-  end
-end
diff --git a/spec/matchers/phrase_matcher.rb b/spec/matchers/phrase_matcher.rb
deleted file mode 100644
index 2e901ff..0000000
--- a/spec/matchers/phrase_matcher.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class PhraseMatcher
-  def initialize(string)
-    @string = string
-    @pattern = /\b#{Regexp.escape string}\b/
-  end
-
-  def matches?(actual)
-    @actual = actual.to_s
-    @actual =~ @pattern
-  end
-
-  def failure_message
-    "expected #{@actual.inspect} to contain phrase #{@string.inspect}"
-  end
-
-  def negative_failure_message
-    "expected #{@actual.inspect} not to contain phrase #{@string.inspect}"
-  end
-end
diff --git a/spec/matchers/query_count_matcher.rb b/spec/matchers/query_count_matcher.rb
index 336d2d3..e1ce103 100644
--- a/spec/matchers/query_count_matcher.rb
+++ b/spec/matchers/query_count_matcher.rb
@@ -1,15 +1,11 @@
-class QueryCountMatcher
-  def initialize(num)
-    @expected_count = num
-  end
-
-  def matches?(block)
+RSpec::Matchers.define :execute do |expected_count|
+  match do |block|
     run(block)
 
-    if @expected_count.respond_to? :include?
-      @expected_count.include? @count
+    if expected_count.respond_to? :include?
+      expected_count.include? @count
     else
-      @count == @expected_count
+      @count == expected_count
     end
   end
 
@@ -22,15 +18,14 @@ class QueryCountMatcher
     @count = $query_count
   end
 
-  def performed_queries
-    @queries
-  end
+  chain(:queries) {}
+  supports_block_expectations
 
-  def failure_message
-    "expected #{@expected_count} queries, got #{@count}\n#{@queries.join("\n")}"
+  failure_message do
+    "expected #{expected_count} queries, got #{@count}\n#{@queries.join("\n")}"
   end
 
-  def negative_failure_message
-    "expected query count not to be #{@expected_count}"
+  failure_message_when_negated do
+    "expected query count not to be #{expected_count}"
   end
 end
diff --git a/spec/page_number_spec.rb b/spec/page_number_spec.rb
index 57c144b..6d46192 100644
--- a/spec/page_number_spec.rb
+++ b/spec/page_number_spec.rb
@@ -2,50 +2,50 @@ require 'spec_helper'
 require 'will_paginate/page_number'
 require 'json'
 
-describe WillPaginate::PageNumber do
+RSpec.describe WillPaginate::PageNumber do
   describe "valid" do
     def num
       WillPaginate::PageNumber.new('12', 'page')
     end
 
     it "== 12" do
-      num.should eq(12)
+      expect(num).to eq(12)
     end
 
     it "inspects to 'page 12'" do
-      num.inspect.should eq('page 12')
+      expect(num.inspect).to eq('page 12')
     end
 
     it "is a PageNumber" do
-      (num.instance_of? WillPaginate::PageNumber).should be
+      expect(num.instance_of? WillPaginate::PageNumber).to be
     end
 
     it "is a kind of Numeric" do
-      (num.is_a? Numeric).should be
+      expect(num.is_a? Numeric).to be
     end
 
     it "is a kind of Integer" do
-      (num.is_a? Integer).should be
+      expect(num.is_a? Integer).to be
     end
 
     it "isn't directly a Integer" do
-      (num.instance_of? Integer).should_not be
+      expect(num.instance_of? Integer).not_to be
     end
 
     it "passes the PageNumber=== type check" do |variable|
-      (WillPaginate::PageNumber === num).should be
+      expect(WillPaginate::PageNumber === num).to be
     end
 
     it "passes the Numeric=== type check" do |variable|
-      (Numeric === num).should be
+      expect(Numeric === num).to be
     end
 
     it "fails the Numeric=== type check" do |variable|
-      (Integer === num).should_not be
+      expect(Integer === num).not_to be
     end
 
     it "serializes as JSON number" do
-      JSON.dump(page: num).should eq('{"page":12}')
+      expect(JSON.dump(page: num)).to eq('{"page":12}')
     end
   end
 
@@ -55,37 +55,37 @@ describe WillPaginate::PageNumber do
     end
 
     it "errors out on non-int values" do
-      lambda { create(nil) }.should raise_error(WillPaginate::InvalidPage)
-      lambda { create('') }.should raise_error(WillPaginate::InvalidPage)
-      lambda { create('Schnitzel') }.should raise_error(WillPaginate::InvalidPage)
+      expect { create(nil) }.to raise_error(WillPaginate::InvalidPage)
+      expect { create('') }.to raise_error(WillPaginate::InvalidPage)
+      expect { create('Schnitzel') }.to raise_error(WillPaginate::InvalidPage)
     end
 
     it "errors out on zero or less" do
-      lambda { create(0) }.should raise_error(WillPaginate::InvalidPage)
-      lambda { create(-1) }.should raise_error(WillPaginate::InvalidPage)
+      expect { create(0) }.to raise_error(WillPaginate::InvalidPage)
+      expect { create(-1) }.to raise_error(WillPaginate::InvalidPage)
     end
 
     it "doesn't error out on zero for 'offset'" do
-      lambda { create(0, 'offset') }.should_not raise_error
-      lambda { create(-1, 'offset') }.should raise_error(WillPaginate::InvalidPage)
+      expect { create(0, 'offset') }.not_to raise_error
+      expect { create(-1, 'offset') }.to raise_error(WillPaginate::InvalidPage)
     end
   end
 
   describe "coercion method" do
     it "defaults to 'page' name" do
       num = WillPaginate::PageNumber(12)
-      num.inspect.should eq('page 12')
+      expect(num.inspect).to eq('page 12')
     end
 
     it "accepts a custom name" do
       num = WillPaginate::PageNumber(12, 'monkeys')
-      num.inspect.should eq('monkeys 12')
+      expect(num.inspect).to eq('monkeys 12')
     end
 
     it "doesn't affect PageNumber instances" do
       num = WillPaginate::PageNumber(12)
       num2 = WillPaginate::PageNumber(num)
-      num2.object_id.should eq(num.object_id)
+      expect(num2.object_id).to eq(num.object_id)
     end
   end
 end
diff --git a/spec/per_page_spec.rb b/spec/per_page_spec.rb
index ece303f..1f66bc8 100644
--- a/spec/per_page_spec.rb
+++ b/spec/per_page_spec.rb
@@ -1,18 +1,18 @@
 require 'spec_helper'
 require 'will_paginate/per_page'
 
-describe WillPaginate::PerPage do
+RSpec.describe WillPaginate::PerPage do
 
   class MyModel
     extend WillPaginate::PerPage
   end
 
   it "has the default value" do
-    MyModel.per_page.should == 30
+    expect(MyModel.per_page).to eq(30)
 
     WillPaginate.per_page = 10
     begin
-      MyModel.per_page.should == 10
+      expect(MyModel.per_page).to eq(10)
     ensure
       WillPaginate.per_page = 30
     end
@@ -21,7 +21,7 @@ describe WillPaginate::PerPage do
   it "casts values to int" do
     WillPaginate.per_page = '10'
     begin
-      MyModel.per_page.should == 10
+      expect(MyModel.per_page).to eq(10)
     ensure
       WillPaginate.per_page = 30
     end
@@ -30,9 +30,9 @@ describe WillPaginate::PerPage do
   it "has an explicit value" do
     MyModel.per_page = 12
     begin
-      MyModel.per_page.should == 12
+      expect(MyModel.per_page).to eq(12)
       subclass = Class.new(MyModel)
-      subclass.per_page.should == 12
+      expect(subclass.per_page).to eq(12)
     ensure
       MyModel.send(:remove_instance_variable, '@per_page')
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 49c3885..96fb210 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -3,39 +3,31 @@ require 'view_helpers/view_example_group'
 
 Dir[File.expand_path('../matchers/*_matcher.rb', __FILE__)].each { |matcher| require matcher }
 
+RSpec::Matchers.alias_matcher :include_phrase, :include
+
 RSpec.configure do |config|
   config.include Module.new {
     protected
 
-    def include_phrase(string)
-      PhraseMatcher.new(string)
-    end
-
     def have_deprecation(msg)
-      DeprecationMatcher.new(msg)
-    end
-
-    def run_queries(num)
-      QueryCountMatcher.new(num)
+      output(/^DEPRECATION WARNING: #{Regexp.escape(msg)}/).to_stderr
     end
 
     def ignore_deprecation
-      ActiveSupport::Deprecation.silence { yield }
-    end
-
-    def show_queries(&block)
-      counter = QueryCountMatcher.new(nil)
-      counter.run block
-    ensure
-      queries = counter.performed_queries
-      if queries.any?
-        puts queries
+      if ActiveSupport::Deprecation.respond_to?(:silence)
+        ActiveSupport::Deprecation.silence { yield }
       else
-        puts "no queries"
+        yield
       end
     end
   }
 
   config.mock_with :mocha
   config.backtrace_exclusion_patterns << /view_example_group/
+  config.expose_dsl_globally = false
+  config.expect_with :rspec do |expectations|
+    expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+  end
+  config.shared_context_metadata_behavior = :apply_to_host_groups
+  config.disable_monkey_patching!
 end
diff --git a/spec/view_helpers/action_view_spec.rb b/spec/view_helpers/action_view_spec.rb
index 7a1304f..fd84451 100644
--- a/spec/view_helpers/action_view_spec.rb
+++ b/spec/view_helpers/action_view_spec.rb
@@ -17,7 +17,7 @@ Routes.draw do
   get 'baz/list' => 'baz#list'
 end
 
-describe WillPaginate::ActionView do
+RSpec.describe WillPaginate::ActionView do
 
   before(:all) do
     I18n.load_path.concat WillPaginate::I18n.load_path
@@ -57,14 +57,14 @@ describe WillPaginate::ActionView do
     paginate do |pagination|
       assert_select 'a[href]', 3 do |elements|
         validate_page_numbers [2,3,2], elements
-        text(elements[2]).should == 'Next →'
+        expect(text(elements[2])).to eq('Next →')
       end
       assert_select 'span', 1 do |spans|
-        spans[0]['class'].should == 'previous_page disabled'
-        text(spans[0]).should == '← Previous'
+        expect(spans[0]['class']).to eq('previous_page disabled')
+        expect(text(spans[0])).to eq('← Previous')
       end
       assert_select 'em.current', '1'
-      text(pagination[0]).should == '← Previous 1 2 3 Next →'
+      expect(text(pagination[0])).to eq('← Previous 1 2 3 Next →')
     end
   end
 
@@ -78,7 +78,7 @@ describe WillPaginate::ActionView do
   end
 
   it "should render nothing when there is only 1 page" do
-    paginate(:per_page => 30).should be_empty
+    expect(paginate(:per_page => 30)).to be_empty
   end
 
   it "should paginate with options" do
@@ -86,12 +86,12 @@ describe WillPaginate::ActionView do
       assert_select 'a[href]', 4 do |elements|
         validate_page_numbers [1,1,3,3], elements
         # test rel attribute values:
-        text(elements[0]).should == 'Prev'
-        elements[0]['rel'].should == 'prev'
-        text(elements[1]).should == '1'
-        elements[1]['rel'].should == 'prev'
-        text(elements[3]).should == 'Next'
-        elements[3]['rel'].should == 'next'
+        expect(text(elements[0])).to eq('Prev')
+        expect(elements[0]['rel']).to eq('prev')
+        expect(text(elements[1])).to eq('1')
+        expect(elements[1]['rel']).to eq('prev')
+        expect(text(elements[3])).to eq('Next')
+        expect(elements[3]['rel']).to eq('next')
       end
       assert_select '.current', '2'
     end
@@ -127,19 +127,19 @@ describe WillPaginate::ActionView do
   it "should match expected markup" do
     paginate
     expected = <<-HTML
-      <div class="pagination" role="navigation" aria-label="Pagination"><span class="previous_page disabled" aria-disabled="true">&#8592; Previous</span>
+      <div class="pagination" role="navigation" aria-label="Pagination"><span class="previous_page disabled" aria-label="Previous page">&#8592; Previous</span>
       <em class="current" aria-label="Page 1" aria-current="page">1</em>
       <a href="/foo/bar?page=2" aria-label="Page 2" rel="next">2</a>
       <a href="/foo/bar?page=3" aria-label="Page 3">3</a>
-      <a href="/foo/bar?page=2" class="next_page" rel="next">Next &#8594;</a></div>
+      <a href="/foo/bar?page=2" class="next_page" rel="next" aria-label="Next page">Next &#8594;</a></div>
     HTML
     expected.strip!.gsub!(/\s{2,}/, ' ')
     expected_dom = parse_html_document(expected)
 
     if expected_dom.respond_to?(:canonicalize)
-      html_document.canonicalize.should == expected_dom.canonicalize
+      expect(html_document.canonicalize).to eq(expected_dom.canonicalize)
     else
-      html_document.root.should == expected_dom.root
+      expect(html_document.root).to eq(expected_dom.root)
     end
   end
   
@@ -150,7 +150,7 @@ describe WillPaginate::ActionView do
     assert_select 'a[href]', 1 do |links|
       query = links.first['href'].split('?', 2)[1]
       parts = query.gsub('&amp;', '&').split('&').sort
-      parts.should == %w(page=2 tag=%3Cbr%3E)
+      expect(parts).to eq(%w(page=2 tag=%3Cbr%3E))
     end
   end
   
@@ -220,11 +220,13 @@ describe WillPaginate::ActionView do
     assert_no_links_match /p0wned/
   end
   
-  it "should not preserve parameters on POST" do
+  it "should only preserve query parameters on POST" do
     request.post
     request.params :foo => 'bar'
+    request.query_parameters = { :hello => 'world' }
     paginate
     assert_no_links_match /foo=bar/
+    assert_links_match /hello=world/
   end
   
   it "should add additional parameters to links" do
@@ -313,9 +315,9 @@ describe WillPaginate::ActionView do
     @template = '<%= will_paginate options %>'
     controller.controller_name = 'developers'
     
-    lambda {
+    expect {
       paginate(nil)
-    }.should raise_error(ActionView::TemplateError, /@developers/)
+    }.to raise_error(ActionView::TemplateError, /@developers/)
   end
 
   ## i18n
@@ -346,7 +348,7 @@ describe WillPaginate::ActionView do
 
     assert_select 'a[href]', 4 do |links|
       urls = links.map {|l| l['href'] }.uniq
-      urls.should == ['/dummy/page/1', '/dummy/page/3']
+      expect(urls).to eq(['/dummy/page/1', '/dummy/page/3'])
     end
   end
 
@@ -365,18 +367,18 @@ describe WillPaginate::ActionView do
 
     assert_select 'a[href]', 4 do |links|
       urls = links.map {|l| l['href'] }.uniq
-      urls.should == ['/dummy/page/1', '/dummy/page/3']
+      expect(urls).to eq(['/dummy/page/1', '/dummy/page/3'])
     end
   end
 
   # TODO: re-enable once Rails 6.1.4 ships
-  xit "page_entries_info" do
+  it "page_entries_info" do
     @template = "<%= page_entries_info collection, options %>"
     output = render(
       collection: WillPaginate::Collection.new(1, 1, 3),
       options: {html: false},
     )
-    output.should == "Displaying entries 1 - 0 of 3 in total"
+    expect(output).to eq("Displaying entries 1 - 0 of 3 in total")
   end
 
   private
@@ -465,6 +467,18 @@ class DummyRequest
     @params.update(more) if more
     ActionController::Parameters.new(@params)
   end
+
+  def query_parameters
+    if get?
+      params
+    else
+      ActionController::Parameters.new(@query_parameters)
+    end
+  end
+
+  def query_parameters=(more)
+    @query_parameters = more.with_indifferent_access
+  end
   
   def host_with_port
     'example.com'
diff --git a/spec/view_helpers/base_spec.rb b/spec/view_helpers/base_spec.rb
index 1d6e86b..ca31fc0 100644
--- a/spec/view_helpers/base_spec.rb
+++ b/spec/view_helpers/base_spec.rb
@@ -5,7 +5,7 @@ require 'active_support'
 require 'active_support/core_ext/string/inflections'
 require 'active_support/inflections'
 
-describe WillPaginate::ViewHelpers do
+RSpec.describe WillPaginate::ViewHelpers do
 
   before(:all) do
     # make sure default translations aren't loaded
@@ -26,12 +26,12 @@ describe WillPaginate::ViewHelpers do
       renderer.expects(:prepare).with(collection, instance_of(Hash), self)
       renderer.expects(:to_html).returns('<PAGES>')
       
-      will_paginate(collection, :renderer => renderer).should == '<PAGES>'
+      expect(will_paginate(collection, :renderer => renderer)).to eq('<PAGES>')
     end
     
     it "should return nil for single-page collections" do
       collection = mock 'Collection', :total_pages => 1
-      will_paginate(collection).should be_nil
+      expect(will_paginate(collection)).to be_nil
     end
 
     it "should call html_safe on result" do
@@ -43,7 +43,7 @@ describe WillPaginate::ViewHelpers do
       renderer.stubs(:prepare)
       renderer.expects(:to_html).returns(html)
 
-      will_paginate(collection, :renderer => renderer).should eql(html)
+      expect(will_paginate(collection, :renderer => renderer)).to eql(html)
     end
   end
 
@@ -52,9 +52,9 @@ describe WillPaginate::ViewHelpers do
 
     it "deprecates setting :renderer" do
       begin
-        lambda {
+        expect {
           pagination_options[:renderer] = 'test'
-        }.should have_deprecation("pagination_options[:renderer] shouldn't be set")
+        }.to have_deprecation("pagination_options[:renderer] shouldn't be set")
       ensure
         pagination_options.delete :renderer
       end
@@ -72,14 +72,14 @@ describe WillPaginate::ViewHelpers do
     end
 
     it "should display middle results and total count" do
-      info(:page => 2, :per_page => 5).should == "Displaying strings 6 - 10 of 26 in total"
+      expect(info(:page => 2, :per_page => 5)).to eq("Displaying strings 6 - 10 of 26 in total")
     end
 
     it "uses translation if available" do
       translation :will_paginate => {
         :page_entries_info => {:multi_page => 'Showing %{from} - %{to}'}
       }
-      info(:page => 2, :per_page => 5).should == "Showing 6 - 10"
+      expect(info(:page => 2, :per_page => 5)).to eq("Showing 6 - 10")
     end
 
     it "uses specific translation if available" do
@@ -87,32 +87,33 @@ describe WillPaginate::ViewHelpers do
         :page_entries_info => {:multi_page => 'Showing %{from} - %{to}'},
         :string => { :page_entries_info => {:multi_page => 'Strings %{from} to %{to}'} }
       }
-      info(:page => 2, :per_page => 5).should == "Strings 6 to 10"
+      expect(info(:page => 2, :per_page => 5)).to eq("Strings 6 to 10")
     end
 
     it "should output HTML by default" do
-      info({ :page => 2, :per_page => 5 }, :html => true).should ==
+      expect(info({ :page => 2, :per_page => 5 }, :html => true)).to eq(
         "Displaying strings <b>6&nbsp;-&nbsp;10</b> of <b>26</b> in total"
+      )
     end
 
     it "should display shortened end results" do
-      info(:page => 7, :per_page => 4).should include_phrase('strings 25 - 26')
+      expect(info(:page => 7, :per_page => 4)).to include_phrase('strings 25 - 26')
     end
 
     it "should handle longer class names" do
       collection = @array.paginate(:page => 2, :per_page => 5)
       model = stub('Class', :name => 'ProjectType', :to_s => 'ProjectType')
       collection.first.stubs(:class).returns(model)
-      info(collection).should include_phrase('project types')
+      expect(info(collection)).to include_phrase('project types')
     end
 
     it "should adjust output for single-page collections" do
-      info(('a'..'d').to_a.paginate(:page => 1, :per_page => 5)).should == "Displaying all 4 strings"
-      info(['a'].paginate(:page => 1, :per_page => 5)).should == "Displaying 1 string"
+      expect(info(('a'..'d').to_a.paginate(:page => 1, :per_page => 5))).to eq("Displaying all 4 strings")
+      expect(info(['a'].paginate(:page => 1, :per_page => 5))).to eq("Displaying 1 string")
     end
   
     it "should display 'no entries found' for empty collections" do
-      info([].paginate(:page => 1, :per_page => 5)).should == "No entries found"
+      expect(info([].paginate(:page => 1, :per_page => 5))).to eq("No entries found")
     end
 
     it "uses model_name.human when available" do
@@ -121,7 +122,7 @@ describe WillPaginate::ViewHelpers do
       model = stub('Class', :model_name => name)
       collection = [1].paginate(:page => 1)
 
-      info(collection, :model => model).should == "Displaying 1 flower"
+      expect(info(collection, :model => model)).to eq("Displaying 1 flower")
     end
 
     it "uses custom translation instead of model_name.human" do
@@ -131,7 +132,7 @@ describe WillPaginate::ViewHelpers do
       translation :will_paginate => {:models => {:flower_key => 'tulip'}}
       collection = [1].paginate(:page => 1)
 
-      info(collection, :model => model).should == "Displaying 1 tulip"
+      expect(info(collection, :model => model)).to eq("Displaying 1 tulip")
     end
 
     private
diff --git a/spec/view_helpers/link_renderer_base_spec.rb b/spec/view_helpers/link_renderer_base_spec.rb
index cdf5f9b..2bb6a79 100644
--- a/spec/view_helpers/link_renderer_base_spec.rb
+++ b/spec/view_helpers/link_renderer_base_spec.rb
@@ -2,39 +2,39 @@ require 'spec_helper'
 require 'will_paginate/view_helpers/link_renderer_base'
 require 'will_paginate/collection'
 
-describe WillPaginate::ViewHelpers::LinkRendererBase do
+RSpec.describe WillPaginate::ViewHelpers::LinkRendererBase do
   
   before do
     @renderer = described_class.new
   end
   
   it "should raise error when unprepared" do
-    lambda {
+    expect {
       @renderer.pagination
-    }.should raise_error
+    }.to raise_error(NoMethodError)
   end
   
   it "should prepare with collection and options" do
     prepare({})
-    @renderer.send(:current_page).should == 1
+    expect(@renderer.send(:current_page)).to eq(1)
   end
   
   it "should have total_pages accessor" do
     prepare :total_pages => 42
-    @renderer.send(:total_pages).should == 42
+    expect(@renderer.send(:total_pages)).to eq(42)
   end
   
   it "should clear old cached values when prepared" do
     prepare(:total_pages => 1)
-    @renderer.send(:total_pages).should == 1
+    expect(@renderer.send(:total_pages)).to eq(1)
     # prepare with different object:
     prepare(:total_pages => 2)
-    @renderer.send(:total_pages).should == 2
+    expect(@renderer.send(:total_pages)).to eq(2)
   end
   
   it "should have pagination definition" do
     prepare({ :total_pages => 1 }, :page_links => true)
-    @renderer.pagination.should == [:previous_page, 1, :next_page]
+    expect(@renderer.pagination).to eq([:previous_page, 1, :next_page])
   end
   
   describe "visible page numbers" do
@@ -66,7 +66,7 @@ describe WillPaginate::ViewHelpers::LinkRendererBase do
     
     def showing_pages(*pages)
       pages = pages.first.to_a if Array === pages.first or Range === pages.first
-      @renderer.send(:windowed_page_numbers).should == pages
+      expect(@renderer.send(:windowed_page_numbers)).to eq(pages)
     end
   end
   
diff --git a/spec/view_helpers/view_example_group.rb b/spec/view_helpers/view_example_group.rb
index 8139f7a..5718efe 100644
--- a/spec/view_helpers/view_example_group.rb
+++ b/spec/view_helpers/view_example_group.rb
@@ -51,10 +51,10 @@ module ViewExampleGroup
   def validate_page_numbers(expected, links, param_name = :page)
     param_pattern = /\W#{Regexp.escape(param_name.to_s)}=([^&]*)/
     
-    links.map { |el|
+    expect(links.map { |el|
       unescape_href(el) =~ param_pattern
       $1 ? $1.to_i : $1
-    }.should == expected
+    }).to eq(expected)
   end
 
   def assert_links_match(pattern, links = nil, numbers = nil)
@@ -66,20 +66,20 @@ module ViewExampleGroup
     
     links.each do |el|
       href = unescape_href(el)
-      href.should =~ pattern
+      expect(href).to match(pattern)
       if numbers
         href =~ pattern
         pages << ($1.nil?? nil : $1.to_i)
       end
     end
 
-    pages.should == numbers if numbers
+    expect(pages).to eq(numbers) if numbers
   end
 
   def assert_no_links_match(pattern)
     assert_select 'div.pagination a[href]' do |elements|
       elements.each do |el|
-        unescape_href(el).should_not =~ pattern
+        expect(unescape_href(el)).not_to match(pattern)
       end
     end
   end
@@ -99,7 +99,5 @@ module ViewExampleGroup
 end
 
 RSpec.configure do |config|
-  config.include ViewExampleGroup, :type => :view, :example_group => {
-    :file_path => %r{spec/view_helpers/}
-  }
+  config.include ViewExampleGroup, :type => :view, :file_path => %r{spec/view_helpers/}
 end
diff --git a/will_paginate.gemspec b/will_paginate.gemspec
index 6762ab0..2a2dac2 100644
--- a/will_paginate.gemspec
+++ b/will_paginate.gemspec
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
   s.required_ruby_version = '>= 2.0'
   
   s.summary = "Pagination plugin for web frameworks and other apps"
-  s.description = "will_paginate provides a simple API for performing paginated queries with Active Record, DataMapper and Sequel, and includes helpers for rendering pagination links in Rails, Sinatra, Hanami, and Merb web apps."
+  s.description = "will_paginate provides a simple API for performing paginated queries with Active Record and Sequel, and includes helpers for rendering pagination links in Rails, Sinatra, and Hanami web apps."
   
   s.authors  = ['Mislav Marohnić']
   s.email    = 'mislav.marohnic@gmail.com'
@@ -26,7 +26,7 @@ Gem::Specification.new do |s|
   s.rdoc_options = ['--main', 'README.md', '--charset=UTF-8']
   s.extra_rdoc_files = ['README.md', 'LICENSE']
   
-  s.files = Dir['Rakefile', '{bin,lib,test,spec}/**/*', 'README*', 'LICENSE*']
+  s.files = Dir['lib/**/*', 'README*', 'LICENSE*']
 
   # include only files in version control
   git_dir = File.expand_path('../.git', __FILE__)

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/will_paginate-4.0.0.gemspec

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/ruby/vendor_ruby/will_paginate/data_mapper.rb
-rw-r--r--  root/root   /usr/lib/ruby/vendor_ruby/will_paginate/view_helpers/merb.rb
-rw-r--r--  root/root   /usr/share/rubygems-integration/all/specifications/will_paginate-3.3.1.gemspec

No differences were encountered in the control files

More details

Full run details