diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..dc07643 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,153 @@ +name: Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test_sqlite: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + ruby: ['3.1', '3.2', '3.3', '3.4'] + rails: ['6.1', '7.0', '7.1', '7.2'] + exclude: + - ruby: 3.4 + rails: 6.1 + - ruby: 3.4 + rails: 7.0 + + name: SQLite / Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: Install gems + env: + MATRIX_RAILS_VERSION: ${{ matrix.rails }} + run: | + export BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/rails_${MATRIX_RAILS_VERSION}.gemfile" + bundle install --jobs 4 --retry 3 + + - name: RSpec + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + DB: sqlite + run: bundle exec rake + + test_mysql: + runs-on: ubuntu-latest + + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: password + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + strategy: + fail-fast: false + matrix: + ruby: ['3.1', '3.2', '3.3', '3.4'] + rails: ['6.1', '7.0', '7.1', '7.2'] + exclude: + - ruby: 3.4 + rails: 6.1 + - ruby: 3.4 + rails: 7.0 + + name: MySQL / Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: Install gems + env: + MATRIX_RAILS_VERSION: ${{ matrix.rails }} + run: | + export BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/rails_${MATRIX_RAILS_VERSION}.gemfile" + bundle install --jobs 4 --retry 3 + + - name: Setup database + run: | + mysql -e 'create database IF NOT EXISTS unread_test;' -u root --password=password -P 3306 -h 127.0.0.1 + + - name: RSpec + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + DB: mysql + run: bundle exec rake + + test_postgres: + runs-on: ubuntu-latest + + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + strategy: + fail-fast: false + matrix: + ruby: ['3.1', '3.2', '3.3', '3.4'] + rails: ['6.1', '7.0', '7.1', '7.2'] + exclude: + - ruby: 3.4 + rails: 6.1 + - ruby: 3.4 + rails: 7.0 + + name: PostgreSQL / Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} + + steps: + - uses: actions/checkout@v4 + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: Install gems + env: + MATRIX_RAILS_VERSION: ${{ matrix.rails }} + run: | + export BUNDLE_GEMFILE="${GITHUB_WORKSPACE}/gemfiles/rails_${MATRIX_RAILS_VERSION}.gemfile" + bundle install --jobs 4 --retry 3 + + - name: Setup database + run: | + PGPASSWORD=postgres psql -c 'create database unread_test;' -U postgres -p 5432 -h localhost + + - name: RSpec + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + DB: postgres + run: bundle exec rake diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b1bd327..0000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: ruby -rvm: - - 2.2.9 - - 2.3.6 - - 2.4.3 - - 2.5.0 -gemfile: - - gemfiles/rails_5_2.gemfile - - gemfiles/rails_5_1.gemfile - - gemfiles/rails_5_0.gemfile - - gemfiles/rails_4_2.gemfile - - gemfiles/rails_4_1.gemfile - - gemfiles/rails_4_0.gemfile -matrix: - exclude: - - rvm: 2.4.3 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: 2.4.3 - gemfile: gemfiles/rails_4_1.gemfile - - rvm: 2.5.0 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: 2.5.0 - gemfile: gemfiles/rails_4_1.gemfile -before_install: gem update bundler -sudo: false -env: - - DB=sqlite - - DB=mysql - - DB=postgres -before_script: - - gem update --system # https://github.com/travis-ci/travis-ci/issues/8978 - - sh -c "if [ '$DB' = 'mysql' ]; then mysql -e 'create database IF NOT EXISTS unread_test;'; fi" - - sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'create database unread_test;' -U postgres; fi" diff --git a/Appraisals b/Appraisals index 547e4f9..17deb80 100644 --- a/Appraisals +++ b/Appraisals @@ -1,29 +1,27 @@ -appraise "rails-5-2" do - gem "activerecord", "~> 5.2.0.beta2" - gem "mysql2", ">= 0.3.18", "< 0.5", "!= 0.4.3" +appraise "rails-7.2" do + gem "activerecord", "~> 7.2.0" + gem "mysql2", "~> 0.5" + gem "pg", "~> 1.3" + gem "sqlite3", ">= 2.0" end -appraise "rails-5-1" do - gem "activerecord", "~> 5.1.0" - gem 'mysql2', '>= 0.3.18', '< 0.5' +appraise "rails-7.1" do + gem "activerecord", "~> 7.1.0" + gem "mysql2", "~> 0.5" + gem "pg", "~> 1.3" + gem "sqlite3", "~> 1.4" end -appraise "rails-5-0" do - gem "activerecord", "~> 5.0.0" - gem 'mysql2', '>= 0.3.18', '< 0.5' +appraise "rails-7.0" do + gem "activerecord", "~> 7.0.0" + gem "mysql2", "~> 0.5" + gem "pg", "~> 1.1" + gem "sqlite3", "~> 1.4" end -appraise "rails-4-2" do - gem "activerecord", "~> 4.2.5" - gem 'mysql2', '>= 0.3.13', '< 0.5' -end - -appraise "rails-4-1" do - gem "activerecord", "~> 4.1.14" - gem "mysql2", '~> 0.3.13' -end - -appraise "rails-4-0" do - gem "activerecord", "~> 4.0.13" - gem "mysql2", '~> 0.3.10' +appraise "rails-6.1" do + gem "activerecord", "~> 6.1.2", ">= 6.1.2.1" + gem "mysql2", "~> 0.5" + gem "pg", "~> 1.1" + gem "sqlite3", "~> 1.4" end diff --git a/MIT-LICENSE b/MIT-LICENSE index 3d98d42..75c4e85 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2010-2018 Georg Ledermann +Copyright (c) 2010-2023 Georg Ledermann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 5a1e86e..b8e0e02 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Unread Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast. -[![Build Status](https://travis-ci.org/ledermann/unread.svg?branch=master)](https://travis-ci.org/ledermann/unread) +[![Build Status](https://github.com/ledermann/unread/workflows/Test/badge.svg?branch=master)](https://github.com/ledermann/unread/actions) [![Maintainability](https://api.codeclimate.com/v1/badges/930c8df0f99b20324444/maintainability)](https://codeclimate.com/github/ledermann/unread/maintainability) [![Coverage Status](https://coveralls.io/repos/ledermann/unread/badge.svg?branch=master)](https://coveralls.io/r/ledermann/unread?branch=master) @@ -19,8 +19,8 @@ Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast. ## Requirements -* Ruby 2.2 or newer -* Rails 4.0 or newer (including Rails 5.1 and 5.2) +* Ruby 3.1 or newer +* Rails 6.1 or newer (including Rails 7.2) * MySQL, PostgreSQL or SQLite * Needs a timestamp field in your models (like created_at or updated_at) with a database index on it @@ -160,6 +160,30 @@ users[1].have_read?(message2) Message.cleanup_read_marks! ``` +## Getting read/unread stats through a relationship + +```ruby +class Document < ApplicationRecord + has_many :comments +end + +class Comment < ApplicationRecord + acts_as_readable on: :created_at + belongs_to :document +end + +# Get unread comments count for a document +document = Document.find(1) +default_hash = Hash.new { |h, k| h[k] = { unread: 0, total: 0 } } +document.comments.with_read_marks_for(current_user).reduce(default_hash) do |hash, comment| + hash[comment.id][:unread] += 1 if comment.unread?(current_user) + hash[comment.id][:total] += 1 + hash +end +# => {20=>{:unread=>1, :total=>10}, 82=>{:unread=>0, :total=>4} +``` + +Using `with_read_marks_for` here is the key. It uses just one query and makes sure that the following `unread?` invocations use the result of the first query. ## How does it work? @@ -199,4 +223,4 @@ AND messages.created_at > '2010-10-20 08:50:00' Hint: You should add a database index on `messages.created_at`. -Copyright (c) 2010-2018 [Georg Ledermann](http://www.georg-ledermann.de) and [contributors](https://github.com/ledermann/unread/graphs/contributors), released under the MIT license +Copyright (c) 2010-2023 [Georg Ledermann](https://ledermann.dev) and [contributors](https://github.com/ledermann/unread/graphs/contributors), released under the MIT license diff --git a/gemfiles/rails_4_0.gemfile b/gemfiles/rails_4_0.gemfile deleted file mode 100644 index c319a35..0000000 --- a/gemfiles/rails_4_0.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 4.0.13" -gem "mysql2", "~> 0.3.10" - -gemspec path: "../" diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile deleted file mode 100644 index 79165aa..0000000 --- a/gemfiles/rails_4_1.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 4.1.14" -gem "mysql2", "~> 0.3.13" - -gemspec path: "../" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile deleted file mode 100644 index 7a7908b..0000000 --- a/gemfiles/rails_4_2.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 4.2.5" -gem "mysql2", ">= 0.3.13", "< 0.5" - -gemspec path: "../" diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile deleted file mode 100644 index f7dfd7f..0000000 --- a/gemfiles/rails_5_0.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 5.0.0" -gem "mysql2", ">= 0.3.18", "< 0.5" - -gemspec path: "../" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile deleted file mode 100644 index 979beae..0000000 --- a/gemfiles/rails_5_1.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 5.1.0" -gem "mysql2", ">= 0.3.18", "< 0.5" - -gemspec path: "../" diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile deleted file mode 100644 index ae456ee..0000000 --- a/gemfiles/rails_5_2.gemfile +++ /dev/null @@ -1,8 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 5.2.0.beta2" -gem "mysql2", ">= 0.3.18", "< 0.5", "!= 0.4.3" - -gemspec path: "../" diff --git a/gemfiles/rails_6.1.gemfile b/gemfiles/rails_6.1.gemfile new file mode 100644 index 0000000..b107a77 --- /dev/null +++ b/gemfiles/rails_6.1.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activerecord", "~> 6.1.2", ">= 6.1.2.1" +gem "mysql2", "~> 0.5" +gem "pg", "~> 1.1" +gem "sqlite3", "~> 1.4" + +gemspec path: "../" diff --git a/gemfiles/rails_7.0.gemfile b/gemfiles/rails_7.0.gemfile new file mode 100644 index 0000000..e4e22d0 --- /dev/null +++ b/gemfiles/rails_7.0.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activerecord", "~> 7.0.0" +gem "mysql2", "~> 0.5" +gem "pg", "~> 1.1" +gem "sqlite3", "~> 1.4" + +gemspec path: "../" diff --git a/gemfiles/rails_7.1.gemfile b/gemfiles/rails_7.1.gemfile new file mode 100644 index 0000000..b155949 --- /dev/null +++ b/gemfiles/rails_7.1.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activerecord", "~> 7.1.0" +gem "mysql2", "~> 0.5" +gem "pg", "~> 1.3" +gem "sqlite3", "~> 1.4" + +gemspec path: "../" diff --git a/gemfiles/rails_7.2.gemfile b/gemfiles/rails_7.2.gemfile new file mode 100644 index 0000000..8254482 --- /dev/null +++ b/gemfiles/rails_7.2.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activerecord", "~> 7.2.0" +gem "mysql2", "~> 0.5" +gem "pg", "~> 1.3" +gem "sqlite3", ">= 2.0" + +gemspec path: "../" diff --git a/lib/generators/unread/migration/migration_generator.rb b/lib/generators/unread/migration/migration_generator.rb index e197c9b..3684340 100644 --- a/lib/generators/unread/migration/migration_generator.rb +++ b/lib/generators/unread/migration/migration_generator.rb @@ -13,11 +13,22 @@ def create_migration_file end def self.next_migration_number(dirname) - if ActiveRecord::Base.timestamped_migrations + if self.timestamped_migrations? Time.now.utc.strftime("%Y%m%d%H%M%S") else "%.3d" % (current_migration_number(dirname) + 1) end end + + def self.timestamped_migrations? + ( + ActiveRecord::Base.respond_to?(:timestamped_migrations) && + ActiveRecord::Base.timestamped_migrations + ) || + ( + ActiveRecord.respond_to?(:timestamped_migrations) && + ActiveRecord.timestamped_migrations + ) + end end end diff --git a/lib/generators/unread/migration/templates/migration.rb b/lib/generators/unread/migration/templates/migration.rb index 098a51c..099e74b 100644 --- a/lib/generators/unread/migration/templates/migration.rb +++ b/lib/generators/unread/migration/templates/migration.rb @@ -1,9 +1,9 @@ -class UnreadMigration < Unread::MIGRATION_BASE_CLASS +class UnreadMigration < ActiveRecord::Migration[6.0] def self.up create_table ReadMark, force: true, options: create_options do |t| t.references :readable, polymorphic: { null: false } t.references :reader, polymorphic: { null: false } - t.datetime :timestamp + t.datetime :timestamp, null: false end add_index ReadMark, [:reader_id, :reader_type, :readable_type, :readable_id], name: 'read_marks_reader_readable_index', unique: true diff --git a/lib/generators/unread/polymorphic_reader_migration/polymorphic_reader_migration_generator.rb b/lib/generators/unread/polymorphic_reader_migration/polymorphic_reader_migration_generator.rb index 05e0f2d..b865ac6 100644 --- a/lib/generators/unread/polymorphic_reader_migration/polymorphic_reader_migration_generator.rb +++ b/lib/generators/unread/polymorphic_reader_migration/polymorphic_reader_migration_generator.rb @@ -13,11 +13,22 @@ def create_migration_file end def self.next_migration_number(dirname) - if ActiveRecord::Base.timestamped_migrations - Time.now.utc.strftime("%Y%m%d%H%M%S") + if self.timestamped_migrations? + Time.now.utc.strftime('%Y%m%d%H%M%S') else "%.3d" % (current_migration_number(dirname) + 1) end end + + def self.timestamped_migrations? + ( + ActiveRecord::Base.respond_to?(:timestamped_migrations) && + ActiveRecord::Base.timestamped_migrations + ) || + ( + ActiveRecord.respond_to?(:timestamped_migrations) && + ActiveRecord.timestamped_migrations + ) + end end end diff --git a/lib/generators/unread/polymorphic_reader_migration/templates/unread_polymorphic_reader_migration.rb b/lib/generators/unread/polymorphic_reader_migration/templates/unread_polymorphic_reader_migration.rb index 485d4da..2113659 100644 --- a/lib/generators/unread/polymorphic_reader_migration/templates/unread_polymorphic_reader_migration.rb +++ b/lib/generators/unread/polymorphic_reader_migration/templates/unread_polymorphic_reader_migration.rb @@ -1,4 +1,4 @@ -class UnreadPolymorphicReaderMigration < Unread::MIGRATION_BASE_CLASS +class UnreadPolymorphicReaderMigration < ActiveRecord::Migration[6.0] def self.up remove_index :read_marks, [:user_id, :readable_type, :readable_id] rename_column :read_marks, :user_id, :reader_id diff --git a/lib/unread.rb b/lib/unread.rb index e617ebb..1ded71c 100644 --- a/lib/unread.rb +++ b/lib/unread.rb @@ -1,7 +1,6 @@ require 'active_record' require 'unread/base' -require 'unread/read_mark' require 'unread/readable' require 'unread/reader' require 'unread/readable_scopes' @@ -9,10 +8,8 @@ require 'unread/garbage_collector' require 'unread/version' -ActiveRecord::Base.send :include, Unread +ActiveSupport.on_load(:active_record) do + require 'unread/read_mark' -Unread::MIGRATION_BASE_CLASS = if ActiveRecord::VERSION::MAJOR >= 5 - ActiveRecord::Migration[5.0] -else - ActiveRecord::Migration + include Unread end diff --git a/lib/unread/base.rb b/lib/unread/base.rb index 3cfa080..078da27 100644 --- a/lib/unread/base.rb +++ b/lib/unread/base.rb @@ -8,7 +8,7 @@ def acts_as_reader ReadMark.reader_classes ||= [] unless ReadMark.reader_classes.include?(self) - ReadMark.belongs_to :reader, polymorphic: true, inverse_of: :read_marks + ReadMark.belongs_to :reader, polymorphic: true, inverse_of: :read_marks, optional: true has_many :read_marks, dependent: :delete_all, as: :reader, inverse_of: :reader diff --git a/lib/unread/readable_scopes.rb b/lib/unread/readable_scopes.rb index ca1ca4d..eeff7b1 100644 --- a/lib/unread/readable_scopes.rb +++ b/lib/unread/readable_scopes.rb @@ -7,8 +7,8 @@ def join_read_marks(reader) joins "LEFT JOIN #{ReadMark.quoted_table_name} ON #{ReadMark.quoted_table_name}.readable_type = '#{readable_parent.name}' AND #{ReadMark.quoted_table_name}.readable_id = #{quoted_table_name}.#{quoted_primary_key} - AND #{ReadMark.quoted_table_name}.reader_id = #{quote_bound_value(reader.id)} - AND #{ReadMark.quoted_table_name}.reader_type = #{quote_bound_value(reader.class.base_class.name)} + AND #{ReadMark.quoted_table_name}.reader_id = #{quoted(reader.id)} + AND #{ReadMark.quoted_table_name}.reader_type = #{quoted(reader.class.base_class.name)} AND #{ReadMark.quoted_table_name}.timestamp >= #{quoted_table_name}.#{connection.quote_column_name(readable_options[:on])}" end @@ -39,8 +39,16 @@ def with_read_marks_for(reader) join_read_marks(reader).select("#{quoted_table_name}.*, #{ReadMark.quoted_table_name}.id AS read_mark_id, - #{quote_bound_value(reader.class.base_class.name)}#{postgresql_string_cast} AS read_mark_reader_type, - #{quote_bound_value(reader.id)} AS read_mark_reader_id") + #{quoted(reader.class.base_class.name)}#{postgresql_string_cast} AS read_mark_reader_type, + #{quoted(reader.id)} AS read_mark_reader_id") + end + + def quoted(value) + if Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new('7.2') + quote_bound_value(connection, value) + else + quote_bound_value(value) + end end end end diff --git a/lib/unread/reader.rb b/lib/unread/reader.rb index f526728..dce4e96 100644 --- a/lib/unread/reader.rb +++ b/lib/unread/reader.rb @@ -16,7 +16,13 @@ module InstanceMethods def read_mark_global(klass) @read_mark_global ||= {} readable_klass = klass.readable_parent - @read_mark_global[readable_klass] ||= read_marks.where(readable_type: readable_klass.name).global.first + + # Memoize with NIL handling + if @read_mark_global.has_key?(readable_klass) + @read_mark_global[readable_klass] + else + @read_mark_global[readable_klass] = read_marks.where(readable_type: readable_klass.name).global.first + end end def forget_memoized_read_mark_global diff --git a/lib/unread/reader_scopes.rb b/lib/unread/reader_scopes.rb index b6936c8..00a4831 100644 --- a/lib/unread/reader_scopes.rb +++ b/lib/unread/reader_scopes.rb @@ -12,7 +12,7 @@ def join_read_marks(readable) joins "LEFT JOIN #{ReadMark.quoted_table_name} ON #{ReadMark.quoted_table_name}.readable_type = '#{readable.class.readable_parent.name}' - AND (#{ReadMark.quoted_table_name}.readable_id = #{readable.id} OR #{ReadMark.quoted_table_name}.readable_id IS NULL) + AND (#{ReadMark.quoted_table_name}.readable_id = #{quoted(readable.id)} OR #{ReadMark.quoted_table_name}.readable_id IS NULL) AND #{ReadMark.quoted_table_name}.reader_id = #{quoted_table_name}.#{quoted_primary_key} AND #{ReadMark.quoted_table_name}.reader_type = '#{connection.quote_string base_class.name}' AND #{ReadMark.quoted_table_name}.timestamp >= '#{connection.quoted_date readable.send(readable.class.readable_options[:on])}'" @@ -31,8 +31,16 @@ def with_read_marks_for(readable) join_read_marks(readable).select("#{quoted_table_name}.*, #{ReadMark.quoted_table_name}.id AS read_mark_id, - #{quote_bound_value readable.class.name}#{postgresql_string_cast} AS read_mark_readable_type, - #{readable.id} AS read_mark_readable_id") + #{quoted(readable.class.name)}#{postgresql_string_cast} AS read_mark_readable_type, + #{quoted(readable.id)} AS read_mark_readable_id") + end + + def quoted(value) + if Gem::Version.new(ActiveRecord::VERSION::STRING) >= Gem::Version.new('7.2') + quote_bound_value(connection, value) + else + quote_bound_value(value) + end end end end diff --git a/lib/unread/version.rb b/lib/unread/version.rb index d1da520..5008351 100644 --- a/lib/unread/version.rb +++ b/lib/unread/version.rb @@ -1,3 +1,3 @@ module Unread - VERSION = '0.10.1' + VERSION = '0.14.0' end diff --git a/spec/database.yml b/spec/database.yml index d7bc521..139b8d9 100644 --- a/spec/database.yml +++ b/spec/database.yml @@ -5,8 +5,14 @@ mysql: adapter: mysql2 database: unread_test username: root + password: password encoding: utf8 + host: 127.0.0.1 + port: 3306 postgres: adapter: postgresql database: unread_test username: postgres + password: postgres + host: localhost + port: 5432 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 45f5c45..34fd217 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -64,7 +64,12 @@ def setup_db puts "Testing with ActiveRecord #{ActiveRecord::VERSION::STRING} on #{db_name}" ActiveRecord::Base.establish_connection(db_name.to_sym) - ActiveRecord::Base.default_timezone = :utc + if ActiveRecord.respond_to?(:default_timezone=) + # Rails 7 + ActiveRecord.default_timezone = :utc + else + ActiveRecord::Base.default_timezone = :utc + end ActiveRecord::Migration.verbose = false UnreadMigration.up diff --git a/spec/support/spec_migration.rb b/spec/support/spec_migration.rb index eb641ec..70a2398 100644 --- a/spec/support/spec_migration.rb +++ b/spec/support/spec_migration.rb @@ -1,4 +1,4 @@ -class SpecMigration < Unread::MIGRATION_BASE_CLASS +class SpecMigration < ActiveRecord::Migration[6.0] def self.up create_table Reader, primary_key: 'number', force: true do |t| t.string :name diff --git a/spec/unread/readable_spec.rb b/spec/unread/readable_spec.rb index b742b74..ef275fc 100644 --- a/spec/unread/readable_spec.rb +++ b/spec/unread/readable_spec.rb @@ -195,7 +195,7 @@ wait expect(@email1.unread?(@reader)).to be_falsey - @email1.update_attributes! subject: 'changed' + @email1.update! subject: 'changed' expect(@email1.unread?(@reader)).to be_truthy end @@ -253,6 +253,34 @@ expect(@reader.read_marks.single.count).to eq 1 end + + context 'when the reader class defines a default_scope that excludes tha reader instance' do + before { ReadMark.stub(belongs_to_required_by_default: true) } + + let!(:reader_class) do + CustomReader = Class.new(ActiveRecord::Base) do + self.primary_key = 'number' + self.table_name = 'readers' + + acts_as_reader + + default_scope { where.not(name: 'foo') } + end + end + let!(:reader) { reader_class.create!(name: 'foo') } + let(:document) { Document.create! } + + before do + wait + document + end + + subject { document.mark_as_read!(for: reader) } + + it 'does not raise_error' do + expect { subject }.not_to raise_error + end + end end describe '.mark_as_read!' do diff --git a/spec/unread/reader_spec.rb b/spec/unread/reader_spec.rb index 5f0fea0..05cb0a4 100644 --- a/spec/unread/reader_spec.rb +++ b/spec/unread/reader_spec.rb @@ -149,7 +149,7 @@ wait expect(@reader.have_read?(@email1)).to be_truthy - @email1.update_attributes! subject: 'changed' + @email1.update! subject: 'changed' expect(@reader.have_read?(@email1)).to be_falsey end diff --git a/unread.gemspec b/unread.gemspec index 7f7b982..3dcfbca 100644 --- a/unread.gemspec +++ b/unread.gemspec @@ -8,11 +8,11 @@ Gem::Specification.new do |s| s.version = Unread::VERSION s.licenses = ['MIT'] s.authors = ["Georg Ledermann"] - s.email = ["mail@georg-ledermann.de"] + s.email = ["georg@ledermann.dev"] s.homepage = "https://github.com/ledermann/unread" s.summary = %q{Manages read/unread status of ActiveRecord objects} s.description = %q{This gem creates a scope for unread objects and adds methods to mark objects as read } - s.required_ruby_version = '>= 2.2' + s.required_ruby_version = '>= 3.1' s.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) @@ -21,17 +21,17 @@ Gem::Specification.new do |s| s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) } s.require_paths = ["lib"] - s.add_dependency 'activerecord', '>= 3' + s.add_dependency 'activerecord', '>= 6.1' s.add_development_dependency 'bundler' s.add_development_dependency 'rake' s.add_development_dependency 'timecop' s.add_development_dependency 'sqlite3' s.add_development_dependency 'mysql2' - s.add_development_dependency 'pg', '< 1' + s.add_development_dependency 'pg' s.add_development_dependency 'rspec' s.add_development_dependency 'simplecov' s.add_development_dependency 'term-ansicolor' - s.add_development_dependency 'coveralls' + s.add_development_dependency 'coveralls_reborn' s.add_development_dependency 'appraisal' end