unread 0.3.1 → 0.4.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/MIT-LICENSE +3 -1
- data/README.md +9 -2
- data/Rakefile +3 -12
- data/ci/Gemfile-rails-3-0 +4 -1
- data/ci/Gemfile-rails-3-1 +4 -1
- data/ci/Gemfile-rails-3-2 +4 -1
- data/ci/Gemfile-rails-4-0 +4 -1
- data/ci/Gemfile-rails-4-1 +9 -0
- data/lib/generators/unread/migration/migration_generator.rb +1 -1
- data/lib/unread/readable.rb +3 -1
- data/lib/unread/scopes.rb +1 -1
- data/lib/unread/version.rb +1 -1
- data/spec/base_spec.rb +38 -0
- data/spec/model/email.rb +4 -0
- data/spec/model/reader.rb +4 -0
- data/spec/read_mark_spec.rb +7 -0
- data/spec/readable_spec.rb +206 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/support/matchers/perform_queries.rb +22 -0
- data/spec/support/query_counter.rb +17 -0
- data/spec/support/timecop.rb +3 -0
- data/unread.gemspec +3 -0
- metadata +64 -12
- data/changelog.md +0 -75
- data/test/database.yml +0 -3
- data/test/schema.rb +0 -20
- data/test/test_helper.rb +0 -27
- data/test/unread_test.rb +0 -166
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 62f6d5dbe191941fe9f5b358318fefbf93ad0c20
|
4
|
+
data.tar.gz: 80e1d361fc919f39f33142283b2dfe8d1d9376a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8808316873efd20db90cb45b9bbf14e4bc6fb3336f69d3f2480bfe8feaee04a21c39a44672d28c892e6365ceb26440525c36283e27d6508baec32f8e2aa72246
|
7
|
+
data.tar.gz: a55db92bee6d0b5969cb244655137e08afc7438a8abedbcb65f92a44d05c77ce60dad18d677e64c745b3b831063e08a5076773d717773d30a1685f74d2735f1e
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,8 @@ Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast.
|
|
5
5
|
|
6
6
|
[](https://travis-ci.org/ledermann/unread)
|
7
7
|
[](https://codeclimate.com/github/ledermann/unread)
|
8
|
+
[](https://coveralls.io/r/ledermann/unread)
|
9
|
+
|
8
10
|
|
9
11
|
## Features
|
10
12
|
|
@@ -18,11 +20,16 @@ Ruby gem to manage read/unread status of ActiveRecord objects - and it's fast.
|
|
18
20
|
|
19
21
|
## Requirements
|
20
22
|
|
21
|
-
* Ruby 1.9.3 or
|
23
|
+
* Ruby 1.9.3 or newer
|
22
24
|
* Rails 3 (including 3.0, 3.1, 3.2) and Rails 4. For use with Rails 2.3 there is a branch named "rails2"
|
23
25
|
* Needs a timestamp field in your models (like created_at or updated_at) with a database index on it
|
24
26
|
|
25
27
|
|
28
|
+
## Changelog
|
29
|
+
|
30
|
+
https://github.com/ledermann/unread/releases
|
31
|
+
|
32
|
+
|
26
33
|
## Installation
|
27
34
|
|
28
35
|
Step 1: Add this to your Gemfile:
|
@@ -133,4 +140,4 @@ There are two other gems/plugins doing a similar job:
|
|
133
140
|
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.
|
134
141
|
|
135
142
|
|
136
|
-
Copyright (c) 2010-
|
143
|
+
Copyright (c) 2010-2014 [Georg Ledermann](http://www.georg-ledermann.de), released under the MIT license
|
data/Rakefile
CHANGED
@@ -1,15 +1,6 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
2
3
|
|
3
|
-
|
4
|
-
require 'rake/testtask'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
|
6
|
-
|
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
|
6
|
+
task :default => :spec
|
data/ci/Gemfile-rails-3-0
CHANGED
data/ci/Gemfile-rails-3-1
CHANGED
data/ci/Gemfile-rails-3-2
CHANGED
data/ci/Gemfile-rails-4-0
CHANGED
@@ -9,7 +9,7 @@ module Unread
|
|
9
9
|
source_root File.expand_path('../templates', __FILE__)
|
10
10
|
|
11
11
|
def create_migration_file
|
12
|
-
migration_template 'migration.rb', 'db/migrate/unread_migration'
|
12
|
+
migration_template 'migration.rb', 'db/migrate/unread_migration.rb'
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.next_migration_number(dirname)
|
data/lib/unread/readable.rb
CHANGED
@@ -2,6 +2,8 @@ module Unread
|
|
2
2
|
module Readable
|
3
3
|
module ClassMethods
|
4
4
|
def mark_as_read!(target, options)
|
5
|
+
raise ArgumentError unless options.is_a?(Hash)
|
6
|
+
|
5
7
|
user = options[:for]
|
6
8
|
assert_reader(user)
|
7
9
|
|
@@ -70,7 +72,7 @@ module Unread
|
|
70
72
|
ReadMark.transaction do
|
71
73
|
ReadMark.delete_all :readable_type => self.base_class.name
|
72
74
|
ReadMark.connection.execute <<-EOT
|
73
|
-
INSERT INTO
|
75
|
+
INSERT INTO #{ReadMark.table_name} (user_id, readable_type, timestamp)
|
74
76
|
SELECT #{ReadMark.reader_class.primary_key}, '#{self.base_class.name}', '#{Time.current.to_s(:db)}'
|
75
77
|
FROM #{ReadMark.reader_class.table_name}
|
76
78
|
EOT
|
data/lib/unread/scopes.rb
CHANGED
@@ -4,7 +4,7 @@ module Unread
|
|
4
4
|
def join_read_marks(user)
|
5
5
|
assert_reader(user)
|
6
6
|
|
7
|
-
joins "LEFT JOIN read_marks ON read_marks.readable_type = '#{base_class.name}'
|
7
|
+
joins "LEFT JOIN #{ReadMark.table_name} as read_marks ON read_marks.readable_type = '#{base_class.name}'
|
8
8
|
AND read_marks.readable_id = #{table_name}.#{primary_key}
|
9
9
|
AND read_marks.user_id = #{user.id}
|
10
10
|
AND read_marks.timestamp >= #{table_name}.#{readable_options[:on]}"
|
data/lib/unread/version.rb
CHANGED
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unread::Base do
|
4
|
+
before :each do
|
5
|
+
@email = Email.create!
|
6
|
+
wait
|
7
|
+
@reader = Reader.create! :name => 'John'
|
8
|
+
end
|
9
|
+
|
10
|
+
describe :acts_as_reader do
|
11
|
+
it "should create global read mark" do
|
12
|
+
expect(@reader.read_marks.count).to eq 1
|
13
|
+
expect(@reader.read_marks.global.count).to eq 1
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should define association for ReadMark" do
|
17
|
+
expect(@reader.read_marks.first.user).to eq(@reader)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should reset read_marks for created reader" do
|
21
|
+
expect(Email.unread_by(@reader)).to be_empty
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe :acts_as_readable do
|
26
|
+
it "should define association" do
|
27
|
+
expect(@email.read_marks.count).to eq 0
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should add class to ReadMark.readable_classes" do
|
31
|
+
expect(ReadMark.readable_classes).to eq [ Email ]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should use default options" do
|
35
|
+
expect(Email.readable_options).to eq({ :on => :updated_at })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/spec/model/email.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unread::Readable do
|
4
|
+
before :each do
|
5
|
+
@reader = Reader.create! :name => 'David'
|
6
|
+
@other_reader = Reader.create :name => 'Matz'
|
7
|
+
wait
|
8
|
+
@email1 = Email.create!
|
9
|
+
wait
|
10
|
+
@email2 = Email.create!
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :unread_by do
|
14
|
+
it "should return all objects" do
|
15
|
+
expect(Email.unread_by(@reader)).to eq [@email1, @email2]
|
16
|
+
expect(Email.unread_by(@other_reader)).to eq [@email1, @email2]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should return unread records" do
|
20
|
+
@email1.mark_as_read! :for => @reader
|
21
|
+
|
22
|
+
expect(Email.unread_by(@reader)).to eq [@email2]
|
23
|
+
expect(Email.unread_by(@reader).count).to eq 1
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not allow invalid parameter" do
|
27
|
+
[ 42, nil, 'foo', :foo, {} ].each do |not_a_reader|
|
28
|
+
expect {
|
29
|
+
Email.unread_by(not_a_reader)
|
30
|
+
}.to raise_error(ArgumentError)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not allow unsaved reader" do
|
35
|
+
unsaved_reader = Reader.new
|
36
|
+
|
37
|
+
expect {
|
38
|
+
Email.unread_by(unsaved_reader)
|
39
|
+
}.to raise_error(ArgumentError)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe :with_read_marks_for do
|
44
|
+
it "should return readables" do
|
45
|
+
expect(Email.with_read_marks_for(@reader).to_a).to eq([@email1, @email2])
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should be countable" do
|
49
|
+
expect(Email.with_read_marks_for(@reader).count(:messageid)).to eq(2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not allow invalid parameter" do
|
53
|
+
[ 42, nil, 'foo', :foo, {} ].each do |not_a_reader|
|
54
|
+
expect {
|
55
|
+
Email.with_read_marks_for(not_a_reader)
|
56
|
+
}.to raise_error(ArgumentError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not allow unsaved reader" do
|
61
|
+
unsaved_reader = Reader.new
|
62
|
+
|
63
|
+
expect {
|
64
|
+
Email.with_read_marks_for(unsaved_reader)
|
65
|
+
}.to raise_error(ArgumentError)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe :unread? do
|
70
|
+
it "should recognize unread object" do
|
71
|
+
expect(@email1.unread?(@reader)).to be_truthy
|
72
|
+
expect(@email1.unread?(@other_reader)).to be_truthy
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should handle updating object" do
|
76
|
+
@email1.mark_as_read! :for => @reader
|
77
|
+
wait
|
78
|
+
expect(@email1.unread?(@reader)).to be_falsey
|
79
|
+
|
80
|
+
@email1.update_attributes! :subject => 'changed'
|
81
|
+
expect(@email1.unread?(@reader)).to be_truthy
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should raise error for invalid argument" do
|
85
|
+
expect {
|
86
|
+
@email1.unread?(42)
|
87
|
+
}.to raise_error(ArgumentError)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should work with eager-loaded read marks" do
|
91
|
+
@email1.mark_as_read! :for => @reader
|
92
|
+
|
93
|
+
expect {
|
94
|
+
emails = Email.with_read_marks_for(@reader).to_a
|
95
|
+
|
96
|
+
expect(emails[0].unread?(@reader)).to be_falsey
|
97
|
+
expect(emails[1].unread?(@reader)).to be_truthy
|
98
|
+
}.to perform_queries(1)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe '#mark_as_read!' do
|
103
|
+
it "should mark a single object as read" do
|
104
|
+
@email1.mark_as_read! :for => @reader
|
105
|
+
|
106
|
+
expect(@email1.unread?(@reader)).to be_falsey
|
107
|
+
expect(Email.unread_by(@reader)).to eq [@email2]
|
108
|
+
|
109
|
+
expect(@email1.unread?(@other_reader)).to be_truthy
|
110
|
+
expect(Email.unread_by(@other_reader)).to eq [@email1, @email2]
|
111
|
+
|
112
|
+
expect(@reader.read_marks.single.count).to eq 1
|
113
|
+
expect(@reader.read_marks.single.first.readable).to eq @email1
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should be idempotent" do
|
117
|
+
@email1.mark_as_read! :for => @reader
|
118
|
+
@email1.mark_as_read! :for => @reader
|
119
|
+
|
120
|
+
expect(@reader.read_marks.single.count).to eq 1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '.mark_as_read!' do
|
125
|
+
it "should mark multi objects as read" do
|
126
|
+
expect(@email1.unread?(@reader)).to be_truthy
|
127
|
+
expect(@email2.unread?(@reader)).to be_truthy
|
128
|
+
|
129
|
+
Email.mark_as_read! [ @email1, @email2 ], :for => @reader
|
130
|
+
|
131
|
+
expect(@email1.unread?(@reader)).to be_falsey
|
132
|
+
expect(@email2.unread?(@reader)).to be_falsey
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should mark all objects as read" do
|
136
|
+
Email.mark_as_read! :all, :for => @reader
|
137
|
+
|
138
|
+
expect(@reader.read_mark_global(Email).timestamp).to eq Time.current
|
139
|
+
expect(@reader.read_marks.single).to eq []
|
140
|
+
expect(ReadMark.single.count).to eq 0
|
141
|
+
expect(ReadMark.global.count).to eq 2
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should mark all objects as read with existing read objects" do
|
145
|
+
wait
|
146
|
+
|
147
|
+
Email.mark_as_read! :all, :for => @reader
|
148
|
+
@email1.mark_as_read! :for => @reader
|
149
|
+
|
150
|
+
expect(@reader.read_marks.single).to eq []
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should not allow invalid arguments" do
|
154
|
+
expect {
|
155
|
+
Email.mark_as_read! :foo, :for => @reader
|
156
|
+
}.to raise_error(ArgumentError)
|
157
|
+
|
158
|
+
expect {
|
159
|
+
Email.mark_as_read! :foo, :bar
|
160
|
+
}.to raise_error(ArgumentError)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe :reset_read_marks_for_all do
|
165
|
+
it "should reset read marks" do
|
166
|
+
Email.reset_read_marks_for_all
|
167
|
+
|
168
|
+
expect(ReadMark.single.count).to eq 0
|
169
|
+
expect(ReadMark.global.count).to eq 2
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe :cleanup_read_marks! do
|
174
|
+
it "should delete all single read marks" do
|
175
|
+
expect(@reader.read_marks.single.count).to eq 0
|
176
|
+
|
177
|
+
@email1.mark_as_read! :for => @reader
|
178
|
+
|
179
|
+
expect(Email.unread_by(@reader)).to eq [@email2]
|
180
|
+
expect(@reader.read_marks.single.count).to eq 1
|
181
|
+
|
182
|
+
Email.cleanup_read_marks!
|
183
|
+
|
184
|
+
@reader.reload
|
185
|
+
expect(@reader.read_marks.single.count).to eq 0
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should reset if all objects are read" do
|
189
|
+
@email1.mark_as_read! :for => @reader
|
190
|
+
@email2.mark_as_read! :for => @reader
|
191
|
+
|
192
|
+
expect(@reader.read_marks.single.count).to eq 2
|
193
|
+
|
194
|
+
Email.cleanup_read_marks!
|
195
|
+
|
196
|
+
expect(@reader.read_marks.single.count).to eq 0
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should not delete read marks from other readables" do
|
200
|
+
other_read_mark = @reader.read_marks.create! :readable_type => 'Foo', :readable_id => 42, :timestamp => 5.years.ago
|
201
|
+
Email.cleanup_read_marks!
|
202
|
+
|
203
|
+
expect(ReadMark.exists?(other_read_mark.id)).to be_truthy
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
|
4
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
5
|
+
SimpleCov::Formatter::HTMLFormatter,
|
6
|
+
Coveralls::SimpleCov::Formatter
|
7
|
+
]
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter '/spec/'
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'active_record'
|
13
|
+
require 'timecop'
|
14
|
+
require 'unread'
|
15
|
+
|
16
|
+
require 'model/reader'
|
17
|
+
require 'model/email'
|
18
|
+
|
19
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
20
|
+
# in spec/support/ and its subdirectories.
|
21
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
|
22
|
+
|
23
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
24
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
25
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
26
|
+
# loaded once.
|
27
|
+
#
|
28
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
29
|
+
RSpec.configure do |config|
|
30
|
+
# Run specs in random order to surface order dependencies. If you find an
|
31
|
+
# order dependency and want to debug it, you can fix the order by providing
|
32
|
+
# the seed, which is printed after each run.
|
33
|
+
# --seed 1234
|
34
|
+
# config.order = 'random'
|
35
|
+
|
36
|
+
config.before :each do
|
37
|
+
clear_db
|
38
|
+
end
|
39
|
+
|
40
|
+
config.after :each do
|
41
|
+
Timecop.return
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if I18n.respond_to?(:enforce_available_locales=)
|
46
|
+
I18n.enforce_available_locales = false
|
47
|
+
end
|
48
|
+
|
49
|
+
def setup_db
|
50
|
+
puts "Testing with ActiveRecord #{ActiveRecord::VERSION::STRING}"
|
51
|
+
|
52
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
53
|
+
ActiveRecord::Migration.verbose = false
|
54
|
+
|
55
|
+
require File.expand_path('../../lib/generators/unread/migration/templates/migration.rb', __FILE__)
|
56
|
+
UnreadMigration.migrate(:up)
|
57
|
+
|
58
|
+
ActiveRecord::Schema.define(:version => 1) do
|
59
|
+
create_table :readers, :primary_key => 'number', :force => true do |t|
|
60
|
+
t.string :name
|
61
|
+
end
|
62
|
+
|
63
|
+
create_table :emails, :primary_key => 'messageid', :force => true do |t|
|
64
|
+
t.string :subject
|
65
|
+
t.text :content
|
66
|
+
t.datetime :created_at
|
67
|
+
t.datetime :updated_at
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def clear_db
|
73
|
+
Reader.delete_all
|
74
|
+
Email.delete_all
|
75
|
+
ReadMark.delete_all
|
76
|
+
end
|
77
|
+
|
78
|
+
setup_db
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec::Matchers.define :perform_queries do |expected|
|
2
|
+
match do |block|
|
3
|
+
query_count(&block) == expected
|
4
|
+
end
|
5
|
+
|
6
|
+
failure_message do |actual|
|
7
|
+
"Expected to run #{expected} queries, got #{@counter.query_count}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def query_count(&block)
|
11
|
+
@counter = ActiveRecord::QueryCounter.new
|
12
|
+
ActiveSupport::Notifications.subscribe('sql.active_record', @counter.to_proc)
|
13
|
+
yield
|
14
|
+
ActiveSupport::Notifications.unsubscribe(@counter.to_proc)
|
15
|
+
|
16
|
+
@counter.query_count
|
17
|
+
end
|
18
|
+
|
19
|
+
def supports_block_expectations?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class QueryCounter
|
3
|
+
attr_reader :query_count
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@query_count = 0
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_proc
|
10
|
+
lambda(&method(:callback))
|
11
|
+
end
|
12
|
+
|
13
|
+
def callback(name, start, finish, message_id, values)
|
14
|
+
@query_count += 1 unless %w(CACHE SCHEMA).include?(values[:name]) || values[:sql] =~ /^begin/i || values[:sql] =~ /^commit/i
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/unread.gemspec
CHANGED
@@ -23,4 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_development_dependency 'rake'
|
24
24
|
s.add_development_dependency 'timecop'
|
25
25
|
s.add_development_dependency 'sqlite3'
|
26
|
+
s.add_development_dependency 'rspec'
|
27
|
+
s.add_development_dependency 'simplecov'
|
28
|
+
s.add_development_dependency 'coveralls'
|
26
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unread
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Georg Ledermann
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -66,6 +66,48 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: coveralls
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
69
111
|
description: 'This gem creates a scope for unread objects and adds methods to mark
|
70
112
|
objects as read '
|
71
113
|
email:
|
@@ -80,11 +122,11 @@ files:
|
|
80
122
|
- MIT-LICENSE
|
81
123
|
- README.md
|
82
124
|
- Rakefile
|
83
|
-
- changelog.md
|
84
125
|
- ci/Gemfile-rails-3-0
|
85
126
|
- ci/Gemfile-rails-3-1
|
86
127
|
- ci/Gemfile-rails-3-2
|
87
128
|
- ci/Gemfile-rails-4-0
|
129
|
+
- ci/Gemfile-rails-4-1
|
88
130
|
- lib/generators/unread/migration/migration_generator.rb
|
89
131
|
- lib/generators/unread/migration/templates/migration.rb
|
90
132
|
- lib/unread.rb
|
@@ -94,10 +136,15 @@ files:
|
|
94
136
|
- lib/unread/reader.rb
|
95
137
|
- lib/unread/scopes.rb
|
96
138
|
- lib/unread/version.rb
|
97
|
-
-
|
98
|
-
-
|
99
|
-
-
|
100
|
-
-
|
139
|
+
- spec/base_spec.rb
|
140
|
+
- spec/model/email.rb
|
141
|
+
- spec/model/reader.rb
|
142
|
+
- spec/read_mark_spec.rb
|
143
|
+
- spec/readable_spec.rb
|
144
|
+
- spec/spec_helper.rb
|
145
|
+
- spec/support/matchers/perform_queries.rb
|
146
|
+
- spec/support/query_counter.rb
|
147
|
+
- spec/support/timecop.rb
|
101
148
|
- unread.gemspec
|
102
149
|
homepage: ''
|
103
150
|
licenses: []
|
@@ -118,12 +165,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
165
|
version: '0'
|
119
166
|
requirements: []
|
120
167
|
rubyforge_project: unread
|
121
|
-
rubygems_version: 2.
|
168
|
+
rubygems_version: 2.2.2
|
122
169
|
signing_key:
|
123
170
|
specification_version: 4
|
124
171
|
summary: Manages read/unread status of ActiveRecord objects
|
125
172
|
test_files:
|
126
|
-
-
|
127
|
-
-
|
128
|
-
-
|
129
|
-
-
|
173
|
+
- spec/base_spec.rb
|
174
|
+
- spec/model/email.rb
|
175
|
+
- spec/model/reader.rb
|
176
|
+
- spec/read_mark_spec.rb
|
177
|
+
- spec/readable_spec.rb
|
178
|
+
- spec/spec_helper.rb
|
179
|
+
- spec/support/matchers/perform_queries.rb
|
180
|
+
- spec/support/query_counter.rb
|
181
|
+
- spec/support/timecop.rb
|
data/changelog.md
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
|
3
|
-
## 0.3.1 - 2013-12-04
|
4
|
-
|
5
|
-
* Use Time.current instead of Time.now to avoid time zone trouble. Issue #18 (thanks to @henrythe9th)
|
6
|
-
* Handle primary keys other than "id". Issue #29 (thanks to @bcavileer)
|
7
|
-
|
8
|
-
|
9
|
-
## 0.3.0 - 2013-03-17
|
10
|
-
|
11
|
-
* Support for Rails 4 (beta1)
|
12
|
-
|
13
|
-
|
14
|
-
## 0.2.0 - 2013-02-18
|
15
|
-
|
16
|
-
* Support for Rails 2 dropped
|
17
|
-
* Refactoring
|
18
|
-
* Added migration generator
|
19
|
-
|
20
|
-
|
21
|
-
## 0.1.2 - 2013-01-27
|
22
|
-
|
23
|
-
* Scopes: Improved parameter check
|
24
|
-
|
25
|
-
|
26
|
-
## 0.1.1 - 2012-05-01
|
27
|
-
|
28
|
-
* Fixed handling namespaced classes. Closes #10 (thanks to @stanislaw)
|
29
|
-
|
30
|
-
|
31
|
-
## 0.1.0 - 2012-04-21
|
32
|
-
|
33
|
-
* Added scope "with_read_marks_for"
|
34
|
-
* Fixed #7: Added attr_accessible to all ReadMark attributes (thanks to @negative)
|
35
|
-
|
36
|
-
|
37
|
-
## 0.0.7 - 2012-02-29
|
38
|
-
|
39
|
-
* Cleanup files
|
40
|
-
* acts_as_reader: Using inverse_of (available since Rails 2.3.6)
|
41
|
-
|
42
|
-
|
43
|
-
## 0.0.6 - 2011-11-11
|
44
|
-
|
45
|
-
* Fixed #5: Gemspec dependency fix (thanks to @bricker88)
|
46
|
-
* Fixed #6: Removed hard coded dependency on a class named "User" (thanks to @mixandgo)
|
47
|
-
* Some cleanup
|
48
|
-
|
49
|
-
|
50
|
-
## 0.0.5 - 2011-09-09
|
51
|
-
|
52
|
-
* Fixed class loading issue in development environment
|
53
|
-
|
54
|
-
|
55
|
-
## 0.0.4 - 2011-08-31
|
56
|
-
|
57
|
-
* Ignore multiple calls of acts_as_*
|
58
|
-
* Improved error messages
|
59
|
-
* Tested with Rails 3.1
|
60
|
-
|
61
|
-
|
62
|
-
## 0.0.3 - 2011-08-01
|
63
|
-
|
64
|
-
* Fixed gemspec by adding development dependencies
|
65
|
-
* Testing with Travis CI
|
66
|
-
|
67
|
-
|
68
|
-
## 0.0.2 - 2011-06-23
|
69
|
-
|
70
|
-
* Fixed scoping for ActiveRecord 2.x
|
71
|
-
|
72
|
-
|
73
|
-
## 0.0.1 - 2011-06-23
|
74
|
-
|
75
|
-
* Released as Gem
|
data/test/database.yml
DELETED
data/test/schema.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
ActiveRecord::Schema.define(:version => 0) do
|
2
|
-
create_table :readers, :primary_key => 'number', :force => true do |t|
|
3
|
-
t.string :name
|
4
|
-
end
|
5
|
-
|
6
|
-
create_table :emails, :primary_key => 'messageid', :force => true do |t|
|
7
|
-
t.string :subject
|
8
|
-
t.text :content
|
9
|
-
t.datetime :created_at
|
10
|
-
t.datetime :updated_at
|
11
|
-
end
|
12
|
-
|
13
|
-
create_table :read_marks, :force => true do |t|
|
14
|
-
t.integer :readable_id
|
15
|
-
t.integer :user_id, :null => false
|
16
|
-
t.string :readable_type, :null => false
|
17
|
-
t.datetime :timestamp
|
18
|
-
end
|
19
|
-
add_index :read_marks, [:user_id, :readable_type, :readable_id]
|
20
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'test/unit'
|
2
|
-
require 'active_support'
|
3
|
-
require 'active_support/test_case'
|
4
|
-
require 'active_record'
|
5
|
-
require 'timecop'
|
6
|
-
|
7
|
-
configs = YAML.load_file(File.dirname(__FILE__) + '/database.yml')
|
8
|
-
ActiveRecord::Base.configurations = configs
|
9
|
-
|
10
|
-
ActiveRecord::Base.establish_connection('sqlite')
|
11
|
-
ActiveRecord::Migration.verbose = false
|
12
|
-
load(File.dirname(__FILE__) + "/schema.rb")
|
13
|
-
|
14
|
-
require 'unread'
|
15
|
-
|
16
|
-
class Reader < ActiveRecord::Base
|
17
|
-
self.primary_key = 'number'
|
18
|
-
acts_as_reader
|
19
|
-
end
|
20
|
-
|
21
|
-
class Email < ActiveRecord::Base
|
22
|
-
self.primary_key = 'messageid'
|
23
|
-
|
24
|
-
acts_as_readable :on => :updated_at
|
25
|
-
end
|
26
|
-
|
27
|
-
puts "Testing with ActiveRecord #{ActiveRecord::VERSION::STRING}"
|
data/test/unread_test.rb
DELETED
@@ -1,166 +0,0 @@
|
|
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
|
-
wait
|
8
|
-
@email1 = Email.create!
|
9
|
-
wait
|
10
|
-
@email2 = Email.create!
|
11
|
-
end
|
12
|
-
|
13
|
-
def teardown
|
14
|
-
Reader.delete_all
|
15
|
-
Email.delete_all
|
16
|
-
ReadMark.delete_all
|
17
|
-
Timecop.return
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_schema_has_loaded_correctly
|
21
|
-
assert_equal [@email1, @email2], Email.all
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_readable_classes
|
25
|
-
assert_equal [ Email ], ReadMark.readable_classes
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_reader_class
|
29
|
-
assert_equal Reader, ReadMark.reader_class
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_scope
|
33
|
-
assert_equal [@email1, @email2], Email.unread_by(@reader)
|
34
|
-
assert_equal [@email1, @email2], Email.unread_by(@other_reader)
|
35
|
-
|
36
|
-
assert_equal 2, Email.unread_by(@reader).count
|
37
|
-
assert_equal 2, Email.unread_by(@other_reader).count
|
38
|
-
end
|
39
|
-
|
40
|
-
def test_with_read_marks_for
|
41
|
-
@email1.mark_as_read! :for => @reader
|
42
|
-
|
43
|
-
emails = Email.with_read_marks_for(@reader).to_a
|
44
|
-
|
45
|
-
assert emails[0].read_mark_id.present?
|
46
|
-
assert emails[1].read_mark_id.nil?
|
47
|
-
|
48
|
-
assert_equal false, emails[0].unread?(@reader)
|
49
|
-
assert_equal true, emails[1].unread?(@reader)
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_scope_param_check
|
53
|
-
[ 42, nil, 'foo', :foo, {} ].each do |not_a_reader|
|
54
|
-
assert_raise(ArgumentError) { Email.unread_by(not_a_reader)}
|
55
|
-
assert_raise(ArgumentError) { Email.with_read_marks_for(not_a_reader)}
|
56
|
-
end
|
57
|
-
|
58
|
-
unsaved_reader = Reader.new
|
59
|
-
assert_raise(ArgumentError) { Email.unread_by(unsaved_reader)}
|
60
|
-
assert_raise(ArgumentError) { Email.with_read_marks_for(unsaved_reader)}
|
61
|
-
end
|
62
|
-
|
63
|
-
def test_scope_after_reset
|
64
|
-
@email1.mark_as_read! :for => @reader
|
65
|
-
|
66
|
-
assert_equal [@email2], Email.unread_by(@reader)
|
67
|
-
assert_equal 1, Email.unread_by(@reader).count
|
68
|
-
end
|
69
|
-
|
70
|
-
def test_unread_after_create
|
71
|
-
assert_equal true, @email1.unread?(@reader)
|
72
|
-
assert_equal true, @email1.unread?(@other_reader)
|
73
|
-
|
74
|
-
assert_raise(ArgumentError) {
|
75
|
-
@email1.unread?(42)
|
76
|
-
}
|
77
|
-
end
|
78
|
-
|
79
|
-
def test_unread_after_update
|
80
|
-
@email1.mark_as_read! :for => @reader
|
81
|
-
wait
|
82
|
-
@email1.update_attributes! :subject => 'changed'
|
83
|
-
|
84
|
-
assert_equal true, @email1.unread?(@reader)
|
85
|
-
end
|
86
|
-
|
87
|
-
def test_mark_as_read
|
88
|
-
@email1.mark_as_read! :for => @reader
|
89
|
-
|
90
|
-
assert_equal false, @email1.unread?(@reader)
|
91
|
-
assert_equal [@email2], Email.unread_by(@reader)
|
92
|
-
|
93
|
-
assert_equal true, @email1.unread?(@other_reader)
|
94
|
-
assert_equal [@email1, @email2], Email.unread_by(@other_reader)
|
95
|
-
|
96
|
-
assert_equal 1, @reader.read_marks.single.count
|
97
|
-
assert_equal @email1, @reader.read_marks.single.first.readable
|
98
|
-
end
|
99
|
-
|
100
|
-
def test_mark_as_read_multiple
|
101
|
-
assert_equal true, @email1.unread?(@reader)
|
102
|
-
assert_equal true, @email2.unread?(@reader)
|
103
|
-
|
104
|
-
Email.mark_as_read! [ @email1, @email2 ], :for => @reader
|
105
|
-
|
106
|
-
assert_equal false, @email1.unread?(@reader)
|
107
|
-
assert_equal false, @email2.unread?(@reader)
|
108
|
-
end
|
109
|
-
|
110
|
-
def test_mark_as_read_with_marked_all
|
111
|
-
wait
|
112
|
-
|
113
|
-
Email.mark_as_read! :all, :for => @reader
|
114
|
-
@email1.mark_as_read! :for => @reader
|
115
|
-
|
116
|
-
assert_equal [], @reader.read_marks.single
|
117
|
-
end
|
118
|
-
|
119
|
-
def test_mark_as_read_twice
|
120
|
-
@email1.mark_as_read! :for => @reader
|
121
|
-
@email1.mark_as_read! :for => @reader
|
122
|
-
|
123
|
-
assert_equal 1, @reader.read_marks.single.count
|
124
|
-
end
|
125
|
-
|
126
|
-
def test_mark_all_as_read
|
127
|
-
Email.mark_as_read! :all, :for => @reader
|
128
|
-
|
129
|
-
assert_equal Time.current, @reader.read_mark_global(Email).timestamp
|
130
|
-
assert_equal [], @reader.read_marks.single
|
131
|
-
assert_equal 0, ReadMark.single.count
|
132
|
-
assert_equal 2, ReadMark.global.count
|
133
|
-
end
|
134
|
-
|
135
|
-
def test_cleanup_read_marks
|
136
|
-
assert_equal 0, @reader.read_marks.single.count
|
137
|
-
|
138
|
-
@email1.mark_as_read! :for => @reader
|
139
|
-
|
140
|
-
assert_equal [@email2], Email.unread_by(@reader)
|
141
|
-
assert_equal 1, @reader.read_marks.single.count
|
142
|
-
|
143
|
-
Email.cleanup_read_marks!
|
144
|
-
|
145
|
-
@reader.reload
|
146
|
-
assert_equal 0, @reader.read_marks.single.count
|
147
|
-
end
|
148
|
-
|
149
|
-
def test_cleanup_read_marks_not_delete_from_other_readables
|
150
|
-
other_read_mark = @reader.read_marks.create! :readable_type => 'Foo', :readable_id => 42, :timestamp => 5.years.ago
|
151
|
-
Email.cleanup_read_marks!
|
152
|
-
assert_equal true, !!ReadMark.exists?(other_read_mark.id) # Rails4 does not return true, but count instead.
|
153
|
-
end
|
154
|
-
|
155
|
-
def test_reset_read_marks_for_all
|
156
|
-
Email.reset_read_marks_for_all
|
157
|
-
|
158
|
-
assert_equal 0, ReadMark.single.count
|
159
|
-
assert_equal 2, ReadMark.global.count
|
160
|
-
end
|
161
|
-
|
162
|
-
private
|
163
|
-
def wait
|
164
|
-
Timecop.freeze(1.minute.from_now.change(:usec => 0))
|
165
|
-
end
|
166
|
-
end
|