unread-mongoid 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d8ad50fe8b295cea62aca80e1fc08fa149caeb5d
4
- data.tar.gz: 6612008cbf7da7497baa6591ccbfc551e6baa9d0
3
+ metadata.gz: 1cd035eab063883dddcbb6f3e99398b4dc5346d7
4
+ data.tar.gz: e18f414642a5cd38eea6fd8a55cbc2fdf2c0606e
5
5
  SHA512:
6
- metadata.gz: 64d9d12c0183503a7a5a441941cd6309b45104ee5dc7d3c68972d20ae53a67bba740ee28bc546b8783d53f13d99bbd0a7a5ef6fc6b8bd8e0e7d6da62f6b88435
7
- data.tar.gz: 2afd84b2942c203d23951e9d363c880d1ef9eefd6092845d469abc1837338197ee640a98bfea9995da8e45401a88455050684db273e8943ad53c047ce6f37834
6
+ metadata.gz: e0c977d07ba535824213afc3ac6b511120ee2cf7700aedd4f56f6596be3a2cc5c79022d835568ea29090fd364720fb1de507b33ef5208cba821968e5e6be6467
7
+ data.tar.gz: 9887a638a534ce31d9a6261f93f59b58e85f537d31ab14615f6c9434fa41a7c8402fd120e86fab5a7d38dbef0b026a897b67eebed724ce7b2720c018eca19bbb
data/README.md CHANGED
@@ -4,39 +4,16 @@ Unread-Mongoid
4
4
  Ruby gem to manage read/unread status of Mongoid objects.
5
5
 
6
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 reccomend you use his gem, this is a task much better suited to a relational db.
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 are using a relational DB, make sure to check it out.
8
8
 
9
9
  ## Features
10
10
 
11
11
  * Manages unread records for anything you want users to read (like messages, documents, comments etc.)
12
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
13
+ * Supports _mark all as read_ to mark **all** records (loops through creating a readmark for each)
14
14
  * Gives you a scope to get the unread or read records for a given user
15
15
  * Needs only one additional collection
16
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
17
  ## Usage
41
18
 
42
19
  ```ruby
@@ -52,7 +29,7 @@ class Message
52
29
  include Mongoid::Timestamps
53
30
 
54
31
  include UnreadMongoid
55
- acts_as_readable :on => :created_at
32
+ acts_as_readable
56
33
  end
57
34
 
58
35
  message1 = Message.create!
@@ -69,8 +46,4 @@ Message.unread_by(current_user).entries
69
46
  Message.mark_as_read! :all, :for => current_user
70
47
  Message.unread_by(current_user).entries
71
48
  # => [ ]
72
-
73
- # Optional: Cleaning up unneeded markers.
74
- # Do this in a cron job once a day.
75
- Message.cleanup_read_marks!
76
49
  ```
@@ -5,31 +5,17 @@ module UnreadMongoid
5
5
 
6
6
  module Base
7
7
  def acts_as_reader
8
- ReadMark.belongs_to :user, :class_name => self.to_s
8
+ has_many :read_marks, as: :reader, dependent: :destroy
9
9
 
10
- has_many :read_marks, :dependent => :destroy, :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
10
+ include Reader
22
11
  end
23
12
 
24
- def acts_as_readable(options={})
25
- class_attribute :readable_options
13
+ def acts_as_readable
14
+ has_many :read_marks, as: :readable, dependent: :destroy
26
15
 
27
- self.readable_options = options
28
-
29
- has_many :read_marks, :as => :readable, :dependent => :destroy
30
-
31
- ReadMark.readable_classes ||= []
32
- ReadMark.readable_classes << self unless ReadMark.readable_classes.include?(self)
16
+ before_save do |readable|
17
+ readable.mark_as_unread!
18
+ end
33
19
 
34
20
  include Readable::InstanceMethods
35
21
  extend Readable::ClassMethods
@@ -1,21 +1,9 @@
1
1
  class ReadMark
2
2
  include Mongoid::Document
3
3
 
4
- field :timestamp, type: Time
4
+ belongs_to :readable, polymorphic: true, index: true
5
+ belongs_to :reader, polymorphic: true, index: true
5
6
 
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
7
+ validates_presence_of :reader_id, :reader_type, :readable_id, :readable_type
8
+ validates :reader_id, uniqueness: { scope: [:reader_type, :readable_id, :readable_type] }
21
9
  end
@@ -2,115 +2,39 @@ module UnreadMongoid
2
2
  module Readable
3
3
  module ClassMethods
4
4
  def mark_as_read!(target, options)
5
- user = options[:for]
6
- assert_reader(user)
5
+ reader = options[:for]
6
+ UnreadMongoid::Reader.assert_reader(reader)
7
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
8
+ readables_to_mark = if(target == :all)
9
+ self.unread_by(reader)
10
+ else
11
+ target
12
+ end
42
13
 
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
14
+ self.unread_by(reader).each do |readable|
15
+ raise ArgumentError unless readable.is_a? self
64
16
 
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))
17
+ readable.mark_as_read! :for => reader
69
18
  end
70
19
  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
20
  end
95
21
 
96
22
  module InstanceMethods
97
- def unread?(user)
98
- self.class.unread_by(user).and(_id: self._id).exists?
23
+ def unread?(reader)
24
+ UnreadMongoid::Reader.assert_reader(reader)
25
+
26
+ ReadMark.where(reader: reader, readable: self).empty?
99
27
  end
100
28
 
101
29
  def mark_as_read!(options)
102
- user = options[:for]
103
- self.class.assert_reader(user)
30
+ reader = options[:for]
31
+ UnreadMongoid::Reader.assert_reader(reader)
104
32
 
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
33
+ ReadMark.create(reader: reader, readable: self)
110
34
  end
111
35
 
112
- def read_mark(user)
113
- read_marks.where(:user_id => user._id).first
36
+ def mark_as_unread!
37
+ self.read_marks.destroy_all
114
38
  end
115
39
  end
116
40
  end
@@ -1,14 +1,8 @@
1
1
  module UnreadMongoid
2
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
3
+ def self.assert_reader(obj)
4
+ unless obj.kind_of?(UnreadMongoid::Reader)
5
+ raise ArgumentError, "Class #{obj.class.name} is not registered by acts_as_reader!"
12
6
  end
13
7
  end
14
8
  end
@@ -1,45 +1,25 @@
1
1
  module UnreadMongoid
2
2
  module Readable
3
3
  module Scopes
4
- # TODO rename some of these
4
+ def unread_by(reader)
5
+ UnreadMongoid::Reader.assert_reader(reader)
5
6
 
6
- def unread_by(user)
7
- self.not_in(_id: read_ids(user))
7
+ self.not_in(id: read_ids(reader))
8
8
  end
9
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)
10
+ def read_by(reader)
11
+ UnreadMongoid::Reader.assert_reader(reader)
17
12
 
18
- ReadMark.where(readable_type: self.name, user_id: user._id)
13
+ self.in(id: read_ids(reader))
19
14
  end
20
15
 
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).select do |read_mark|
41
- read_mark.timestamp.to_i >= read_mark.readable.send(self.readable_options[:on]).to_i
42
- end.map(&:readable_id)
16
+ private
17
+ def read_ids(reader)
18
+ ReadMark.where(
19
+ reader_id: reader.id,
20
+ reader_type: reader.class.name,
21
+ readable_type: self.name
22
+ ).only(:readable_id).map(&:readable_id)
43
23
  end
44
24
  end
45
25
  end
@@ -1,3 +1,3 @@
1
1
  module UnreadMongoid
2
- VERSION = '0.0.5'
2
+ VERSION = '0.1.0'
3
3
  end
data/test/test_helper.rb CHANGED
@@ -7,7 +7,7 @@ Mongoid.load!(File.dirname(__FILE__) + '/mongoid.yml')
7
7
 
8
8
  require 'unread_mongoid'
9
9
 
10
- class Reader
10
+ class User
11
11
  include Mongoid::Document
12
12
  include UnreadMongoid
13
13
 
@@ -22,7 +22,7 @@ class Email
22
22
 
23
23
  include UnreadMongoid
24
24
 
25
- acts_as_readable :on => :updated_at
25
+ acts_as_readable
26
26
 
27
27
  field :subject, type: String
28
28
  field :content, type: String
data/test/unread_test.rb CHANGED
@@ -2,8 +2,8 @@ require 'test_helper'
2
2
 
3
3
  class UnreadTest < ActiveSupport::TestCase
4
4
  def setup
5
- @reader = Reader.create! :name => 'David'
6
- @other_reader = Reader.create :name => 'Matz'
5
+ @reader = User.create! :name => 'David'
6
+ @other_reader = User.create :name => 'Matz'
7
7
 
8
8
  wait
9
9
  @email1 = Email.create!
@@ -12,24 +12,12 @@ class UnreadTest < ActiveSupport::TestCase
12
12
  end
13
13
 
14
14
  def teardown
15
- Reader.delete_all
15
+ User.delete_all
16
16
  Email.delete_all
17
17
  ReadMark.delete_all
18
18
  Timecop.return
19
19
  end
20
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
21
  def test_scope
34
22
  assert_equal [@email1, @email2], Email.unread_by(@reader)
35
23
  assert_equal [@email1, @email2], Email.unread_by(@other_reader)
@@ -44,36 +32,17 @@ class UnreadTest < ActiveSupport::TestCase
44
32
  assert_equal [@email1], Email.read_by(@reader).entries
45
33
  end
46
34
 
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
35
  def test_scope_param_check
60
36
  [ 42, nil, 'foo', :foo, {} ].each do |not_a_reader|
61
37
  assert_raise(ArgumentError) { Email.unread_by(not_a_reader) }
62
38
  assert_raise(ArgumentError) { Email.read_by(not_a_reader) }
63
39
  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
40
  end
72
41
 
73
42
  def test_scope_after_reset
74
43
  @email1.mark_as_read! :for => @reader
75
44
 
76
- assert_equal [@email2], Email.unread_by(@reader)
45
+ assert_equal [@email2], Email.unread_by(@reader).entries
77
46
  assert_equal 1, Email.unread_by(@reader).count
78
47
  end
79
48
 
@@ -102,9 +71,6 @@ class UnreadTest < ActiveSupport::TestCase
102
71
 
103
72
  assert_equal true, @email1.unread?(@other_reader)
104
73
  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
74
  end
109
75
 
110
76
  def test_mark_as_read_multiple
@@ -117,64 +83,68 @@ class UnreadTest < ActiveSupport::TestCase
117
83
  assert_equal false, @email2.unread?(@reader)
118
84
  end
119
85
 
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
86
  def test_mark_as_read_twice
130
87
  @email1.mark_as_read! :for => @reader
131
88
  @email1.mark_as_read! :for => @reader
132
89
 
133
- assert_equal 1, @reader.read_marks.single.count
90
+ assert_equal 1, ReadMark.where(reader: @reader).count
134
91
  end
135
92
 
136
93
  def test_mark_all_as_read
137
94
  Email.mark_as_read! :all, :for => @reader
138
95
 
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
96
+ assert_equal [], Email.unread_by(@reader)
143
97
  end
144
98
 
145
- def test_cleanup_read_marks
146
- assert_equal 0, @reader.read_marks.single.count
99
+ def test_destroys_readmarks_when_readable_is_destroyed
100
+ @email1.mark_as_read! for: @reader
101
+
102
+ assert_equal 1, ReadMark.count
147
103
 
148
- @email1.mark_as_read! :for => @reader
104
+ @email1.destroy
149
105
 
150
- assert_equal [@email2], Email.unread_by(@reader).entries
151
- assert_equal 1, @reader.read_marks.single.count
106
+ assert_equal 0, ReadMark.count
107
+ end
152
108
 
153
- Email.cleanup_read_marks!
109
+ def test_destroys_readmarks_when_reader_is_destroyed
110
+ @email1.mark_as_read! for: @reader
111
+
112
+ assert_equal 1, ReadMark.count
113
+
114
+ @reader.destroy
154
115
 
155
- @reader.reload
156
- assert_equal 0, @reader.read_marks.single.count
116
+ assert_equal 0, ReadMark.count
157
117
  end
158
118
 
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?
119
+ def test_does_not_destroy_readable_when_readmark_is_destroyed
120
+ email_id = @email1.id
121
+
122
+ @email1.mark_as_read! for: @reader
123
+
124
+ ReadMark.destroy_all
125
+
126
+ assert_equal 0, ReadMark.count
127
+ assert_equal 1, Email.where(id: email_id).count
163
128
  end
164
129
 
165
- def test_reset_read_marks_for_all
166
- Email.reset_read_marks_for_all
130
+ def test_does_not_destroy_reader_when_readmark_is_destroyed
131
+ reader_id = @reader
132
+
133
+ @email1.mark_as_read! for: @reader
134
+
135
+ ReadMark.destroy_all
167
136
 
168
- assert_equal 0, ReadMark.single.count
169
- assert_equal 2, ReadMark.global.count
137
+ assert_equal 0, ReadMark.count
138
+ assert_equal 1, User.where(id: reader_id).count
170
139
  end
171
140
 
172
- def test_destroys_readmarks_when_readable_is_destroyed
173
- count = ReadMark.count
141
+ def test_mark_as_unread_sets_readable_back_to_unread
174
142
  @email1.mark_as_read! for: @reader
175
- assert_equal count + 1, ReadMark.count
176
- @email1.destroy
177
- assert_equal count, ReadMark.count
143
+ assert_equal false, @email1.unread?(@reader)
144
+
145
+ @email1.mark_as_unread!
146
+
147
+ assert_equal true, @email1.unread?(@reader)
178
148
  end
179
149
  private
180
150
  def wait
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unread-mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hunter Haydel
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-21 00:00:00.000000000 Z
12
+ date: 2014-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongoid