simple-feed 2.1.0 → 3.0.1
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.
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'
|