unread 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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