unread 0.1.1 → 0.1.2
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 +82 -72
- data/changelog.md +4 -0
- data/ci/Gemfile.rails-2.3.x +1 -1
- data/ci/Gemfile.rails-3.0.x +1 -1
- data/ci/Gemfile.rails-3.1.x +1 -1
- data/ci/Gemfile.rails-3.2.x +1 -1
- data/lib/unread/acts_as_readable.rb +11 -8
- data/lib/unread/version.rb +1 -1
- data/test/test_helper.rb +5 -8
- data/test/unread_test.rb +11 -4
- data/unread.gemspec +1 -1
- metadata +87 -96
data/README.md
CHANGED
@@ -18,82 +18,87 @@ Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast.
|
|
18
18
|
|
19
19
|
## Requirements
|
20
20
|
|
21
|
-
* Ruby 1.8.7 or 1.9.
|
21
|
+
* Ruby 1.8.7 or 1.9.3
|
22
22
|
* Rails >= 2.3.6 (including 3.0, 3.1, 3.2)
|
23
23
|
* Tested with SQLite and MySQL
|
24
|
-
* Needs a timestamp field in your models (
|
24
|
+
* Needs a timestamp field in your models (like created_at or updated_at) with a database index on it
|
25
25
|
|
26
26
|
|
27
27
|
## Installation
|
28
28
|
|
29
29
|
Step 1: Add this to your Gemfile:
|
30
|
-
|
30
|
+
|
31
31
|
gem 'unread'
|
32
|
-
|
32
|
+
|
33
33
|
and run
|
34
|
-
|
34
|
+
|
35
35
|
bundle
|
36
|
-
|
37
|
-
|
36
|
+
|
37
|
+
|
38
38
|
Step 2: Add this migration:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
add_index :read_marks, [:user_id, :readable_type, :readable_id]
|
49
|
-
end
|
50
|
-
|
51
|
-
def self.down
|
52
|
-
drop_table :read_marks
|
53
|
-
end
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
class CreateReadMarks < ActiveRecord::Migration
|
42
|
+
def self.up
|
43
|
+
create_table :read_marks, :force => true do |t|
|
44
|
+
t.integer :readable_id
|
45
|
+
t.integer :user_id, :null => false
|
46
|
+
t.string :readable_type, :null => false, :limit => 20
|
47
|
+
t.datetime :timestamp
|
54
48
|
end
|
55
49
|
|
50
|
+
add_index :read_marks, [:user_id, :readable_type, :readable_id]
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.down
|
54
|
+
drop_table :read_marks
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
56
59
|
and run the migration:
|
57
|
-
|
60
|
+
|
58
61
|
rake db:migrate
|
59
62
|
|
60
63
|
|
61
64
|
## Usage
|
62
65
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
```ruby
|
67
|
+
class User < ActiveRecord::Base
|
68
|
+
acts_as_reader
|
69
|
+
end
|
70
|
+
|
71
|
+
class Message < ActiveRecord::Base
|
72
|
+
acts_as_readable :on => :created_at
|
73
|
+
end
|
74
|
+
|
75
|
+
message1 = Message.create!
|
76
|
+
message2 = Message.create!
|
77
|
+
|
78
|
+
## Get unread messages for a given user
|
79
|
+
Message.unread_by(current_user)
|
80
|
+
# => [ message1, message2 ]
|
81
|
+
|
82
|
+
message1.mark_as_read! :for => current_user
|
83
|
+
Message.unread_by(current_user)
|
84
|
+
# => [ message2 ]
|
70
85
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
messages[1].unread?(current_user)
|
88
|
-
# => true
|
89
|
-
|
90
|
-
Message.mark_as_read! :all, :for => current_user
|
91
|
-
Message.unread_by(current_user)
|
92
|
-
# => [ ]
|
93
|
-
|
94
|
-
# Optional: Cleaning up unneeded markers
|
95
|
-
# Do this in a cron job once a day.
|
96
|
-
Message.cleanup_read_marks!
|
86
|
+
## Get all messages including the read status for a given user
|
87
|
+
messages = Message.with_read_marks_for(current_user)
|
88
|
+
# => [ message1, message2 ]
|
89
|
+
messages[0].unread?(current_user)
|
90
|
+
# => false
|
91
|
+
messages[1].unread?(current_user)
|
92
|
+
# => true
|
93
|
+
|
94
|
+
Message.mark_as_read! :all, :for => current_user
|
95
|
+
Message.unread_by(current_user)
|
96
|
+
# => [ ]
|
97
|
+
|
98
|
+
# Optional: Cleaning up unneeded markers.
|
99
|
+
# Do this in a cron job once a day.
|
100
|
+
Message.cleanup_read_marks!
|
101
|
+
```
|
97
102
|
|
98
103
|
|
99
104
|
## How does it work?
|
@@ -109,21 +114,26 @@ It will be ensured that the list of read items will not grow up too much:
|
|
109
114
|
|
110
115
|
Overall, this gem can be used for large data. Please have a look at the generated SQL queries, here is an example:
|
111
116
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
117
|
+
```ruby
|
118
|
+
# Assuming we have a user who has marked all messages as read on 2010-10-20 08:50
|
119
|
+
current_user = User.find(42)
|
120
|
+
|
121
|
+
# Get the unread messages for this user
|
122
|
+
Message.unread_by(current_user)
|
123
|
+
```
|
124
|
+
|
125
|
+
Generated query:
|
126
|
+
|
127
|
+
```sql
|
128
|
+
SELECT messages.*
|
129
|
+
FROM messages
|
130
|
+
LEFT JOIN read_marks ON read_marks.readable_type = 'Message'
|
131
|
+
AND read_marks.readable_id = messages.id
|
132
|
+
AND read_marks.user_id = 42
|
133
|
+
AND read_marks.timestamp >= messages.created_at
|
134
|
+
WHERE read_marks.id IS NULL
|
135
|
+
AND messages.created_at > '2010-10-20 08:50:00'
|
136
|
+
```
|
127
137
|
|
128
138
|
Hint: You should add a database index on `messages.created_at`.
|
129
139
|
|
@@ -138,4 +148,4 @@ There are two other gems/plugins doing a similar job:
|
|
138
148
|
Unfortunately, both of them have a lack of performance, because they calculate the unread records doing a `find(:all)`, which should be avoided for a large amount of records. This gem is based on a timestamp algorithm and therefore it's very fast.
|
139
149
|
|
140
150
|
|
141
|
-
Copyright (c) 2010
|
151
|
+
Copyright (c) 2010-2013 [Georg Ledermann](http://www.georg-ledermann.de), released under the MIT license
|
data/changelog.md
CHANGED
data/ci/Gemfile.rails-2.3.x
CHANGED
data/ci/Gemfile.rails-3.0.x
CHANGED
data/ci/Gemfile.rails-3.1.x
CHANGED
data/ci/Gemfile.rails-3.2.x
CHANGED
@@ -106,7 +106,7 @@ module Unread
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def cleanup_read_marks!
|
109
|
-
|
109
|
+
assert_reader_class
|
110
110
|
|
111
111
|
ReadMark.reader_class.find_each do |user|
|
112
112
|
ReadMark.transaction do
|
@@ -126,7 +126,7 @@ module Unread
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def reset_read_marks!(user = :all)
|
129
|
-
|
129
|
+
assert_reader_class
|
130
130
|
|
131
131
|
ReadMark.transaction do
|
132
132
|
if user == :all
|
@@ -145,12 +145,15 @@ module Unread
|
|
145
145
|
true
|
146
146
|
end
|
147
147
|
|
148
|
-
def assert_reader(user
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
148
|
+
def assert_reader(user)
|
149
|
+
assert_reader_class
|
150
|
+
|
151
|
+
raise ArgumentError, "Class #{user.class.name} is not registered by acts_as_reader!" unless user.is_a?(ReadMark.reader_class)
|
152
|
+
raise ArgumentError, "The given user has no id!" unless user.id
|
153
|
+
end
|
154
|
+
|
155
|
+
def assert_reader_class
|
156
|
+
unless ReadMark.reader_class
|
154
157
|
raise RuntimeError, 'There is no class using acts_as_reader!'
|
155
158
|
end
|
156
159
|
end
|
data/lib/unread/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -12,15 +12,12 @@ ActiveRecord::Base.establish_connection(db_name)
|
|
12
12
|
ActiveRecord::Migration.verbose = false
|
13
13
|
load(File.dirname(__FILE__) + "/schema.rb")
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
15
|
+
class Reader < ActiveRecord::Base
|
16
|
+
acts_as_reader
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
end
|
19
|
+
class Email < ActiveRecord::Base
|
20
|
+
acts_as_readable :on => :updated_at
|
23
21
|
end
|
24
|
-
include App
|
25
22
|
|
26
23
|
puts "Testing with ActiveRecord #{ActiveRecord::VERSION::STRING}"
|
data/test/unread_test.rb
CHANGED
@@ -34,10 +34,6 @@ class UnreadTest < ActiveSupport::TestCase
|
|
34
34
|
|
35
35
|
assert_equal 2, Email.unread_by(@reader).count
|
36
36
|
assert_equal 2, Email.unread_by(@other_reader).count
|
37
|
-
|
38
|
-
assert_raise(ArgumentError) {
|
39
|
-
Email.unread_by(42)
|
40
|
-
}
|
41
37
|
end
|
42
38
|
|
43
39
|
def test_with_read_marks_for
|
@@ -52,6 +48,17 @@ class UnreadTest < ActiveSupport::TestCase
|
|
52
48
|
assert_equal true, emails[1].unread?(@reader)
|
53
49
|
end
|
54
50
|
|
51
|
+
def test_scope_param_check
|
52
|
+
[ 42, nil, 'foo', :foo, {} ].each do |not_a_reader|
|
53
|
+
assert_raise(ArgumentError) { Email.unread_by(not_a_reader)}
|
54
|
+
assert_raise(ArgumentError) { Email.with_read_marks_for(not_a_reader)}
|
55
|
+
end
|
56
|
+
|
57
|
+
unsaved_reader = Reader.new
|
58
|
+
assert_raise(ArgumentError) { Email.unread_by(unsaved_reader)}
|
59
|
+
assert_raise(ArgumentError) { Email.with_read_marks_for(unsaved_reader)}
|
60
|
+
end
|
61
|
+
|
55
62
|
def test_scope_after_reset
|
56
63
|
@email1.mark_as_read! :for => @reader
|
57
64
|
|
data/unread.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |s|
|
|
21
21
|
s.add_dependency 'activerecord', '>= 2.3.6'
|
22
22
|
|
23
23
|
s.add_development_dependency 'rake'
|
24
|
-
s.add_development_dependency 'mocha'
|
24
|
+
s.add_development_dependency 'mocha', "~> 0.12.8"
|
25
25
|
s.add_development_dependency 'sqlite3'
|
26
26
|
s.add_development_dependency 'mysql2'
|
27
27
|
end
|
metadata
CHANGED
@@ -1,104 +1,104 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: unread
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
hash: 25
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
6
|
-
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
- 1
|
10
|
-
version: 0.1.1
|
5
|
+
version: 0.1.2
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Georg Ledermann
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
21
|
-
name: activerecord
|
12
|
+
date: 2013-01-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
prerelease: false
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 15
|
29
|
-
segments:
|
30
|
-
- 2
|
31
|
-
- 3
|
32
|
-
- 6
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
33
20
|
version: 2.3.6
|
21
|
+
none: false
|
34
22
|
type: :runtime
|
35
|
-
|
36
|
-
|
23
|
+
name: activerecord
|
24
|
+
requirement: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ! '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 2.3.6
|
29
|
+
none: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
prerelease: false
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
none: false
|
38
|
+
type: :development
|
37
39
|
name: rake
|
40
|
+
requirement: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
none: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
38
47
|
prerelease: false
|
39
|
-
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.12.8
|
40
53
|
none: false
|
41
|
-
requirements:
|
42
|
-
- - ">="
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
hash: 3
|
45
|
-
segments:
|
46
|
-
- 0
|
47
|
-
version: "0"
|
48
54
|
type: :development
|
49
|
-
version_requirements: *id002
|
50
|
-
- !ruby/object:Gem::Dependency
|
51
55
|
name: mocha
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.12.8
|
61
|
+
none: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
52
63
|
prerelease: false
|
53
|
-
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
54
69
|
none: false
|
55
|
-
requirements:
|
56
|
-
- - ">="
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
hash: 3
|
59
|
-
segments:
|
60
|
-
- 0
|
61
|
-
version: "0"
|
62
70
|
type: :development
|
63
|
-
version_requirements: *id003
|
64
|
-
- !ruby/object:Gem::Dependency
|
65
71
|
name: sqlite3
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
none: false
|
78
|
+
- !ruby/object:Gem::Dependency
|
66
79
|
prerelease: false
|
67
|
-
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
68
85
|
none: false
|
69
|
-
requirements:
|
70
|
-
- - ">="
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
hash: 3
|
73
|
-
segments:
|
74
|
-
- 0
|
75
|
-
version: "0"
|
76
86
|
type: :development
|
77
|
-
version_requirements: *id004
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
87
|
name: mysql2
|
80
|
-
|
81
|
-
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
82
93
|
none: false
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
hash: 3
|
87
|
-
segments:
|
88
|
-
- 0
|
89
|
-
version: "0"
|
90
|
-
type: :development
|
91
|
-
version_requirements: *id005
|
92
|
-
description: "This gem creates a scope for unread objects and adds methods to mark objects as read "
|
93
|
-
email:
|
94
|
+
description: ! 'This gem creates a scope for unread objects and adds methods to mark
|
95
|
+
objects as read '
|
96
|
+
email:
|
94
97
|
- mail@georg-ledermann.de
|
95
98
|
executables: []
|
96
|
-
|
97
99
|
extensions: []
|
98
|
-
|
99
100
|
extra_rdoc_files: []
|
100
|
-
|
101
|
-
files:
|
101
|
+
files:
|
102
102
|
- .gitignore
|
103
103
|
- .travis.yml
|
104
104
|
- Gemfile
|
@@ -119,40 +119,31 @@ files:
|
|
119
119
|
- test/test_helper.rb
|
120
120
|
- test/unread_test.rb
|
121
121
|
- unread.gemspec
|
122
|
-
homepage:
|
122
|
+
homepage: ''
|
123
123
|
licenses: []
|
124
|
-
|
125
124
|
post_install_message:
|
126
125
|
rdoc_options: []
|
127
|
-
|
128
|
-
require_paths:
|
126
|
+
require_paths:
|
129
127
|
- lib
|
130
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ! '>='
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
131
133
|
none: false
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
- 0
|
138
|
-
version: "0"
|
139
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ! '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
140
139
|
none: false
|
141
|
-
requirements:
|
142
|
-
- - ">="
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
hash: 3
|
145
|
-
segments:
|
146
|
-
- 0
|
147
|
-
version: "0"
|
148
140
|
requirements: []
|
149
|
-
|
150
141
|
rubyforge_project: unread
|
151
|
-
rubygems_version: 1.8.
|
142
|
+
rubygems_version: 1.8.25
|
152
143
|
signing_key:
|
153
144
|
specification_version: 3
|
154
145
|
summary: Manages read/unread status of ActiveRecord objects
|
155
|
-
test_files:
|
146
|
+
test_files:
|
156
147
|
- test/database.yml
|
157
148
|
- test/schema.rb
|
158
149
|
- test/test_helper.rb
|