simple-feed 2.1.0 → 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of simple-feed might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.envrc +1 -0
- data/.github/workflows/ruby.yml +1 -0
- data/.gitignore +1 -1
- data/.travis.yml +3 -13
- data/CHANGELOG.md +93 -0
- data/README.adoc +168 -51
- data/Rakefile +1 -1
- data/codecov.yml +28 -0
- data/examples/shared/provider_example.rb +43 -25
- data/lib/simplefeed.rb +20 -15
- data/lib/simplefeed/activity/single_user.rb +2 -2
- data/lib/simplefeed/dsl/formatter.rb +58 -23
- data/lib/simplefeed/event.rb +47 -10
- data/lib/simplefeed/feed.rb +20 -9
- data/lib/simplefeed/providers/base/provider.rb +1 -1
- data/lib/simplefeed/providers/hash/provider.rb +26 -16
- data/lib/simplefeed/providers/key.rb +57 -42
- data/lib/simplefeed/providers/proxy.rb +20 -7
- data/lib/simplefeed/providers/redis/provider.rb +6 -6
- data/lib/simplefeed/response.rb +1 -1
- data/lib/simplefeed/version.rb +1 -1
- data/man/running-example-redis-debug.png +0 -0
- data/man/running-example.png +0 -0
- data/man/sf-color-dump.png +0 -0
- data/simple-feed.gemspec +5 -1
- metadata +51 -8
- data/lib/simplefeed/key/template.rb +0 -48
- data/lib/simplefeed/key/type.rb +0 -28
data/lib/simplefeed/feed.rb
CHANGED
@@ -2,11 +2,17 @@
|
|
2
2
|
|
3
3
|
require_relative 'providers'
|
4
4
|
require_relative 'activity/base'
|
5
|
-
|
5
|
+
require_relative 'providers/key'
|
6
6
|
|
7
7
|
module SimpleFeed
|
8
8
|
class Feed
|
9
|
-
attr_accessor :per_page,
|
9
|
+
attr_accessor :per_page,
|
10
|
+
:max_size,
|
11
|
+
:batch_size,
|
12
|
+
:namespace,
|
13
|
+
:data_key_transformer,
|
14
|
+
:meta_key_transformer
|
15
|
+
|
10
16
|
attr_reader :name
|
11
17
|
|
12
18
|
SimpleFeed::Providers.define_provider_methods(self) do |feed, method, opts, &block|
|
@@ -14,19 +20,21 @@ module SimpleFeed
|
|
14
20
|
end
|
15
21
|
|
16
22
|
def initialize(name)
|
17
|
-
@name
|
18
|
-
@name
|
23
|
+
@name = name
|
24
|
+
@name = name.underscore.to_sym unless name.is_a?(Symbol)
|
19
25
|
# set the defaults if not passed in
|
20
|
-
@meta
|
21
|
-
@namespace
|
26
|
+
@meta = {}
|
27
|
+
@namespace = nil
|
22
28
|
@per_page ||= 50
|
23
29
|
@max_size ||= 1000
|
24
30
|
@batch_size ||= 10
|
31
|
+
@meta_key_transformer = nil
|
32
|
+
@data_key_transformer = nil
|
25
33
|
@proxy = nil
|
26
34
|
end
|
27
35
|
|
28
36
|
def provider=(definition)
|
29
|
-
@proxy
|
37
|
+
@proxy = Providers::Proxy.from(definition)
|
30
38
|
@proxy.feed = self
|
31
39
|
@proxy
|
32
40
|
end
|
@@ -63,12 +71,15 @@ module SimpleFeed
|
|
63
71
|
end
|
64
72
|
|
65
73
|
def key(user_id)
|
66
|
-
SimpleFeed::Providers::Key.new(user_id,
|
74
|
+
SimpleFeed::Providers::Key.new(user_id,
|
75
|
+
namespace: namespace,
|
76
|
+
data_key_transformer: data_key_transformer,
|
77
|
+
meta_key_transformer: meta_key_transformer)
|
67
78
|
end
|
68
79
|
|
69
80
|
def eql?(other)
|
70
81
|
other.class == self.class &&
|
71
|
-
%i(per_page max_size name).all? { |m| send(m).equal?(other.send(m)) } &&
|
82
|
+
%i(per_page max_size name namespace data_key_transformer meta_key_transformer).all? { |m| send(m).equal?(other.send(m)) } &&
|
72
83
|
provider.provider.class == other.provider.provider.class
|
73
84
|
end
|
74
85
|
|
@@ -58,7 +58,7 @@ module SimpleFeed
|
|
58
58
|
def with_response_batched(user_ids, external_response = nil)
|
59
59
|
with_response(external_response) do |response|
|
60
60
|
batch(user_ids) do |key|
|
61
|
-
response.for(key.
|
61
|
+
response.for(key.consumer) { yield(key, response) }
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -46,7 +46,7 @@ module SimpleFeed
|
|
46
46
|
def delete_if(user_ids:)
|
47
47
|
with_response_batched(user_ids) do |key|
|
48
48
|
activity(key).map do |event|
|
49
|
-
if yield(event, key.
|
49
|
+
if yield(event, key.consumer)
|
50
50
|
__delete(key, event)
|
51
51
|
event
|
52
52
|
end
|
@@ -80,7 +80,7 @@ module SimpleFeed
|
|
80
80
|
def fetch(user_ids:, since: nil, reset_last_read: false)
|
81
81
|
response = with_response_batched(user_ids) do |key|
|
82
82
|
if since == :unread
|
83
|
-
activity(key).reject { |event| event.at <
|
83
|
+
activity(key).reject { |event| event.at < user_meta_record(key).last_read.to_f }
|
84
84
|
elsif since
|
85
85
|
activity(key).reject { |event| event.at < since.to_f }
|
86
86
|
else
|
@@ -94,7 +94,7 @@ module SimpleFeed
|
|
94
94
|
|
95
95
|
def reset_last_read(user_ids:, at: Time.now)
|
96
96
|
with_response_batched(user_ids) do |key|
|
97
|
-
|
97
|
+
user_meta_record(key)[:last_read] = at
|
98
98
|
at
|
99
99
|
end
|
100
100
|
end
|
@@ -107,13 +107,13 @@ module SimpleFeed
|
|
107
107
|
|
108
108
|
def unread_count(user_ids:)
|
109
109
|
with_response_batched(user_ids) do |key|
|
110
|
-
activity(key).count { |event| event.at >
|
110
|
+
activity(key).count { |event| event.at > user_meta_record(key).last_read.to_f }
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
114
|
def last_read(user_ids:)
|
115
115
|
with_response_batched(user_ids) do |key|
|
116
|
-
|
116
|
+
user_meta_record(key).last_read
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
@@ -122,7 +122,7 @@ module SimpleFeed
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def total_users
|
125
|
-
h.size
|
125
|
+
h.size / 2
|
126
126
|
end
|
127
127
|
|
128
128
|
private
|
@@ -139,27 +139,37 @@ module SimpleFeed
|
|
139
139
|
(size_before > size_after)
|
140
140
|
end
|
141
141
|
|
142
|
-
def
|
142
|
+
def create_meta_record
|
143
143
|
Hashie::Mash.new(
|
144
|
-
{ last_read: 0
|
144
|
+
{ last_read: 0 }
|
145
145
|
)
|
146
146
|
end
|
147
147
|
|
148
|
-
def
|
149
|
-
|
148
|
+
def create_data_record
|
149
|
+
Hashie::Mash.new(
|
150
|
+
{ activity: SortedSet.new }
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
def user_data_record(key)
|
155
|
+
h[key.data] ||= create_data_record
|
156
|
+
end
|
157
|
+
|
158
|
+
def user_meta_record(key)
|
159
|
+
h[key.meta] ||= create_meta_record
|
150
160
|
end
|
151
161
|
|
152
162
|
def wipe_user_record(key)
|
153
|
-
h[key.data] =
|
163
|
+
h[key.data] = create_data_record
|
154
164
|
end
|
155
165
|
|
156
166
|
def activity(key, event = nil)
|
157
|
-
|
158
|
-
|
167
|
+
user_data_record(key)[:activity] << event if event
|
168
|
+
user_data_record(key)[:activity].to_a
|
159
169
|
end
|
160
170
|
|
161
171
|
def add_event(event, key)
|
162
|
-
uas =
|
172
|
+
uas = user_data_record(key)[:activity]
|
163
173
|
if uas.include?(event)
|
164
174
|
false
|
165
175
|
else
|
@@ -172,11 +182,11 @@ module SimpleFeed
|
|
172
182
|
end
|
173
183
|
|
174
184
|
def __last_read(key, _value = nil)
|
175
|
-
|
185
|
+
user_meta_record(key)[:last_read]
|
176
186
|
end
|
177
187
|
|
178
188
|
def __delete(key, event)
|
179
|
-
|
189
|
+
user_data_record(key)[:activity].delete(event)
|
180
190
|
end
|
181
191
|
|
182
192
|
def create_event(*args, **opts)
|
@@ -2,10 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'base62-rb'
|
4
4
|
require 'hashie/mash'
|
5
|
-
require 'simplefeed/key/template'
|
6
|
-
require 'simplefeed/key/type'
|
7
|
-
|
8
|
-
require 'forwardable'
|
9
5
|
|
10
6
|
module SimpleFeed
|
11
7
|
module Providers
|
@@ -15,66 +11,85 @@ module SimpleFeed
|
|
15
11
|
# ↓ ↓
|
16
12
|
# "ff|u.f23098.m"
|
17
13
|
# ↑ ↑
|
18
|
-
# namespace
|
14
|
+
# namespace consumer(base62)
|
19
15
|
#
|
20
16
|
class Key
|
21
|
-
|
17
|
+
class << self
|
18
|
+
def rot13(value)
|
19
|
+
value.tr('abcdefghijklmnopqrstuvwxyz',
|
20
|
+
'nopqrstuvwxyzabcdefghijklm')
|
21
|
+
end
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
SERIALIZED_DATA_TEMPLATE = '{{namespace}}u.{{data_id}}.d'
|
25
|
+
SERIALIZED_META_TEMPLATE = '{{namespace}}u.{{meta_id}}.m'
|
25
26
|
|
26
|
-
|
27
|
-
self.user_id = user_id
|
28
|
-
self.key_template = key_template
|
27
|
+
attr_reader :consumer, :namespace, :data_key_transformer, :meta_key_transformer
|
29
28
|
|
30
|
-
|
29
|
+
def initialize(consumer,
|
30
|
+
namespace: nil,
|
31
|
+
data_key_transformer: nil,
|
32
|
+
meta_key_transformer: nil)
|
33
|
+
@consumer = consumer
|
34
|
+
@namespace = namespace
|
35
|
+
@data_key_transformer = data_key_transformer
|
36
|
+
@meta_key_transformer = meta_key_transformer
|
31
37
|
end
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
key_template.key_types.each do |type|
|
36
|
-
key_name = type.name
|
37
|
-
next if respond_to?(key_name)
|
38
|
-
|
39
|
-
self.class.send(:define_method, key_name) do
|
40
|
-
instance_variable_get("@#{key_name}") ||
|
41
|
-
instance_variable_set("@#{key_name}", type.render(render_options))
|
42
|
-
end
|
43
|
-
end
|
39
|
+
def data
|
40
|
+
@data ||= render(SERIALIZED_DATA_TEMPLATE)
|
44
41
|
end
|
45
42
|
|
46
|
-
def
|
47
|
-
@
|
48
|
-
::Base62.encode(user_id)
|
49
|
-
else
|
50
|
-
rot13(user_id.to_s)
|
51
|
-
end
|
43
|
+
def meta
|
44
|
+
@meta ||= render(SERIALIZED_META_TEMPLATE)
|
52
45
|
end
|
53
46
|
|
54
47
|
def keys
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
def render_options
|
59
|
-
key_template.render_options.merge!({
|
60
|
-
'user_id' => user_id,
|
61
|
-
'base62_user_id' => base62_user_id
|
62
|
-
})
|
48
|
+
[data, meta]
|
63
49
|
end
|
64
50
|
|
65
51
|
def to_s
|
66
|
-
super +
|
52
|
+
super + key_params.to_s
|
67
53
|
end
|
68
54
|
|
69
55
|
def inspect
|
70
|
-
|
56
|
+
super + key_params.inspect
|
71
57
|
end
|
72
58
|
|
73
59
|
private
|
74
60
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
61
|
+
def render(template)
|
62
|
+
template.dup.tap do |output|
|
63
|
+
key_params.each_pair do |key, value|
|
64
|
+
output.gsub!(/{{#{key}}}/, value.to_s)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def obscure_value(id)
|
70
|
+
id = id.to_i if id.is_a?(String) && id =~ /^[\d]+$/
|
71
|
+
|
72
|
+
if id.is_a?(Numeric)
|
73
|
+
::Base62.encode(id)
|
74
|
+
else
|
75
|
+
self.class.rot13(id.to_s)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def key_params
|
80
|
+
@key_params ||= Hashie::Mash.new(
|
81
|
+
namespace: namespace ? "#{namespace}|" : '',
|
82
|
+
data_id: obscure_value(data_id),
|
83
|
+
meta_id: obscure_value(meta_id)
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def meta_id
|
88
|
+
meta_key_transformer&.call(consumer) || consumer
|
89
|
+
end
|
90
|
+
|
91
|
+
def data_id
|
92
|
+
data_key_transformer&.call(consumer) || consumer
|
78
93
|
end
|
79
94
|
end
|
80
95
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module SimpleFeed
|
4
4
|
module Providers
|
5
|
+
RUBY_MAJOR_VERSION = RUBY_VERSION.split('.')[0..1].join.to_i
|
6
|
+
|
5
7
|
class Proxy
|
6
8
|
attr_accessor :provider
|
7
9
|
|
@@ -15,7 +17,7 @@ module SimpleFeed
|
|
15
17
|
end
|
16
18
|
|
17
19
|
def initialize(provider_or_klass, *args, **options)
|
18
|
-
self.provider = if provider_or_klass.is_a?(::String)
|
20
|
+
self.provider = if provider_or_klass.is_a?(::String) || provider_or_klass.is_a?(::Symbol)
|
19
21
|
::Object.const_get(provider_or_klass).new(*args, **options)
|
20
22
|
else
|
21
23
|
provider_or_klass
|
@@ -26,12 +28,23 @@ module SimpleFeed
|
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
provider
|
33
|
-
|
34
|
-
|
31
|
+
if RUBY_MAJOR_VERSION >= 27
|
32
|
+
# Forward all other method calls to Provider
|
33
|
+
def method_missing(name, *args, **opts, &block)
|
34
|
+
if provider&.respond_to?(name)
|
35
|
+
provider.send(name, *args, **opts, &block)
|
36
|
+
else
|
37
|
+
super(name, *args, **opts, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
# Forward all other method calls to Provider
|
42
|
+
def method_missing(name, *args, &block)
|
43
|
+
if provider&.respond_to?(name)
|
44
|
+
provider.send(name, *args, &block)
|
45
|
+
else
|
46
|
+
super(name, *args, &block)
|
47
|
+
end
|
35
48
|
end
|
36
49
|
end
|
37
50
|
end
|
@@ -51,9 +51,9 @@ module SimpleFeed
|
|
51
51
|
raise ArgumentError, '#delete_if must be called with a block that receives (user_id, event) as arguments.' unless block_given?
|
52
52
|
|
53
53
|
with_response_batched(user_ids) do |key|
|
54
|
-
fetch(user_ids: [key.
|
54
|
+
fetch(user_ids: [key.consumer])[key.consumer].map do |event|
|
55
55
|
with_redis do |redis|
|
56
|
-
if yield(event, key.
|
56
|
+
if yield(event, key.consumer)
|
57
57
|
redis.zrem(key.data, event.value) ? event : nil
|
58
58
|
end
|
59
59
|
end
|
@@ -90,7 +90,7 @@ module SimpleFeed
|
|
90
90
|
|
91
91
|
response = with_response_pipelined(user_ids) do |redis, key|
|
92
92
|
if since == :unread
|
93
|
-
redis.zrevrangebyscore(key.data, '+inf', (last_read_response.delete(key.
|
93
|
+
redis.zrevrangebyscore(key.data, '+inf', (last_read_response.delete(key.consumer) || 0).to_f, withscores: true)
|
94
94
|
elsif since
|
95
95
|
redis.zrevrangebyscore(key.data, '+inf', since.to_f, withscores: true)
|
96
96
|
else
|
@@ -120,7 +120,7 @@ module SimpleFeed
|
|
120
120
|
get_users_last_read(redis, key)
|
121
121
|
end
|
122
122
|
with_response_pipelined(response.user_ids, response) do |redis, key, _response|
|
123
|
-
last_read = _response.delete(key.
|
123
|
+
last_read = _response.delete(key.consumer).to_f
|
124
124
|
redis.zcount(key.data, last_read, '+inf')
|
125
125
|
end
|
126
126
|
end
|
@@ -223,7 +223,7 @@ module SimpleFeed
|
|
223
223
|
def with_response_pipelined(user_ids, response = nil)
|
224
224
|
with_response(response) do |response|
|
225
225
|
batch_pipelined(user_ids) do |redis, key|
|
226
|
-
response.for(key.
|
226
|
+
response.for(key.consumer) { yield(redis, key, response) }
|
227
227
|
end
|
228
228
|
end
|
229
229
|
end
|
@@ -231,7 +231,7 @@ module SimpleFeed
|
|
231
231
|
def with_response_multi(user_ids, response = nil)
|
232
232
|
with_response(response) do |response|
|
233
233
|
batch_multi(user_ids) do |redis, key|
|
234
|
-
response.for(key.
|
234
|
+
response.for(key.consumer) { yield(redis, key, response) }
|
235
235
|
end
|
236
236
|
end
|
237
237
|
end
|
data/lib/simplefeed/response.rb
CHANGED
data/lib/simplefeed/version.rb
CHANGED
Binary file
|
data/man/running-example.png
CHANGED
Binary file
|
data/man/sf-color-dump.png
CHANGED
Binary file
|
data/simple-feed.gemspec
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
lib = File.expand_path('lib', __dir__)
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
|
5
|
+
# noinspection RubyResolve
|
6
|
+
load File.expand_path("#{lib}/simplefeed/version.rb")
|
6
7
|
|
7
8
|
Gem::Specification.new do |spec|
|
8
9
|
spec.name = 'simple-feed'
|
@@ -30,10 +31,13 @@ Gem::Specification.new do |spec|
|
|
30
31
|
spec.add_dependency 'hashie'
|
31
32
|
spec.add_dependency 'hiredis'
|
32
33
|
spec.add_dependency 'redis'
|
34
|
+
spec.add_dependency 'tty-box'
|
35
|
+
spec.add_dependency 'tty-screen'
|
33
36
|
|
34
37
|
spec.add_development_dependency 'awesome_print'
|
35
38
|
spec.add_development_dependency 'bundler'
|
36
39
|
spec.add_development_dependency 'codeclimate-test-reporter'
|
40
|
+
spec.add_development_dependency 'codecov'
|
37
41
|
spec.add_development_dependency 'rake'
|
38
42
|
spec.add_development_dependency 'rspec'
|
39
43
|
spec.add_development_dependency 'rspec-its'
|