unread-mongoid 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +76 -0
- data/Rakefile +15 -0
- data/changelog.md +5 -0
- data/lib/generators/unread/migration/migration_generator.rb +23 -0
- data/lib/generators/unread/migration/templates/migration.rb +16 -0
- data/lib/unread_mongoid/base.rb +39 -0
- data/lib/unread_mongoid/read_mark.rb +21 -0
- data/lib/unread_mongoid/readable.rb +117 -0
- data/lib/unread_mongoid/reader.rb +15 -0
- data/lib/unread_mongoid/scopes.rb +44 -0
- data/lib/unread_mongoid/version.rb +3 -0
- data/lib/unread_mongoid.rb +7 -0
- data/test/mongoid.yml +6 -0
- data/test/test_helper.rb +31 -0
- data/test/unread_test.rb +176 -0
- data/unread_mongoid.gemspec +23 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0de8df9d08d0700507b4e45252f7dfa78328f19f
|
4
|
+
data.tar.gz: 028872b091dedaf7bad1c803f38cad621d2ef681
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 38fc2a6c00adb96aa4972014bd3b1fbb94189c64f12a348ccf042674f26f752f8503a474462149796f38bb3926374d4ce1560c3d1ee82b3f868db728eca6f918
|
7
|
+
data.tar.gz: 3a0b7e197a0d142869ae2ccce5412a23de724f26bd9eb0d4ab83dec630994978087ceb2b385c6e90758b42704f35154aef73a8ad33772a7e47ecef5db60a5ad4
|
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010,2013 Georg Ledermann
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
Unread-Mongoid
|
2
|
+
======
|
3
|
+
|
4
|
+
Ruby gem to manage read/unread status of Mongoid objects.
|
5
|
+
|
6
|
+
## Credit
|
7
|
+
First and foremost this is a fork of [Unread](https://github.com/ledermann/unread) by [Georg Ledermann](http://www.georg-ledermann.de). If you don't have to use Mongo for this I highly reccoment you use his gem, this is a task much better suited to a relational db, I had to remove some features to make it work for Mongoid.
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
* Manages unread records for anything you want users to read (like messages, documents, comments etc.)
|
12
|
+
* Supports _mark as read_ to mark a **single** record as read
|
13
|
+
* Supports _mark all as read_ to mark **all** records as read in a single step
|
14
|
+
* Gives you a scope to get the unread or read records for a given user
|
15
|
+
* Needs only one additional collection
|
16
|
+
|
17
|
+
|
18
|
+
## Requirements
|
19
|
+
|
20
|
+
* Ruby 1.8.7 or 1.9.3 or 2.0.0
|
21
|
+
* Rails 3 (including 3.0, 3.1, 3.2) and Rails 4.
|
22
|
+
* Needs a timestamp field in your models (like created_at or updated_at) with a database index on it
|
23
|
+
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
Step 1: Add this to your Gemfile:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
gem 'unread-mongoid'
|
31
|
+
```
|
32
|
+
|
33
|
+
and run
|
34
|
+
|
35
|
+
```shell
|
36
|
+
bundle
|
37
|
+
```
|
38
|
+
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class User
|
44
|
+
include Mongoid::Document
|
45
|
+
include UnreadMongoid
|
46
|
+
|
47
|
+
acts_as_reader
|
48
|
+
end
|
49
|
+
|
50
|
+
class Message
|
51
|
+
include Mongoid::Document
|
52
|
+
include Mongoid::Timestamps
|
53
|
+
|
54
|
+
include UnreadMongoid
|
55
|
+
acts_as_readable :on => :created_at
|
56
|
+
end
|
57
|
+
|
58
|
+
message1 = Message.create!
|
59
|
+
message2 = Message.create!
|
60
|
+
|
61
|
+
## Get unread messages for a given user
|
62
|
+
Message.unread_by(current_user).entries
|
63
|
+
# => [ message1, message2 ]
|
64
|
+
|
65
|
+
message1.mark_as_read! :for => current_user
|
66
|
+
Message.unread_by(current_user).entries
|
67
|
+
# => [ message2 ]
|
68
|
+
|
69
|
+
Message.mark_as_read! :all, :for => current_user
|
70
|
+
Message.unread_by(current_user).entries
|
71
|
+
# => [ ]
|
72
|
+
|
73
|
+
# Optional: Cleaning up unneeded markers.
|
74
|
+
# Do this in a cron job once a day.
|
75
|
+
Message.cleanup_read_marks!
|
76
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
desc 'Default: run unit tests.'
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
desc 'Test the unread plugin.'
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'lib'
|
12
|
+
t.libs << 'test'
|
13
|
+
t.pattern = 'test/**/*_test.rb'
|
14
|
+
t.verbose = true
|
15
|
+
end
|
data/changelog.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module UnreadMongoid
|
5
|
+
class MigrationGenerator < Rails::Generators::Base
|
6
|
+
include Rails::Generators::Migration
|
7
|
+
|
8
|
+
desc "Generates migration for read_markers"
|
9
|
+
source_root File.expand_path('../templates', __FILE__)
|
10
|
+
|
11
|
+
def create_migration_file
|
12
|
+
migration_template 'migration.rb', 'db/migrate/unread_migration'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.next_migration_number(dirname)
|
16
|
+
if ActiveRecord::Base.timestamped_migrations
|
17
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
18
|
+
else
|
19
|
+
"%.3d" % (current_migration_number(dirname) + 1)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class UnreadMigration < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :read_marks, :force => true do |t|
|
4
|
+
t.integer :readable_id
|
5
|
+
t.integer :user_id, :null => false
|
6
|
+
t.string :readable_type, :null => false, :limit => 20
|
7
|
+
t.datetime :timestamp
|
8
|
+
end
|
9
|
+
|
10
|
+
add_index :read_marks, [:user_id, :readable_type, :readable_id]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :read_marks
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module UnreadMongoid
|
2
|
+
def self.included(base)
|
3
|
+
base.extend Base
|
4
|
+
end
|
5
|
+
|
6
|
+
module Base
|
7
|
+
def acts_as_reader
|
8
|
+
ReadMark.belongs_to :user, :class_name => self.to_s
|
9
|
+
|
10
|
+
has_many :read_marks, :dependent => :delete_all, :foreign_key => 'user_id', :inverse_of => :user
|
11
|
+
|
12
|
+
after_create do |user|
|
13
|
+
# We assume that a new user should not be tackled by tons of old messages
|
14
|
+
# created BEFORE he signed up.
|
15
|
+
# Instead, the new user starts with zero unread messages
|
16
|
+
(ReadMark.readable_classes || []).each do |klass|
|
17
|
+
klass.mark_as_read! :all, :for => user
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include Reader::InstanceMethods
|
22
|
+
end
|
23
|
+
|
24
|
+
def acts_as_readable(options={})
|
25
|
+
class_attribute :readable_options
|
26
|
+
|
27
|
+
self.readable_options = options
|
28
|
+
|
29
|
+
has_many :read_marks, :as => :readable, :dependent => :delete_all
|
30
|
+
|
31
|
+
ReadMark.readable_classes ||= []
|
32
|
+
ReadMark.readable_classes << self unless ReadMark.readable_classes.include?(self)
|
33
|
+
|
34
|
+
include Readable::InstanceMethods
|
35
|
+
extend Readable::ClassMethods
|
36
|
+
extend Readable::Scopes
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class ReadMark
|
2
|
+
include Mongoid::Document
|
3
|
+
|
4
|
+
field :timestamp, type: Time
|
5
|
+
|
6
|
+
belongs_to :readable, :polymorphic => true
|
7
|
+
belongs_to :user
|
8
|
+
|
9
|
+
validates_presence_of :user_id, :readable_type
|
10
|
+
|
11
|
+
scope :global, -> { where(:readable_id => nil) }
|
12
|
+
scope :single, -> { ne(readable_id: nil) }
|
13
|
+
scope :older_than, -> (timestamp) { lt(timestamp: timestamp) }
|
14
|
+
|
15
|
+
# Returns the class defined by acts_as_reader
|
16
|
+
def self.reader_class
|
17
|
+
reflect_on_all_associations(:belongs_to).find { |assoc| assoc.name == :user }.try(:klass)
|
18
|
+
end
|
19
|
+
|
20
|
+
class_attribute :readable_classes
|
21
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module UnreadMongoid
|
2
|
+
module Readable
|
3
|
+
module ClassMethods
|
4
|
+
def mark_as_read!(target, options)
|
5
|
+
user = options[:for]
|
6
|
+
assert_reader(user)
|
7
|
+
|
8
|
+
if target == :all
|
9
|
+
reset_read_marks_for_user(user)
|
10
|
+
elsif target.is_a?(Array)
|
11
|
+
mark_array_as_read(target, user)
|
12
|
+
else
|
13
|
+
raise ArgumentError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def mark_array_as_read(array, user)
|
18
|
+
array.each do |obj|
|
19
|
+
raise ArgumentError unless obj.is_a?(self)
|
20
|
+
|
21
|
+
rm = obj.read_marks.where(user_id: user._id).first || obj.read_marks.build(user_id: user._id)
|
22
|
+
rm.timestamp = obj.send(readable_options[:on])
|
23
|
+
rm.save!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# A scope with all items accessable for the given user
|
28
|
+
# It's used in cleanup_read_marks! to support a filtered cleanup
|
29
|
+
# Should be overriden if a user doesn't have access to all items
|
30
|
+
# Default: User has access to all items and should read them all
|
31
|
+
#
|
32
|
+
# Example:
|
33
|
+
# def Message.read_scope(user)
|
34
|
+
# user.visible_messages
|
35
|
+
# end
|
36
|
+
def read_scope(user)
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def cleanup_read_marks!
|
41
|
+
assert_reader_class
|
42
|
+
|
43
|
+
ReadMark.reader_class.each do |user|
|
44
|
+
if oldest_timestamp = read_scope(user).unread_by(user).sort(readable_options[:on] => :asc).first.send(readable_options[:on])
|
45
|
+
# There are unread items, so update the global read_mark for this user to the oldest
|
46
|
+
# unread item and delete older read_marks
|
47
|
+
update_read_marks_for_user(user, oldest_timestamp)
|
48
|
+
else
|
49
|
+
# There is no unread item, so deletes all markers and move global timestamp
|
50
|
+
reset_read_marks_for_user(user)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_read_marks_for_user(user, timestamp)
|
56
|
+
# Delete markers OLDER than the given timestamp
|
57
|
+
user.read_marks.where(:readable_type => self.name).single.older_than(timestamp).delete_all
|
58
|
+
|
59
|
+
# Change the global timestamp for this user
|
60
|
+
rm = user.read_mark_global(self) || user.read_marks.build(:readable_type => self.name)
|
61
|
+
rm.timestamp = timestamp - 1.second
|
62
|
+
rm.save!
|
63
|
+
end
|
64
|
+
|
65
|
+
def reset_read_marks_for_all
|
66
|
+
ReadMark.delete_all :readable_type => self.name
|
67
|
+
ReadMark.reader_class.each do |user|
|
68
|
+
ReadMark.create!(user_id: user._id, readable_type: self.name, timestamp: Time.current.to_s(:db))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def reset_read_marks_for_user(user)
|
73
|
+
assert_reader(user)
|
74
|
+
|
75
|
+
ReadMark.delete_all :readable_type => self.name, :user_id => user._id
|
76
|
+
ReadMark.create! :readable_type => self.name, :user_id => user._id, :timestamp => Time.now
|
77
|
+
end
|
78
|
+
|
79
|
+
def assert_reader(user)
|
80
|
+
assert_reader_class
|
81
|
+
|
82
|
+
unless user.is_a?(ReadMark.reader_class)
|
83
|
+
raise ArgumentError, "Class #{user.class.name} is not registered by acts_as_reader!"
|
84
|
+
end
|
85
|
+
|
86
|
+
unless user._id
|
87
|
+
raise ArgumentError, "The given user has no id!"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def assert_reader_class
|
92
|
+
raise RuntimeError, 'There is no class using acts_as_reader!' unless ReadMark.reader_class
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module InstanceMethods
|
97
|
+
def unread?(user)
|
98
|
+
self.class.unread_by(user).and(_id: self._id).exists?
|
99
|
+
end
|
100
|
+
|
101
|
+
def mark_as_read!(options)
|
102
|
+
user = options[:for]
|
103
|
+
self.class.assert_reader(user)
|
104
|
+
|
105
|
+
if unread?(user)
|
106
|
+
rm = read_mark(user) || read_marks.build(:user_id => user._id)
|
107
|
+
rm.timestamp = self.send(readable_options[:on]).to_s(:db)
|
108
|
+
rm.save!
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def read_mark(user)
|
113
|
+
read_marks.where(:user_id => user._id).first
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module UnreadMongoid
|
2
|
+
module Reader
|
3
|
+
module InstanceMethods
|
4
|
+
def read_mark_global(klass)
|
5
|
+
instance_var_name = "@read_mark_global_#{klass.name.gsub('::','_')}"
|
6
|
+
if instance_variables.include?(instance_var_name.to_sym)
|
7
|
+
instance_variable_get(instance_var_name)
|
8
|
+
else # memoize
|
9
|
+
obj = self.read_marks.where(:readable_type => klass.name).global.first
|
10
|
+
instance_variable_set(instance_var_name, obj)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module UnreadMongoid
|
2
|
+
module Readable
|
3
|
+
module Scopes
|
4
|
+
# TODO rename some of these
|
5
|
+
|
6
|
+
def unread_by(user)
|
7
|
+
self.not_in(_id: read_ids(user))
|
8
|
+
end
|
9
|
+
|
10
|
+
def read_by(user)
|
11
|
+
self.in(_id: read_ids(user))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def read_marks_query(user)
|
16
|
+
assert_reader(user)
|
17
|
+
|
18
|
+
ReadMark.where(readable_type: self.name, user_id: user._id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def blanket_marks_query(user)
|
22
|
+
read_marks_query(user).and(readable_id: nil).sort(timestamp: :desc)
|
23
|
+
end
|
24
|
+
|
25
|
+
def read_ids(user)
|
26
|
+
specifically_marked_ids(user) + blanketed_ids(user)
|
27
|
+
end
|
28
|
+
|
29
|
+
def blanketed_ids(user)
|
30
|
+
blanket = blanket_marks_query(user).first
|
31
|
+
|
32
|
+
if blanket
|
33
|
+
self.lte(self.readable_options[:on] => blanket.timestamp).only(:_id).map(&:_id)
|
34
|
+
else
|
35
|
+
[]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def specifically_marked_ids(user)
|
40
|
+
read_marks_query(user).ne(readable_id: nil).only(:readable_id).map(&:readable_id)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/test/mongoid.yml
ADDED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'mongoid'
|
3
|
+
require 'timecop'
|
4
|
+
|
5
|
+
ENV['MONGOID_ENV'] = 'test'
|
6
|
+
Mongoid.load!(File.dirname(__FILE__) + '/mongoid.yml')
|
7
|
+
|
8
|
+
require 'unread_mongoid'
|
9
|
+
|
10
|
+
class Reader
|
11
|
+
include Mongoid::Document
|
12
|
+
include UnreadMongoid
|
13
|
+
|
14
|
+
acts_as_reader
|
15
|
+
|
16
|
+
field :name, type: String
|
17
|
+
end
|
18
|
+
|
19
|
+
class Email
|
20
|
+
include Mongoid::Document
|
21
|
+
include Mongoid::Timestamps
|
22
|
+
|
23
|
+
include UnreadMongoid
|
24
|
+
|
25
|
+
acts_as_readable :on => :updated_at
|
26
|
+
|
27
|
+
field :subject, type: String
|
28
|
+
field :content, type: String
|
29
|
+
end
|
30
|
+
|
31
|
+
puts "Testing with Mongoid #{Mongoid::VERSION}"
|
data/test/unread_test.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class UnreadTest < ActiveSupport::TestCase
|
4
|
+
def setup
|
5
|
+
@reader = Reader.create! :name => 'David'
|
6
|
+
@other_reader = Reader.create :name => 'Matz'
|
7
|
+
|
8
|
+
wait
|
9
|
+
@email1 = Email.create!
|
10
|
+
wait
|
11
|
+
@email2 = Email.create!
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
Reader.delete_all
|
16
|
+
Email.delete_all
|
17
|
+
ReadMark.delete_all
|
18
|
+
Timecop.return
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_schema_has_loaded_correctly
|
22
|
+
assert_equal [@email1, @email2], Email.all
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_readable_classes
|
26
|
+
assert_equal [ Email ], ReadMark.readable_classes
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_reader_class
|
30
|
+
assert_equal Reader, ReadMark.reader_class
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_scope
|
34
|
+
assert_equal [@email1, @email2], Email.unread_by(@reader)
|
35
|
+
assert_equal [@email1, @email2], Email.unread_by(@other_reader)
|
36
|
+
|
37
|
+
assert_equal 2, Email.unread_by(@reader).count
|
38
|
+
assert_equal 2, Email.unread_by(@other_reader).count
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_read_by
|
42
|
+
@email1.mark_as_read! :for => @reader
|
43
|
+
|
44
|
+
assert_equal [@email1], Email.read_by(@reader).entries
|
45
|
+
end
|
46
|
+
|
47
|
+
#def test_with_read_marks_for
|
48
|
+
#@email1.mark_as_read! :for => @reader
|
49
|
+
|
50
|
+
#emails = Email.with_read_marks_for(@reader).entries
|
51
|
+
|
52
|
+
#assert emails[0].read_mark_id.present?
|
53
|
+
#assert emails[1].read_mark_ids.nil?
|
54
|
+
|
55
|
+
#assert_equal false, emails[0].unread?(@reader)
|
56
|
+
#assert_equal true, emails[1].unread?(@reader)
|
57
|
+
#end
|
58
|
+
|
59
|
+
def test_scope_param_check
|
60
|
+
[ 42, nil, 'foo', :foo, {} ].each do |not_a_reader|
|
61
|
+
assert_raise(ArgumentError) { Email.unread_by(not_a_reader) }
|
62
|
+
assert_raise(ArgumentError) { Email.read_by(not_a_reader) }
|
63
|
+
end
|
64
|
+
|
65
|
+
# gonna keep this, but not really likely in mongoid
|
66
|
+
unsaved_reader = Reader.new
|
67
|
+
unsaved_reader._id = nil
|
68
|
+
|
69
|
+
assert_raise(ArgumentError) { Email.unread_by(unsaved_reader) }
|
70
|
+
assert_raise(ArgumentError) { Email.read_by(unsaved_reader) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_scope_after_reset
|
74
|
+
@email1.mark_as_read! :for => @reader
|
75
|
+
|
76
|
+
assert_equal [@email2], Email.unread_by(@reader)
|
77
|
+
assert_equal 1, Email.unread_by(@reader).count
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_unread_after_create
|
81
|
+
assert_equal true, @email1.unread?(@reader)
|
82
|
+
assert_equal true, @email1.unread?(@other_reader)
|
83
|
+
|
84
|
+
assert_raise(ArgumentError) {
|
85
|
+
@email1.unread?(42)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_unread_after_update
|
90
|
+
@email1.mark_as_read! :for => @reader
|
91
|
+
wait
|
92
|
+
@email1.update_attributes! :subject => 'changed'
|
93
|
+
|
94
|
+
assert_equal true, @email1.unread?(@reader)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_mark_as_read
|
98
|
+
@email1.mark_as_read!(:for => @reader)
|
99
|
+
|
100
|
+
assert_equal false, @email1.unread?(@reader)
|
101
|
+
assert_equal [@email2], Email.unread_by(@reader).entries
|
102
|
+
|
103
|
+
assert_equal true, @email1.unread?(@other_reader)
|
104
|
+
assert_equal [@email1, @email2], Email.unread_by(@other_reader).entries
|
105
|
+
|
106
|
+
assert_equal 1, @reader.read_marks.single.count
|
107
|
+
assert_equal @email1, @reader.read_marks.single.first.readable
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_mark_as_read_multiple
|
111
|
+
assert_equal true, @email1.unread?(@reader)
|
112
|
+
assert_equal true, @email2.unread?(@reader)
|
113
|
+
|
114
|
+
Email.mark_as_read! [ @email1, @email2 ], :for => @reader
|
115
|
+
|
116
|
+
assert_equal false, @email1.unread?(@reader)
|
117
|
+
assert_equal false, @email2.unread?(@reader)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_mark_as_read_with_marked_all
|
121
|
+
wait
|
122
|
+
|
123
|
+
Email.mark_as_read! :all, :for => @reader
|
124
|
+
@email1.mark_as_read! :for => @reader
|
125
|
+
|
126
|
+
assert_equal [], @reader.read_marks.single.entries
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_mark_as_read_twice
|
130
|
+
@email1.mark_as_read! :for => @reader
|
131
|
+
@email1.mark_as_read! :for => @reader
|
132
|
+
|
133
|
+
assert_equal 1, @reader.read_marks.single.count
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_mark_all_as_read
|
137
|
+
Email.mark_as_read! :all, :for => @reader
|
138
|
+
|
139
|
+
assert_equal Time.current, @reader.read_mark_global(Email).timestamp
|
140
|
+
assert_equal [], @reader.read_marks.single
|
141
|
+
assert_equal 0, ReadMark.single.count
|
142
|
+
assert_equal 2, ReadMark.global.count
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_cleanup_read_marks
|
146
|
+
assert_equal 0, @reader.read_marks.single.count
|
147
|
+
|
148
|
+
@email1.mark_as_read! :for => @reader
|
149
|
+
|
150
|
+
assert_equal [@email2], Email.unread_by(@reader).entries
|
151
|
+
assert_equal 1, @reader.read_marks.single.count
|
152
|
+
|
153
|
+
Email.cleanup_read_marks!
|
154
|
+
|
155
|
+
@reader.reload
|
156
|
+
assert_equal 0, @reader.read_marks.single.count
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_cleanup_read_marks_not_delete_from_other_readables
|
160
|
+
other_read_mark = @reader.read_marks.create! :readable_type => 'Foo', :readable_id => 42, :timestamp => 5.years.ago
|
161
|
+
Email.cleanup_read_marks!
|
162
|
+
assert_equal true, ReadMark.where(_id: other_read_mark._id).exists?
|
163
|
+
end
|
164
|
+
|
165
|
+
def test_reset_read_marks_for_all
|
166
|
+
Email.reset_read_marks_for_all
|
167
|
+
|
168
|
+
assert_equal 0, ReadMark.single.count
|
169
|
+
assert_equal 2, ReadMark.global.count
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
def wait
|
174
|
+
Timecop.freeze(1.minute.from_now.change(:usec => 0))
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "unread_mongoid/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "unread-mongoid"
|
7
|
+
s.version = UnreadMongoid::VERSION
|
8
|
+
s.authors = ["Hunter Haydel", "Georg Ledermann"]
|
9
|
+
s.email = ["haydh530@gmail.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Manages read/unread status of Mongoid objects}
|
12
|
+
s.description = %q{This gem creates a scope for unread objects and adds methods to mark objects as read }
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency 'mongoid', '~> 3.1.0'
|
20
|
+
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'timecop'
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unread-mongoid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Hunter Haydel
|
8
|
+
- Georg Ledermann
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mongoid
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 3.1.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ~>
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 3.1.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rake
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: timecop
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
description: 'This gem creates a scope for unread objects and adds methods to mark
|
57
|
+
objects as read '
|
58
|
+
email:
|
59
|
+
- haydh530@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- .gitignore
|
65
|
+
- Gemfile
|
66
|
+
- MIT-LICENSE
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- changelog.md
|
70
|
+
- lib/generators/unread/migration/migration_generator.rb
|
71
|
+
- lib/generators/unread/migration/templates/migration.rb
|
72
|
+
- lib/unread_mongoid.rb
|
73
|
+
- lib/unread_mongoid/base.rb
|
74
|
+
- lib/unread_mongoid/read_mark.rb
|
75
|
+
- lib/unread_mongoid/readable.rb
|
76
|
+
- lib/unread_mongoid/reader.rb
|
77
|
+
- lib/unread_mongoid/scopes.rb
|
78
|
+
- lib/unread_mongoid/version.rb
|
79
|
+
- test/mongoid.yml
|
80
|
+
- test/test_helper.rb
|
81
|
+
- test/unread_test.rb
|
82
|
+
- unread_mongoid.gemspec
|
83
|
+
homepage: ''
|
84
|
+
licenses: []
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.0.2
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: Manages read/unread status of Mongoid objects
|
106
|
+
test_files:
|
107
|
+
- test/mongoid.yml
|
108
|
+
- test/test_helper.rb
|
109
|
+
- test/unread_test.rb
|