Git Product home page Git Product logo

unread's Introduction

Unread

Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast.

Build Status Maintainability Coverage Status

Features

  • Manages unread records for anything you want readers (e.g. users) to read (like messages, documents, comments etc.)
  • Supports mark as read to mark a single record as read
  • Supports mark all as read to mark all records as read in a single step
  • Gives you a scope to get the unread records for a given reader
  • Needs only one additional database table
  • Most important: Great performance

Requirements

  • Ruby 3.0 or newer
  • Rails 6.1 or newer (including Rails 7.1)
  • MySQL, PostgreSQL or SQLite
  • Needs a timestamp field in your models (like created_at or updated_at) with a database index on it

Changelog

https://github.com/ledermann/unread/releases

Installation

Step 1: Add this to your Gemfile:

gem 'unread'

and run

bundle

Step 2: Generate and run the migration:

rails g unread:migration
rake db:migrate

Upgrade from previous releases

If you upgrade from an older release of this gem, you should read the upgrade notes.

Usage

class User < ActiveRecord::Base
  acts_as_reader

  # Optional: Allow a subset of users as readers only
  def self.reader_scope
    where(is_admin: true)
  end
end

class Message < ActiveRecord::Base
  acts_as_readable on: :created_at

  # The `on:` option sets the relevant attribute for comparing timestamps.
  #
  # The default is :updated_at, so updating a record, which was read by a
  # reader makes it unread again.
  #
  # Using :created_at, only new records will show up as unread. Updating a
  # record which was read by a reader, will NOT mark it as unread.
  #
  # Any other existing timestamp field can be used as `on:` option.
end

message1 = Message.create!
message2 = Message.create!

## Get unread messages for a given user
Message.unread_by(current_user)
# => [ message1, message2 ]

message1.mark_as_read! for: current_user
Message.unread_by(current_user)
# => [ message2 ]

## Get read messages for a given user
Message.read_by(current_user)
# => [ ]

message1.mark_as_read! for: current_user
Message.read_by(current_user)
# => [ message1 ]

## Get all messages including the read status for a given user
messages = Message.with_read_marks_for(current_user)
# => [ message1, message2 ]
messages[0].unread?(current_user)
# => false
messages[1].unread?(current_user)
# => true

Message.mark_as_read! :all, for: current_user
Message.unread_by(current_user)
# => [ ]

Message.read_by(current_user)
# => [ message1, message2 ]

## Get users that have not read a given message
user1 = User.create!
user2 = User.create!

User.have_not_read(message1)
# => [ user1, user2 ]

message1.mark_as_read! for: user1
User.have_not_read(message1)
# => [ user2 ]

## Get users that have read a given message
User.have_read(message1)
# => [ user1 ]

message1.mark_as_read! for: user2
User.have_read(message1)
# => [ user1, user2 ]

Message.mark_as_read! :all, for: user1
User.have_not_read(message1)
# => [ ]
User.have_not_read(message2)
# => [ user2 ]

User.have_read(message1)
# => [ user1, user2 ]
User.have_read(message2)
# => [ user1 ]

## Get all users including their read status for a given message
users = User.with_read_marks_for(message1)
# => [ user1, user2 ]
users[0].have_read?(message1)
# => true
users[1].have_read?(message2)
# => false

# Optional: Cleaning up unneeded markers
# Do this in a cron job once a day
Message.cleanup_read_marks!

Getting read/unread stats through a relationship

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?

The main idea of this gem is to manage a list of read items for every reader after a certain timestamp.

The gem defines a scope doing a LEFT JOIN to this list, so your app can get the unread items in a performant manner. Of course, other scopes can be combined.

It will be ensured that the list of read items will not grow up too much:

  • If a user uses "mark all as read", his list gets deleted and the timestamp is set to the current time.
  • If a user never uses "mark all as read", the list will grow and grow with each item he reads. But there is help: Your app can use a cleanup method which removes unnecessary list items.

Overall, this gem can be used for large data. Please have a look at the generated SQL queries, here is an example:

# Assuming we have a user who has marked all messages as read on 2010-10-20 08:50
current_user = User.find(42)

# Get the unread messages for this user
Message.unread_by(current_user)

Generated query:

SELECT messages.*
FROM messages
LEFT JOIN read_marks ON read_marks.readable_type = "Message"
                    AND read_marks.readable_id = messages.id
                    AND read_marks.reader_id = 42
                    AND read_marks.reader_type = 'User'
                    AND read_marks.timestamp >= messages.created_at
WHERE read_marks.id IS NULL
AND messages.created_at > '2010-10-20 08:50:00'

Hint: You should add a database index on messages.created_at.

Copyright (c) 2010-2023 Georg Ledermann and contributors, released under the MIT license

unread's People

Contributors

allenwq avatar bcavileer avatar everplays avatar fatkodima avatar felipetio avatar fotos avatar frexuz avatar huiping avatar jarinudom avatar juanitofatas avatar jumichot avatar k-rudy avatar koriroys avatar kyuden avatar lazyatom avatar ledermann avatar lowjoel avatar meacuna avatar mifrill avatar mikeki avatar parndt avatar punchdrunker avatar pyro2927 avatar sadfuzzy avatar simpl1g avatar taher-ghaleb avatar tangopium avatar xzhflying avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

unread's Issues

Mark as read not working after changing timezone of application to EST

Hi,

For some reason, after changing the timezone of my application to EST, marking items are read no longer works. I believe it is caused by the left join clause: "AND read_marks.timestamp >= messages.created_at" but I am not sure how to debug/fix this or why this is happening

Reverting to the old default timezone of UTC fixes this.

Any help would be greatly appreciated!
Thanks

hardcoded dependency on User

This has a hardcoded dependency on User meaning if your user class is called Account, you can't use this gem. Is there any way around this ? Or are there any plans of making it more flexible ?

RSpec matchers

Would you be interested in shipping RSpec matchers with this gem?

I only have two in mind actually for now:

# unread_matchers.rb
RSpec::Matchers.define :be_readable do
  # check to see if the model has `acts_as_readable` declared on it
  match { |actual| actual.is_a?(::Unread::Readable::InstanceMethods) }
end

RSpec::Matchers.define :be_a_reader do
  # check to see if the model has `acts_as_reader` declared on it
  match { |actual| actual.is_a?(::Unread::Reader::InstanceMethods) }
end

# message_spec.rb
it { is_expected.to be_readable }

# user_spec.rb
it { is_expected.to be_a_reader }

There is no class using acts_as_reader!

Hi there,

I've created a rake task to run cleanup_read_marks! but it's complaining that There is no class using acts_as_reader!. Trying in the console yields the same error. Any ideas?

/usr/local/opt/rbenv/versions/jruby-1.7.4/gemsets/400rc1/gems/faraday-0.8.7/lib/faraday/request/multipart.rb:5 warning: already initialized constant DEFAULT_BOUNDARY
rake aborted!
There is no class using acts_as_reader!
/usr/local/opt/rbenv/versions/jruby-1.7.4/gemsets/400rc1/gems/unread-0.3.0/lib/unread/readable.rb:97:in `assert_reader_class'
/usr/local/opt/rbenv/versions/jruby-1.7.4/gemsets/400rc1/gems/unread-0.3.0/lib/unread/readable.rb:43:in `cleanup_read_marks!'
/Users/Ken/Code/j-platform/lib/tasks/j_platform.rake:18:in `(root)'

in-persistence attribute of unread boolean

Was looking at this method but behaved not as expected:
messages = Message.with_read_marks_for(current_user)

I was thinking if a method called with_unread_attr_for(reader) that would return all a list of readables with boolean attribute value indicating if it is unread by the reader. This might save us making additional calls to database to get the unread status of a list of readables.

Or am I missing something?

#read_mark_global - error when 'klass' is namespaced.

NameError

`@read_mark_global_Carrier::Message' is not allowed as an instance variable name

Rails.root: /home/stanislaw/work/apps/distant
Application Trace | Framework Trace | Full Trace

unread (0.1.0) lib/unread/acts_as_readable.rb:196:in instance_variable_get' unread (0.1.0) lib/unread/acts_as_readable.rb:196:inread_mark_global'
unread (0.1.0) lib/unread/acts_as_readable.rb:46:in block in acts_as_readable' activerecord (3.2.3) lib/active_record/scoping/named.rb:180:incall'
activerecord (3.2.3) lib/active_record/scoping/named.rb:180:in block (2 levels) in scope' activerecord (3.2.3) lib/active_record/scoping/default.rb:41:inblock in unscoped'

Accessing read/unread stats via another relationship

Hi there, I've spent a bit of time trying to figure this out so I thought I'd ask here in case you had any pointers :)

I have a model called Values. A value has many comments, and the comments readable via your gem.

I'm rendering a document full of values so I want to select all the values in a document, and also have an additional two columns: comments_count and unread_comments_count. This allows me to render the document indicating the comments as efficiently as possible. Right now I'm issuing a query for each unread_by call that is causing a performance problem.

I've had some success getting an unread_count by merging unread_by() into the query and forcing a LEFT JOIN between values and comments. But then it doesn't seem possible to get a count of all of the comments (read and unread) in that same query.

Any pointers would be greatly appreciated. :)

Can't create global read marks if a model is both reader and readable

If have a model like the following

class User < ActiveRecord::Base
  acts_as_reader
  acts_as_readable :on => :created_at
end

marking all instances of a model won't work. It seems that calling has_many with the same association. if I change the call of has_many in acts_as_readable to

has_many :read_marks_readable, :class_name => "ReadMark", :as => :readable, :dependent => :delete_all

all is well (if readable.rb is updated too, of course). Should I issue a pull request for that change? And if so do you have a good idea what to call the associations?

Can't mass-assign protected attributes for ReadMark: readable_type, user_id, timestamp

I'm using Devise 3.2.3 and Unread 0.3.1 on Rails 4.0.3. I've recently encountered this error while trying to create a new User..

I have the User model set to acts_as_reader and the PrivateMessage model acts_as_readable :on => :created_at. Here's the debugger output:

SQL (1.1ms) INSERT INTO "users" ("created_at", "email", "encrypted_password", "slug", "updated_at", "user_name") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["created_at", Tue, 25 Feb 2014 03:11:34 UTC +00:00], ["email", "[email protected]"], ["encrypted_password", "$2a$10$DNW/W82Nk9sWWZgvwM6i3.Ah7DbTcAUuxPlV.suKCrV1UFdY69v5i"], ["slug", "dasun1"], ["updated_at", Tue, 25 Feb 2014 03:11:34 UTC +00:00], ["user_name", "dasun1"]]
SQL (0.6ms) DELETE FROM "read_marks" WHERE "read_marks"."readable_type" = 'PrivateMessage' AND "read_marks"."user_id" = 60
WARNING: Can't mass-assign protected attributes for ReadMark: readable_type, user_id, timestamp
app/controllers/registrations_controller.rb:9:in `create'
(0.4ms) ROLLBACK
Redirected to http://localhost:3000/users

A lot of same requests after new reader creating

After creating reader (user), I getting a lot of same requests. It's creating and deleting read_mark about 18 times.

SQL (6.7ms) DELETE FROM "read_marks" WHERE "read_marks"."reader_id" = $1 AND "read_marks"."reader_type" = $2 AND "read_marks"."readable_type" = $3 [["reader_id", 50763], ["reader_type", "User"], ["readable_type", "Event"]]

SQL (11.9ms) INSERT INTO "read_marks" ("reader_id", "reader_type", "readable_type", "timestamp") VALUES ($1, $2, $3, $4) RETURNING "id" [["reader_id", 50763], ["reader_type", "User"], ["readable_type", "Event"], ["timestamp", "2016-09-14 13:12:51.715057"]]

2016-09-14 16 24 24

Get list of records that have unread related records.

I have Conversation that has_many Messages. Is there best way to get Conversations with unread messages inside?

class Conversation < ApplicationRecord
  has_many :messages
end

class Message < ApplicationRecord
  acts_as_readable :on => :created_at
  belongs_to :conversation
end

After few hours of try's, and after I wrote this question (order thoughts) -
my solution is:

user = User.last
Conversation.joins(:messages).where(id: Message.unread_by(user).pluck(:conversation_id))

Think this query will return Conversations with unread messages inside. May be there is the better way?

Support for a UUID primary key

Does this gem work with UUID as the primary key?

I looked into the source code, but couldn't find anything. I also tried it on a sample app, but it breaks on this line in readable_scopes.rb:

AND read_marks.user_id        = #{user.id}

It results in something like:

AND read_marks.user_id          = e61422-0e0c-4a49-9a4

Something like:

AND read_marks.user_id          = 'e61422-0e0c-4a49-9a4'

... seems to work. Any ideas?

Multi-tenancy rails application issues

Hi, we are building out a multi-tenancy web app which requires adding the account_id field on read_mark table. We would love to use unread gem and is it possible to allow us for insert the custom field via code?

Problem with readmarks JOIN query when model testing with Rspec

Hi. I've been using your fantastic gem on a project that I'm now writing a test suite for and am having some problems I cannot figure out. I have fully detailed this issue here.

Do you have any suggestions or knowledge as to what I can do to get this working? Any help would be greatly appreciated!

mark_as_read! not working without timeout

Hello. I found a strange bug. When I execute this single-line code inside console:

Message.create; Message.mark_as_read! :all, :for => User.first; Message.unread_by(User.first)

I have this result, but it must be empty array:

 [#<Message:0x007f8cd8893318
  id: 18,
  created_at: Fri, 22 May 2015 23:52:09 MSK +03:00,
  updated_at: Fri, 22 May 2015 23:52:09 MSK +03:00>]

When I execute this code with sleep, it works correctly:

Message.create; sleep(1); Message.mark_as_read! :all, :for => User.first; Message.unread_by(User.first)

Chat - is a realtime application. It should not work so...

Small dependency issue when trying to do a fresh 'bundle install'

If I try to do a bundle install on a fresh machine (or rvm version) before any gems have been installed (except bundler, of course), unread causes an error:

There was a LoadError while evaluating unread.gemspec: no such file to load -- active_record/version from (rails_root)/rails/ruby/1.8/bundler/gems/unread-2958a58bb126/unread.gemspec:4

I have to comment out the unread line in my gemfile, run bundle install, uncomment, and run it again. Not a big deal, but something to consider.

Class User is not registered by acts_as_reader!

Hey Georg,

I don't mean to bother you with all of these issues, but I figure that it may at least be helpful to you, even if we can't solve my issue specifically.

With the commit that refactored the checking (0d10f77), I am now getting the "Class User is not registered by acts_as_reader!" argument error thrown at me. However, it only happens AFTER the first request... in other words, I am able to check for Topic.unread_by(@current_user) successfully once, but then any time after that throws the error. I do have acts_as_reader on my User model. Like we agreed before, it's probably some anomaly with my code that is causing these strange issues.

I'm using tree 95e93bc now, as this is the last version of your gem that has worked for me, but I'd love to be able to use the latest commits. Using rails 3.0.9 and Ruby 1.9.2.

Again, thanks for the hard work on this gem!

Slow N+1 query

The method 'mark_array_as_read' has a N+1 query when creating read_marks for multiple readables.

Wrapping the 'save!' method of all objects in a single transaction could speed up the process.

.cleanup_read_marks! performance?

The current .cleanup_read_marks! appears to iterate over every entry in the table, checking for older entries, and then deleting all but the newest. This could result in a lot of un-used find queries where there are no read marks that need to be cleaned for a certain readable. It seems like it could be optimized by having a query with some sort of WHERE COUNT(read_marks.id) > 1 GROUP BY readable_id, readable_type, which would only then iterate over things that actually needed cleaning.

Class.mark_as_read! :all, for: user doesn't mark all as unread in a transaction

When inside a transaction, say:

Book.transaction do
  Book.mark_as_read! :all, for: user
  Book.unread_by(user)
end

would still return unread books from before the transaction. I understand that this could be by design, but perhaps there could be instance variables set above the persistence level to provide support for this scenario?

Rails5 delete_all(conditions: ) deprecation warning

Hi,

Is there a rail5 branch for testing? AFAICT acts_as_reader will cause deprecation warnings on delete_all, as below:

DEPRECATION WARNING: Passing conditions to delete_all is deprecated and will be removed in Rails 5.1. To achieve the same use where(conditions).delete_all

Problem in usage examples

Currently, when a new user(reader) is created, all messages (readables) created before that point in time will be marked as read. It can only be disabled by overriding the setup_new_reader function in the reader.

So the in the example have_not_read method should ideally return empty.

## Get users that have not read a given message
user1 = User.create!
user2 = User.create!

User.have_not_read(message1)
# => [ user1, user2 ]

.unread? return unexpected value

If exist class:

class A
end

class B < A
  acts_as_readable :on => :created_at
end

Then use it like here:

b = B.create!
b.mark_as_read! for => user

b.unread?(user) # It will return a unexpected result

Unnecessary SQL when creating a new record with acts_as_reader

I have a user with acts_as_reader marked, and when I create a new user I get the following:


AREL (0.2ms)  DELETE FROM `read_marks` WHERE `read_marks`.`user_id` = 32 AND `read_marks`.`readable_type` = 'Comment'
  AREL (0.2ms)  INSERT INTO `read_marks` (`user_id`, `readable_type`, `timestamp`, `readable_id`) VALUES (32, 'Comment', '2011-08-26 07:40:19', NULL)
  AREL (0.3ms)  DELETE FROM `read_marks` WHERE `read_marks`.`user_id` = 32 AND `read_marks`.`readable_type` = 'Comment'
  AREL (0.2ms)  INSERT INTO `read_marks` (`user_id`, `readable_type`, `timestamp`, `readable_id`) VALUES (32, 'Comment', '2011-08-26 07:40:19', NULL)
  AREL (0.3ms)  DELETE FROM `read_marks` WHERE `read_marks`.`user_id` = 32 AND `read_marks`.`readable_type` = 'Comment'
  AREL (0.2ms)  INSERT INTO `read_marks` (`user_id`, `readable_type`, `timestamp`, `readable_id`) VALUES (32, 'Comment', '2011-08-26 07:40:19', NULL)
  AREL (0.2ms)  DELETE FROM `read_marks` WHERE `read_marks`.`user_id` = 32 AND `read_marks`.`readable_type` = 'Comment'
  AREL (0.2ms)  INSERT INTO `read_marks` (`user_id`, `readable_type`, `timestamp`, `readable_id`) VALUES (32, 'Comment', '2011-08-26 07:40:19', NULL)
  AREL (0.2ms)  DELETE FROM `read_marks` WHERE `read_marks`.`user_id` = 32 AND `read_marks`.`readable_type` = 'Comment'

It does this about 40 times... As you can see, it's just inserting and then deleting the same record over and over into the read_marks table. This behavior can't be normal... Is there a way to stop it from happening? It is slowing down the record creation significantly.

No extension file name .rb

I ran rails generate unread:migration, got the migration file without extension name: .rb
so rake db:migrate won't work.

but I saw there was .rb in:
https://github.com/ledermann/unread/blob/master/lib/generators/unread/migration/migration_generator.rb#L12

weird ....

About my application's environment

Ruby version              2.0.0-p576 (x86_64-darwin14.0.0)
RubyGems version          2.2.2
Rack version              1.5
Rails version             4.1.6
JavaScript Runtime        Node.js (V8)
Active Record version     4.1.6
Action Pack version       4.1.6
Action View version       4.1.6
Action Mailer version     4.1.6
Active Support version    4.1.6
Middleware                Rack::Sendfile, ActionDispatch::Static, Rack::Lock, #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007f9d142648d8>, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, BetterErrors::Middleware, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag, Warden::Manager
Environment               development
Database adapter          sqlite3
Database schema version   20141205085617

Does not work with non pluralized table names

We use singular table names in our apps by setting ActiveRecord::Base.pluralize_table_names = false in application.rb. Baked in references to "read_marks" causes SQL errors.

Would using "#{ReadMark.table_name}" in the sql strings in the scopes instead of "read_marks" fix this issue?

Mark as read for all

As per the example...

Model.mark_as_read! :all, for: current_user

...when I try to use this in the console it looks like it's expecting more like it's missing a closing bracket or something...

2.3.1 :009 > Model.mark_as_read!(:all, for: User.find(1))
2.3.1 :010?>

Even if I don't enclose it in brackets as per the example. Am I referencing this wrong?

I have the latest version of the gem. Thanks!

The primary key of my model is not 'id'

My "Vehicle" model's primary key is 'vin' and not 'id'.

When I use the unread gem it doesn't work when I call vehicle.mark_as_read! for: current_user

It works when my primary_key is 'id'.

Customizing

How can i specify an array of people ids, that will not get an "unread" mark for the message?

STI & Unread not working as expected

I have the following configuration:

class Error < ActiveRecord::Base
   ...
end

class UserError < Error
   acts_as_readable on: :created_at
   ...
end

class SystemError < Error
   acts_as_readable on: :created_at
   ...
end

If I now do the following: UserError.mark_as_read! :all, for: current_user actually all entries of the class "Error" are marked as read. Isn't it possible to save in the readable_type the actual class and not the base class?

Thank you in advance for your time and efforts!

Any way to mark a record as unread?

Hi, I want to mark a marked record as unread, I thinks it should be easy to add mark_as_unread! method. But after dig into the code, I found that unable to mark a record as unread if I have called SomeClass.mark_as_read! :all, for: user. So do you have any suggestion to implement the mark_as_unread! method?

Feature Idea: Multiple types of 'read'

Let me start by quoting the fish shell docs and state that, since "configurability is the root of all evil," I am knowingly proposing something that might seem a bit evil.

That said, I think it would be really useful to have different types of "read" state. The use case I have in mind is the ability to mark records as having been viewed on the index action, as opposed to having been actually clicked and opened. (i.e. distinguishing between records that have never been seen in any form, vs records that haven't yet been 'read' in full)

Another use case could be for records that go out in a digest email -- marking records as 'emailed' ensures that the same records won't be included in the next digest. This allows for more flexible scheduling of such emails (for instance, sending an email as soon as 10 records exist that haven't been 'emailed').

So, in short, different types of read -- 'read', 'seen', 'emailed', 'viewed', etc -- all powered by the same underlying infrastructure of tracking and marking elements as having been verbed at a specific timestamp by (or to) a specific user.

The schema changes would probably involve adding a read_type column which would default to 'read', and then updating the queries so that they search by a specified read type (also defaulting to 'read'). So the defaults would cause the gem to behave exactly as it does now.

I am actually quite happy to work on a pull request, but I thought I'd field the idea first.

Allow multi-level STI

If you have this scenario

class User
    acts_as_reader
end
class A
    acts_as_readable on: :created_at
end

class A < B
end

class B < C
end

And try to do something like

user = User.create!
c = C.create!

# Assume A.last is the same record as c
a = A.last
a.mark_as_read!(for: user)
user.have_read?(c)
# Expect true, but got false

I think it has to do with readable_parent behaviour.

I made a pull request to address this issue.

"ArgumentError: Class User is not registered by acts_as_readable."

I haven't been able to get this to work at all. Rails 5.1. Seems to be set up correctly. I have a User model and a Message model. User has acts_as_reader, Message has acts_as_readable on: :created_at. The initial gem migration has been run. The read_marks table exists.

But... if I try to run any of the gem's commands in the console, like for example -

Message.read_by(someuser)

(where someuser is a variable with one user assigned to it), I get -

ArgumentError: Class User is not registered by acts_as_readable.

Any idea what that's about? I've googled around and haven't found much. Nor has digging into the code been illuminating to this point. I mean... as far as I can tell, this is set up correctly. But it's not tracking read/unread messages per user.

Couldn't find unread_migration generator

script/plugin install git://github.com/ledermann/unread.git ->
available script/generators -- mark_as_read_migration.

script/generate unread_migration fails with "Couldn't find unread_migration generator"

rename vendors/plugins/unread/generators/mark_as_read_migration to unread_migration (as in documentation) and then
script/generate unread_migration works ok

Can there be an unread scope in the reader

I wonder if it's possible and necessary to have this kind of scope in the reader:

reader.unread(registered_model_name)

For example, if the User is the reader and the Notification is readable, we want to get user's unread notifications. Maybe we need this kind of scope( user is an instance of User) :

  • user.unread(Notification)

It performs just like Notification.unread_by(user), but maybe it can be used to custom some scopes.

For instance, if there is a type of notification called popup, we want to create a scope for the user called unread_popups. How can we do it now?

But with the new scope, we can do it like this scope :unread_popups -> { unread(Notification).where( notification_type: 'popup') }.

Will this be a useful suggestion? ๐Ÿ˜ณ

Documentation for `on:` of `acts_as_readable`

Hey! I was wondering if I could get a bit more documentation on the on: part of acts_as_readable, on: :update. I saw the code here and the specs here, but couldn't really figure out what it's good for.

Could somebody elaborate a bit?

Adding a scope to unread

Issue I received via email:


I need to add the ability to monitor items only if the user has subscribed to that parent's item.

Example:

User subscribes to a Project.
That Project has Notes.
User sees there are unread Notes for the subscribed Projects but not other projects.

My thoughts are that I could create lib/assets/modules/unread/readable/scopes.rb that looks like

module Unread
    module Readable
        module Scopes
            def subscribed_unread_by(user)
                unread_by(user) joins 'my join clause to select subscribed items'
            end
        end
    end
end

Do you think this methodology would work?

migration error on utf8mb4 table

I'm using utf8mb4 as default charset of mysql. (utf8 is very popular setting in APAC region)
When I ran the latest migration of unread, I was caught by limitation of key length (767bytes)

Mysql2::Error: Specified key was too long; max key length is 767 bytes:
 CREATE UNIQUE INDEX `read_marks_reader_readable_index`  ON `read_marks` (`reader_id`, `reader_type`, `readable_type`, `readable_id`) 

Because utf8 requires 3 or 4 times longer bytes than latin1, those columns are too large to create a unique index.

I think there are 2 solutions.
One is adding CHARSET option to create statement.

create_table ReadMark, :options => 'DEFAULT CHARSET=latin1' ...

This ignores default charset of database.
Actually I don't use multibyte characters for readable_type and reader_type, so this makes no problem.
But for someone using multibyte characters for readable_type and reader_type, it may be a breaking change.

Another one is shortening readable_type and reader_type. This is more breaking change.

 change_column ReadMark, :readable_type, :string, :limit=>100
 change_column ReadMark, :reader_type, :string, :limit=>100

I prefere former one, so can I make PR to add charset option?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.