diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..4c31b83 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,129 @@ +name: Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test_sqlite: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + ruby: [ '2.7', '3.0', '3.1' ] + rails: [ '6.0', '6.1', '7.0' ] + + name: SQLite / Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} + + steps: + - uses: actions/checkout@v2 + + - 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 + 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: [ '2.7', '3.0', '3.1' ] + rails: [ '6.0', '6.1', '7.0' ] + + name: MySQL / Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} + + steps: + - uses: actions/checkout@v2 + + - 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 + 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: [ '2.7', '3.0', '3.1' ] + rails: [ '6.0', '6.1', '7.0' ] + + name: PostgreSQL / Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }} + + steps: + - uses: actions/checkout@v2 + + - 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 + run: bundle exec rake diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index d252172..0000000 --- a/.travis.yml +++ /dev/null @@ -1,44 +0,0 @@ -language: ruby -rvm: - - 2.4.6 - - 2.5.5 - - 2.6.3 -gemfile: - - gemfiles/rails_6_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.6 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: 2.4.6 - gemfile: gemfiles/rails_4_1.gemfile - - rvm: 2.4.6 - gemfile: gemfiles/rails_6_0.gemfile - - rvm: 2.5.5 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: 2.5.5 - gemfile: gemfiles/rails_4_1.gemfile - - rvm: 2.6.3 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: 2.6.3 - gemfile: gemfiles/rails_4_1.gemfile -before_install: gem update bundler -sudo: false -env: - - DB=sqlite - - DB=mysql - - DB=postgres -services: - - mysql - - 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" -addons: - postgresql: "9.4" diff --git a/Appraisals b/Appraisals index 6eafe88..446bb36 100644 --- a/Appraisals +++ b/Appraisals @@ -1,48 +1,20 @@ -appraise "rails-6-0" do - gem "activerecord", "~> 6.0.0.beta3" - gem "mysql2", ">= 0.4.4" - gem "pg", ">= 0.18", "< 2.0" +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-5-2" do - gem "activerecord", "~> 5.2.3" - gem "mysql2", ">= 0.4.4", "< 0.6.0" - gem "pg", ">= 0.18", "< 2.0" - gem "sqlite3", "~> 1.3", ">= 1.3.6" -end - -appraise "rails-5-1" do - gem "activerecord", "~> 5.1.7" - gem "mysql2", ">= 0.3.18", "< 0.6.0" - gem "pg", ">= 0.18", "< 2.0" - gem "sqlite3", "~> 1.3", ">= 1.3.6" +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 -appraise "rails-5-0" do - gem "activerecord", "~> 5.0.7" - gem 'mysql2', '>= 0.3.18', '< 0.6.0' +appraise "rails-6.0" do + gem "activerecord", "~> 6.0.0" + gem "mysql2", ">= 0.4.4" gem "pg", ">= 0.18", "< 2.0" - gem 'sqlite3', '~> 1.3.6' -end - -appraise "rails-4-2" do - gem "activerecord", "~> 4.2.11" - gem 'mysql2', '>= 0.3.13', '< 0.6.0' - gem "pg", "~> 0.15" - gem 'sqlite3', '~> 1.3.6' -end - -appraise "rails-4-1" do - gem "activerecord", "~> 4.1.16" - gem 'mysql2', '~> 0.3.13' - gem "pg", "~> 0.11" - gem 'sqlite3', '~> 1.3.6' -end - -appraise "rails-4-0" do - gem "activerecord", "~> 4.0.13" - gem "mysql2", '~> 0.3.10' - gem "pg", "~> 0.11" - gem 'sqlite3', '~> 1.3.6' + gem "sqlite3", "~> 1.4" end diff --git a/MIT-LICENSE b/MIT-LICENSE index 11f8026..77416d9 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2010-2019 Georg Ledermann +Copyright (c) 2010-2022 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 b4df74d..3b8238b 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.4 or newer -* Rails 4.0 or newer (including Rails 6) +* Ruby 2.7 or newer +* Rails 6.0 or newer (including Rails 7) * MySQL, PostgreSQL or SQLite * Needs a timestamp field in your models (like created_at or updated_at) with a database index on it @@ -223,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-2019 [Georg Ledermann](http://www.georg-ledermann.de) and [contributors](https://github.com/ledermann/unread/graphs/contributors), released under the MIT license +Copyright (c) 2010-2022 [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 3d1ccaf..0000000 --- a/gemfiles/rails_4_0.gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 4.0.13" -gem "mysql2", "~> 0.3.10" -gem "pg", "~> 0.11" -gem "sqlite3", "~> 1.3.6" - -gemspec path: "../" diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile deleted file mode 100644 index 6ede9af..0000000 --- a/gemfiles/rails_4_1.gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 4.1.16" -gem "mysql2", "~> 0.3.13" -gem "pg", "~> 0.11" -gem "sqlite3", "~> 1.3.6" - -gemspec path: "../" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile deleted file mode 100644 index a7350b1..0000000 --- a/gemfiles/rails_4_2.gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 4.2.11" -gem "mysql2", ">= 0.3.13", "< 0.6.0" -gem "pg", "~> 0.15" -gem "sqlite3", "~> 1.3.6" - -gemspec path: "../" diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile deleted file mode 100644 index a2d1171..0000000 --- a/gemfiles/rails_5_0.gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 5.0.7" -gem "mysql2", ">= 0.3.18", "< 0.6.0" -gem "pg", ">= 0.18", "< 2.0" -gem "sqlite3", "~> 1.3.6" - -gemspec path: "../" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile deleted file mode 100644 index bb41c09..0000000 --- a/gemfiles/rails_5_1.gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 5.1.7" -gem "mysql2", ">= 0.3.18", "< 0.6.0" -gem "pg", ">= 0.18", "< 2.0" -gem "sqlite3", "~> 1.3", ">= 1.3.6" - -gemspec path: "../" diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile deleted file mode 100644 index 9d1c2a6..0000000 --- a/gemfiles/rails_5_2.gemfile +++ /dev/null @@ -1,10 +0,0 @@ -# This file was generated by Appraisal - -source "https://rubygems.org" - -gem "activerecord", "~> 5.2.3" -gem "mysql2", ">= 0.4.4", "< 0.6.0" -gem "pg", ">= 0.18", "< 2.0" -gem "sqlite3", "~> 1.3", ">= 1.3.6" - -gemspec path: "../" diff --git a/gemfiles/rails_6_0.gemfile b/gemfiles/rails_6.0.gemfile similarity index 82% rename from gemfiles/rails_6_0.gemfile rename to gemfiles/rails_6.0.gemfile index 54c91d5..2fd3073 100644 --- a/gemfiles/rails_6_0.gemfile +++ b/gemfiles/rails_6.0.gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -gem "activerecord", "~> 6.0.0.beta3" +gem "activerecord", "~> 6.0.0" gem "mysql2", ">= 0.4.4" gem "pg", ">= 0.18", "< 2.0" gem "sqlite3", "~> 1.4" 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/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/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/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/version.rb b/lib/unread/version.rb index e40b0f0..d88f91b 100644 --- a/lib/unread/version.rb +++ b/lib/unread/version.rb @@ -1,3 +1,3 @@ module Unread - VERSION = '0.11.0' + VERSION = '0.12.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 c8add8c..ef275fc 100644 --- a/spec/unread/readable_spec.rb +++ b/spec/unread/readable_spec.rb @@ -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/unread.gemspec b/unread.gemspec index 97708ab..83525d8 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.4' + s.required_ruby_version = '>= 2.7' s.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/})