unread 0.0.7 → 0.1.0

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.
data/README.md CHANGED
@@ -71,6 +71,7 @@ Step 2: Add this migration:
71
71
  message1 = Message.create!
72
72
  message2 = Message.create!
73
73
 
74
+ ## Get unread messages for a given user
74
75
  Message.unread_by(current_user)
75
76
  # => [ message1, message2 ]
76
77
 
@@ -78,6 +79,14 @@ Step 2: Add this migration:
78
79
  Message.unread_by(current_user)
79
80
  # => [ message2 ]
80
81
 
82
+ ## Get all messages including the read status for a given user
83
+ messages = Message.with_read_marks_for(current_user)
84
+ # => [ message1, message2 ]
85
+ messages[0].unread?(current_user)
86
+ # => false
87
+ messages[1].unread?(current_user)
88
+ # => true
89
+
81
90
  Message.mark_as_read! :all, :for => current_user
82
91
  Message.unread_by(current_user)
83
92
  # => [ ]
data/changelog.md CHANGED
@@ -1,4 +1,9 @@
1
- 0.0.7 - not yet released
1
+ 0.1.0 - 2012/04/21
2
+
3
+ * Added scope "with_read_marks_for"
4
+ * Fixed #7: Added attr_accessible to all ReadMark attributes (thanks to @negative)
5
+
6
+ 0.0.7 - 2012/02/29
2
7
 
3
8
  * Cleanup files
4
9
  * acts_as_reader: Using inverse_of (available since Rails 2.3.6)
@@ -1,5 +1,6 @@
1
1
  class ReadMark < ActiveRecord::Base
2
2
  belongs_to :readable, :polymorphic => true
3
+ attr_accessible :readable_id, :user_id, :readable_type, :timestamp
3
4
 
4
5
  validates_presence_of :user_id, :readable_type
5
6
 
@@ -14,6 +14,8 @@ module Unread
14
14
  klass.mark_as_read! :all, :for => user
15
15
  end
16
16
  end
17
+
18
+ include ReaderInstanceMethods
17
19
  end
18
20
 
19
21
  def acts_as_readable(options={})
@@ -41,18 +43,27 @@ module Unread
41
43
  AND read_marks.user_id = #{user.id}
42
44
  AND read_marks.timestamp >= #{self.table_name}.#{readable_options[:on]}",
43
45
  :conditions => 'read_marks.id IS NULL' }
44
- if last = read_timestamp(user)
45
- result[:conditions] += " AND #{self.table_name}.#{readable_options[:on]} > '#{last.to_s(:db)}'"
46
+ if global_time_stamp = user.read_mark_global(self).try(:timestamp)
47
+ result[:conditions] += " AND #{self.table_name}.#{readable_options[:on]} > '#{global_time_stamp.to_s(:db)}'"
46
48
  end
47
49
  result
48
50
  }
49
51
 
50
- extend ClassMethods
51
- include InstanceMethods
52
+ send scope_method, :with_read_marks_for, lambda { |user|
53
+ assert_reader(user)
54
+
55
+ { :select => "#{self.table_name}.*, read_marks.id AS read_mark_id",
56
+ :joins => "LEFT JOIN read_marks ON read_marks.readable_type = '#{self.base_class.name}'
57
+ AND read_marks.readable_id = #{self.table_name}.id
58
+ AND read_marks.user_id = #{user.id}
59
+ AND read_marks.timestamp >= #{self.table_name}.#{readable_options[:on]}" }
60
+ }
61
+ extend ReadableClassMethods
62
+ include ReadableInstanceMethods
52
63
  end
53
64
  end
54
65
 
55
- module ClassMethods
66
+ module ReadableClassMethods
56
67
  def mark_as_read!(target, options)
57
68
  raise ArgumentError unless target == :all || target.is_a?(Array)
58
69
 
@@ -63,8 +74,6 @@ module Unread
63
74
  reset_read_marks!(user)
64
75
  elsif target.is_a?(Array)
65
76
  ReadMark.transaction do
66
- last = read_timestamp(user)
67
-
68
77
  target.each do |obj|
69
78
  raise ArgumentError unless obj.is_a?(self)
70
79
 
@@ -77,17 +86,8 @@ module Unread
77
86
  end
78
87
  end
79
88
 
80
- def read_mark(user)
81
- assert_reader(user)
82
- user.read_marks.readable_type(self.base_class.name).global.first
83
- end
84
-
85
- def read_timestamp(user)
86
- read_mark(user).try(:timestamp)
87
- end
88
-
89
89
  def set_read_mark(user, timestamp)
90
- rm = read_mark(user) || user.read_marks.build(:readable_type => self.base_class.name)
90
+ rm = user.read_mark_global(self) || user.read_marks.build(:readable_type => self.base_class.name)
91
91
  rm.timestamp = timestamp
92
92
  rm.save!
93
93
  end
@@ -156,9 +156,20 @@ module Unread
156
156
  end
157
157
  end
158
158
 
159
- module InstanceMethods
159
+ module ReadableInstanceMethods
160
160
  def unread?(user)
161
- self.class.unread_by(user).exists?(self)
161
+ if self.respond_to?(:read_mark_id)
162
+ # For use with scope "with_read_marks_for"
163
+ return false if self.read_mark_id
164
+
165
+ if global_timestamp = user.read_mark_global(self.class).try(:timestamp)
166
+ self.send(readable_options[:on]) > global_timestamp
167
+ else
168
+ true
169
+ end
170
+ else
171
+ self.class.unread_by(user).exists?(self)
172
+ end
162
173
  end
163
174
 
164
175
  def mark_as_read!(options)
@@ -167,7 +178,7 @@ module Unread
167
178
 
168
179
  ReadMark.transaction do
169
180
  if unread?(user)
170
- rm = read_mark(user) || read_marks.build(:user => user)
181
+ rm = read_mark(user) || read_marks.build(:user_id => user.id)
171
182
  rm.timestamp = self.send(readable_options[:on])
172
183
  rm.save!
173
184
  end
@@ -178,6 +189,16 @@ module Unread
178
189
  read_marks.user(user).first
179
190
  end
180
191
  end
192
+
193
+ module ReaderInstanceMethods
194
+ def read_mark_global(klass)
195
+ instance_var_name = "@read_mark_global_#{klass.name}"
196
+ instance_variable_get(instance_var_name) || begin # memoize
197
+ obj = self.read_marks.readable_type(klass.base_class.name).global.first
198
+ instance_variable_set(instance_var_name, obj)
199
+ end
200
+ end
201
+ end
181
202
  end
182
203
 
183
204
  ActiveRecord::Base.send :include, Unread
@@ -1,3 +1,3 @@
1
1
  module Unread
2
- VERSION = '0.0.7'
2
+ VERSION = '0.1.0'
3
3
  end
data/test/unread_test.rb CHANGED
@@ -40,6 +40,18 @@ class UnreadTest < ActiveSupport::TestCase
40
40
  }
41
41
  end
42
42
 
43
+ def test_with_read_marks_for
44
+ @email1.mark_as_read! :for => @reader
45
+
46
+ emails = Email.with_read_marks_for(@reader).all
47
+
48
+ assert emails[0].read_mark_id.present?
49
+ assert emails[1].read_mark_id.nil?
50
+
51
+ assert_equal false, emails[0].unread?(@reader)
52
+ assert_equal true, emails[1].unread?(@reader)
53
+ end
54
+
43
55
  def test_scope_after_reset
44
56
  @email1.mark_as_read! :for => @reader
45
57
 
@@ -105,7 +117,7 @@ class UnreadTest < ActiveSupport::TestCase
105
117
 
106
118
  def test_mark_all_as_read
107
119
  Email.mark_as_read! :all, :for => @reader
108
- assert_equal Time.now.to_s, Email.read_mark(@reader).timestamp.to_s
120
+ assert_equal Time.now.to_s, @reader.read_mark_global(Email).try(:timestamp).to_s
109
121
 
110
122
  assert_equal [], @reader.read_marks.single
111
123
  assert_equal 0, ReadMark.single.count
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: unread
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 7
10
- version: 0.0.7
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Georg Ledermann
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-02-29 00:00:00 Z
18
+ date: 2012-04-21 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: activerecord
@@ -148,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
148
  requirements: []
149
149
 
150
150
  rubyforge_project: unread
151
- rubygems_version: 1.8.17
151
+ rubygems_version: 1.8.23
152
152
  signing_key:
153
153
  specification_version: 3
154
154
  summary: Manages read/unread status of ActiveRecord objects