simple-feed 2.0.2 → 2.1.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 +5 -5
- data/.github/workflows/rubocop.yml +33 -0
- data/.github/workflows/ruby.yml +35 -0
- data/.relaxed-rubocop-2.4.yml +174 -0
- data/.rspec +1 -1
- data/.rubocop.yml +42 -1149
- data/.rubocop_todo.yml +134 -0
- data/.travis.yml +15 -8
- data/Gemfile +2 -0
- data/Guardfile +3 -3
- data/{README.md → README.adoc} +205 -140
- data/Rakefile +6 -7
- data/bin/console +1 -0
- data/examples/shared/provider_example.rb +10 -3
- data/lib/simple-feed.rb +2 -0
- data/lib/simple_feed.rb +2 -0
- data/lib/simplefeed.rb +9 -7
- data/lib/simplefeed/activity/base.rb +2 -0
- data/lib/simplefeed/activity/multi_user.rb +8 -6
- data/lib/simplefeed/activity/single_user.rb +5 -4
- data/lib/simplefeed/dsl.rb +3 -1
- data/lib/simplefeed/dsl/activities.rb +4 -3
- data/lib/simplefeed/dsl/formatter.rb +12 -12
- data/lib/simplefeed/event.rb +4 -3
- data/lib/simplefeed/feed.rb +19 -22
- data/lib/simplefeed/key/template.rb +5 -9
- data/lib/simplefeed/key/type.rb +4 -2
- data/lib/simplefeed/providers.rb +24 -9
- data/lib/simplefeed/providers/base/provider.rb +6 -3
- data/lib/simplefeed/providers/hash.rb +2 -0
- data/lib/simplefeed/providers/hash/paginator.rb +4 -2
- data/lib/simplefeed/providers/hash/provider.rb +11 -21
- data/lib/simplefeed/providers/key.rb +20 -11
- data/lib/simplefeed/providers/proxy.rb +13 -12
- data/lib/simplefeed/providers/redis.rb +2 -0
- data/lib/simplefeed/providers/redis/driver.rb +23 -23
- data/lib/simplefeed/providers/redis/provider.rb +35 -33
- data/lib/simplefeed/providers/redis/stats.rb +12 -13
- data/lib/simplefeed/response.rb +4 -2
- data/lib/simplefeed/version.rb +3 -1
- data/simple-feed.gemspec +16 -14
- metadata +58 -41
data/lib/simplefeed/key/type.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'template'
|
2
4
|
|
3
5
|
module SimpleFeed
|
@@ -11,7 +13,8 @@ module SimpleFeed
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def render(opts = {})
|
14
|
-
|
16
|
+
template.render(opts.merge({ key_type: name,
|
17
|
+
key_marker: marker }))
|
15
18
|
end
|
16
19
|
end
|
17
20
|
|
@@ -21,6 +24,5 @@ module SimpleFeed
|
|
21
24
|
].map do |type|
|
22
25
|
Type.new(type[:name], type[:marker], DEFAULT_TEXT_TEMPLATE)
|
23
26
|
end
|
24
|
-
|
25
27
|
end
|
26
28
|
end
|
data/lib/simplefeed/providers.rb
CHANGED
@@ -1,33 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'providers/key'
|
2
4
|
require_relative 'providers/proxy'
|
3
5
|
|
4
6
|
module SimpleFeed
|
5
7
|
module Providers
|
6
8
|
@registry = {}
|
7
|
-
|
9
|
+
|
8
10
|
def self.registry
|
9
11
|
@registry
|
10
12
|
end
|
11
|
-
|
13
|
+
|
12
14
|
def self.register(provider_name, provider_class)
|
13
|
-
|
15
|
+
registry[provider_name] = provider_class
|
14
16
|
end
|
15
|
-
|
17
|
+
|
16
18
|
# These methods must be implemented by each Provider, and operation on a given
|
17
19
|
# set of users passed via the user_ids: parameter.
|
18
|
-
ACTIVITY_METHODS = %i
|
20
|
+
ACTIVITY_METHODS = %i[
|
21
|
+
store
|
22
|
+
delete
|
23
|
+
delete_if
|
24
|
+
wipe
|
25
|
+
reset_last_read
|
26
|
+
last_read paginate
|
27
|
+
fetch
|
28
|
+
total_count
|
29
|
+
unread_count
|
30
|
+
].freeze
|
19
31
|
|
20
|
-
# These methods must be implemented in order to gather statistics about each provider's
|
32
|
+
# These optional methods must be implemented in order to gather statistics about each provider's
|
21
33
|
# memory consumption and state.
|
22
|
-
FEED_METHODS = %i
|
34
|
+
FEED_METHODS = %i[
|
35
|
+
total_memory_bytes
|
36
|
+
total_users
|
37
|
+
].freeze
|
23
38
|
|
24
|
-
REQUIRED_METHODS = ACTIVITY_METHODS + FEED_METHODS
|
39
|
+
REQUIRED_METHODS = (ACTIVITY_METHODS + FEED_METHODS).freeze
|
25
40
|
|
26
41
|
def self.define_provider_methods(klass, prefix = nil, &block)
|
27
42
|
# Methods on the class instance
|
28
43
|
klass.class_eval do
|
29
44
|
SimpleFeed::Providers::REQUIRED_METHODS.each do |m|
|
30
|
-
method_name = prefix ? "#{prefix}_#{m
|
45
|
+
method_name = prefix ? "#{prefix}_#{m}".to_sym : m
|
31
46
|
define_method(method_name) do |*args, **opts, &b|
|
32
47
|
block.call(self, m, *args, **opts, &b)
|
33
48
|
end
|
@@ -1,4 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'simplefeed/providers/key'
|
4
|
+
require 'date'
|
5
|
+
require 'time'
|
2
6
|
|
3
7
|
module SimpleFeed
|
4
8
|
module Providers
|
@@ -27,7 +31,7 @@ module SimpleFeed
|
|
27
31
|
at = at.to_time if at.respond_to?(:to_time)
|
28
32
|
at = at.to_f if at.respond_to?(:to_f)
|
29
33
|
|
30
|
-
if
|
34
|
+
if respond_to?(:reset_last_read)
|
31
35
|
reset_last_read(user_ids: user_ids, at: at)
|
32
36
|
else
|
33
37
|
raise ArgumentError, "Class #{self.class} does not implement #reset_last_read method"
|
@@ -70,7 +74,7 @@ module SimpleFeed
|
|
70
74
|
def with_response(response = nil)
|
71
75
|
response ||= SimpleFeed::Response.new
|
72
76
|
yield(response)
|
73
|
-
if
|
77
|
+
if respond_to?(:transform_response)
|
74
78
|
response.transform do |user_id, result|
|
75
79
|
# calling into a subclass
|
76
80
|
transform_response(user_id, result)
|
@@ -78,7 +82,6 @@ module SimpleFeed
|
|
78
82
|
end
|
79
83
|
response
|
80
84
|
end
|
81
|
-
|
82
85
|
end
|
83
86
|
end
|
84
87
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SimpleFeed
|
2
4
|
module Providers
|
3
5
|
module Hash
|
@@ -6,7 +8,6 @@ module SimpleFeed
|
|
6
8
|
#
|
7
9
|
# Of course this is not very efficient, because it requires fetching all events for the user.
|
8
10
|
module Paginator
|
9
|
-
|
10
11
|
def paginate(user_ids:, page: nil, per_page: feed.per_page, &block)
|
11
12
|
response = feed.fetch(user_ids: user_ids)
|
12
13
|
response = SimpleFeed::Response.new(response.to_h)
|
@@ -16,11 +17,12 @@ module SimpleFeed
|
|
16
17
|
end
|
17
18
|
|
18
19
|
def paginate_items(items, page: nil, per_page: nil)
|
19
|
-
|
20
|
+
page && page > 0 ? items[((page - 1) * per_page)...(page * per_page)] : items
|
20
21
|
end
|
21
22
|
|
22
23
|
def order_events(events, &block)
|
23
24
|
return nil unless events
|
25
|
+
|
24
26
|
events.sort do |a, b|
|
25
27
|
block ? yield(a, b) : b.at <=> a.at
|
26
28
|
end
|
@@ -1,11 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base62-rb'
|
2
4
|
require 'hashie'
|
3
5
|
require 'set'
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'knjrbfw'
|
7
|
-
rescue LoadError
|
8
|
-
end
|
6
|
+
require 'objspace'
|
9
7
|
|
10
8
|
require 'simplefeed/event'
|
11
9
|
require_relative 'paginator'
|
@@ -21,10 +19,10 @@ module SimpleFeed
|
|
21
19
|
include SimpleFeed::Providers::Hash::Paginator
|
22
20
|
|
23
21
|
def self.from_yaml(file)
|
24
|
-
|
22
|
+
new(YAML.parse(File.read(file)))
|
25
23
|
end
|
26
24
|
|
27
|
-
def initialize(
|
25
|
+
def initialize(opts)
|
28
26
|
self.h = {}
|
29
27
|
h.merge!(opts)
|
30
28
|
end
|
@@ -45,14 +43,12 @@ module SimpleFeed
|
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
48
|
-
def delete_if(user_ids
|
46
|
+
def delete_if(user_ids:)
|
49
47
|
with_response_batched(user_ids) do |key|
|
50
48
|
activity(key).map do |event|
|
51
49
|
if yield(event, key.user_id)
|
52
50
|
__delete(key, event)
|
53
51
|
event
|
54
|
-
else
|
55
|
-
nil
|
56
52
|
end
|
57
53
|
end.compact
|
58
54
|
end
|
@@ -60,7 +56,7 @@ module SimpleFeed
|
|
60
56
|
|
61
57
|
def wipe(user_ids:)
|
62
58
|
with_response_batched(user_ids) do |key|
|
63
|
-
deleted = activity(key).
|
59
|
+
deleted = !activity(key).empty?
|
64
60
|
wipe_user_record(key)
|
65
61
|
deleted
|
66
62
|
end
|
@@ -76,7 +72,7 @@ module SimpleFeed
|
|
76
72
|
|
77
73
|
with_response_batched(user_ids) do |key|
|
78
74
|
activity = activity(key)
|
79
|
-
result =
|
75
|
+
result = page && page > 0 ? activity[((page - 1) * per_page)...(page * per_page)] : activity
|
80
76
|
with_total ? { events: result, total_count: activity.length } : result
|
81
77
|
end
|
82
78
|
end
|
@@ -122,16 +118,11 @@ module SimpleFeed
|
|
122
118
|
end
|
123
119
|
|
124
120
|
def total_memory_bytes
|
125
|
-
|
126
|
-
analyzer = Knj::Memory_analyzer::Object_size_counter.new(self.h)
|
127
|
-
analyzer.calculate_size
|
128
|
-
else
|
129
|
-
raise LoadError, 'Please run "gem install knjrbfw" to get accurate hash size'
|
130
|
-
end
|
121
|
+
ObjectSpace.memsize_of(h)
|
131
122
|
end
|
132
123
|
|
133
124
|
def total_users
|
134
|
-
|
125
|
+
h.size
|
135
126
|
end
|
136
127
|
|
137
128
|
private
|
@@ -140,7 +131,6 @@ module SimpleFeed
|
|
140
131
|
# Methods below operate on a single user only
|
141
132
|
#
|
142
133
|
|
143
|
-
|
144
134
|
def changed_activity_size?(key)
|
145
135
|
ua = activity(key)
|
146
136
|
size_before = ua.size
|
@@ -181,7 +171,7 @@ module SimpleFeed
|
|
181
171
|
end
|
182
172
|
end
|
183
173
|
|
184
|
-
def __last_read(key,
|
174
|
+
def __last_read(key, _value = nil)
|
185
175
|
user_record(key)[:last_read]
|
186
176
|
end
|
187
177
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base62-rb'
|
2
4
|
require 'hashie/mash'
|
3
5
|
require 'simplefeed/key/template'
|
@@ -22,36 +24,40 @@ module SimpleFeed
|
|
22
24
|
def_delegators :@key_template, :key_names, :key_types
|
23
25
|
|
24
26
|
def initialize(user_id, key_template)
|
25
|
-
self.user_id
|
27
|
+
self.user_id = user_id
|
26
28
|
self.key_template = key_template
|
27
29
|
|
28
30
|
define_key_methods
|
29
31
|
end
|
30
32
|
|
33
|
+
# Defines #data and #meta methods.
|
31
34
|
def define_key_methods
|
32
35
|
key_template.key_types.each do |type|
|
33
36
|
key_name = type.name
|
34
|
-
|
35
|
-
self.class.send(:define_method, key_name) do
|
36
|
-
instance_variable_get("@#{key_name}") ||
|
37
|
-
instance_variable_set("@#{key_name}", type.render(render_options))
|
38
|
-
end
|
39
|
-
end
|
37
|
+
next if respond_to?(key_name)
|
40
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
|
41
43
|
end
|
42
44
|
end
|
43
45
|
|
44
46
|
def base62_user_id
|
45
|
-
@base62_user_id ||=
|
47
|
+
@base62_user_id ||= if user_id.is_a?(Numeric)
|
48
|
+
::Base62.encode(user_id)
|
49
|
+
else
|
50
|
+
rot13(user_id.to_s)
|
51
|
+
end
|
46
52
|
end
|
47
53
|
|
48
54
|
def keys
|
49
|
-
key_names.map { |name|
|
55
|
+
key_names.map { |name| send(name) }
|
50
56
|
end
|
51
57
|
|
52
58
|
def render_options
|
53
59
|
key_template.render_options.merge!({
|
54
|
-
'user_id'
|
60
|
+
'user_id' => user_id,
|
55
61
|
'base62_user_id' => base62_user_id
|
56
62
|
})
|
57
63
|
end
|
@@ -66,7 +72,10 @@ module SimpleFeed
|
|
66
72
|
|
67
73
|
private
|
68
74
|
|
75
|
+
def rot13(value)
|
76
|
+
value.tr('abcdefghijklmnopqrstuvwxyz',
|
77
|
+
'nopqrstuvwxyzabcdefghijklm')
|
78
|
+
end
|
69
79
|
end
|
70
80
|
end
|
71
81
|
end
|
72
|
-
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SimpleFeed
|
2
4
|
module Providers
|
3
5
|
class Proxy
|
@@ -6,19 +8,18 @@ module SimpleFeed
|
|
6
8
|
def self.from(definition)
|
7
9
|
if definition.is_a?(::Hash)
|
8
10
|
::SimpleFeed.symbolize!(definition)
|
9
|
-
|
11
|
+
new(definition[:klass], *definition[:args], **definition[:opts])
|
10
12
|
else
|
11
|
-
|
13
|
+
new(definition)
|
12
14
|
end
|
13
|
-
|
14
15
|
end
|
15
16
|
|
16
17
|
def initialize(provider_or_klass, *args, **options)
|
17
|
-
if provider_or_klass.is_a?(::String)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
self.provider = if provider_or_klass.is_a?(::String)
|
19
|
+
::Object.const_get(provider_or_klass).new(*args, **options)
|
20
|
+
else
|
21
|
+
provider_or_klass
|
22
|
+
end
|
22
23
|
|
23
24
|
SimpleFeed::Providers::REQUIRED_METHODS.each do |m|
|
24
25
|
raise ArgumentError, "Invalid provider #{provider.class}\nMethod '#{m}' is required." unless provider.respond_to?(m)
|
@@ -26,11 +27,11 @@ module SimpleFeed
|
|
26
27
|
end
|
27
28
|
|
28
29
|
# Forward all other method calls to Provider
|
29
|
-
def method_missing(name, *args, &block)
|
30
|
-
if
|
31
|
-
|
30
|
+
def method_missing(name, *args, **opts, &block)
|
31
|
+
if provider&.respond_to?(name)
|
32
|
+
provider.send(name, *args, **opts, &block)
|
32
33
|
else
|
33
|
-
super(name, *args, &block)
|
34
|
+
super(name, *args, **opts, &block)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'redis'
|
2
4
|
require 'redis/connection/hiredis'
|
3
5
|
require 'connection_pool'
|
@@ -12,10 +14,10 @@ module SimpleFeed
|
|
12
14
|
@debug = ENV['REDIS_DEBUG']
|
13
15
|
|
14
16
|
def self.debug?
|
15
|
-
|
17
|
+
debug
|
16
18
|
end
|
17
19
|
|
18
|
-
def self.with_debug
|
20
|
+
def self.with_debug
|
19
21
|
previous_value = SimpleFeed::Providers::Redis.debug
|
20
22
|
SimpleFeed::Providers::Redis.debug = true
|
21
23
|
result = yield if block_given?
|
@@ -48,10 +50,11 @@ module SimpleFeed
|
|
48
50
|
colors = [:blue, nil, :blue, :blue, :yellow, :cyan, nil, :blue]
|
49
51
|
components = [
|
50
52
|
Time.now.strftime('%H:%M:%S.%L'), ' rtt=',
|
51
|
-
(sprintf '%.5f', delta*1000), ' ms ',
|
53
|
+
(sprintf '%.5f', delta * 1000), ' ms ',
|
52
54
|
(sprintf '%15s ', m.to_s.upcase),
|
53
55
|
(sprintf '%-40s', args.inspect.gsub(/[",\[\]]/, '')), ' ⇒ ',
|
54
|
-
(result.is_a?(::Redis::Future) ? '' : result.to_s)
|
56
|
+
(result.is_a?(::Redis::Future) ? '' : result.to_s)
|
57
|
+
]
|
55
58
|
components.each_with_index do |component, index|
|
56
59
|
color = self.class.disable_color ? nil : colors[index]
|
57
60
|
component = component.send(color) if color
|
@@ -71,18 +74,16 @@ module SimpleFeed
|
|
71
74
|
|
72
75
|
attr_accessor :pool
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
SimpleFeed::Redis::Driver.new(
|
79
|
-
SimpleFeed::Redis::Driver.new(redis:
|
80
|
-
SimpleFeed::Redis::Driver.new(redis:
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def initialize(**opts)
|
85
|
-
if opts[:pool] && opts[:pool].respond_to?(:with)
|
77
|
+
#
|
78
|
+
# Various ways of defining a new Redis driver:
|
79
|
+
#
|
80
|
+
# SimpleFeed::Redis::Driver.new(pool: ConnectionPool.new(size: 2) { Redis.new })
|
81
|
+
# SimpleFeed::Redis::Driver.new(redis: -> { Redis.new }, pool_size: 2)
|
82
|
+
# SimpleFeed::Redis::Driver.new(redis: Redis.new)
|
83
|
+
# SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, timeout: 0.2 }, pool_size: 1)
|
84
|
+
#
|
85
|
+
def initialize(opts)
|
86
|
+
if opts[:pool]&.respond_to?(:with)
|
86
87
|
self.pool = opts[:pool]
|
87
88
|
|
88
89
|
elsif opts[:redis]
|
@@ -104,7 +105,7 @@ SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, tim
|
|
104
105
|
end
|
105
106
|
end
|
106
107
|
|
107
|
-
raise ArgumentError, "Unable to construct Redis connection from arguments: #{opts.inspect}" unless
|
108
|
+
raise ArgumentError, "Unable to construct Redis connection from arguments: #{opts.inspect}" unless pool&.respond_to?(:with)
|
108
109
|
end
|
109
110
|
|
110
111
|
%i(set get incr decr setex expire del setnx exists zadd zrange).each do |method|
|
@@ -117,7 +118,7 @@ SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, tim
|
|
117
118
|
alias_method :rm, :del
|
118
119
|
alias_method :exists?, :exists
|
119
120
|
|
120
|
-
def exec(redis_method, *args, **
|
121
|
+
def exec(redis_method, *args, **_opts, &block)
|
121
122
|
send_proc = redis_method if redis_method.respond_to?(:call)
|
122
123
|
send_proc ||= ->(redis) { redis.send(redis_method, *args, &block) }
|
123
124
|
|
@@ -125,7 +126,7 @@ SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, tim
|
|
125
126
|
end
|
126
127
|
|
127
128
|
class MockRedis
|
128
|
-
def method_missing(name, *args, &block)
|
129
|
+
def method_missing(name, *args, **_opts, &block)
|
129
130
|
puts "calling redis.#{name}(#{args.to_s.gsub(/[\[\]]/, '')}) { #{block ? block.call : nil} }"
|
130
131
|
end
|
131
132
|
end
|
@@ -133,7 +134,7 @@ SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, tim
|
|
133
134
|
def with_redis
|
134
135
|
with_retries do
|
135
136
|
pool.with do |redis|
|
136
|
-
yield(
|
137
|
+
yield(debug? ? LoggingRedis.new(redis) : redis)
|
137
138
|
end
|
138
139
|
end
|
139
140
|
end
|
@@ -170,13 +171,12 @@ SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, tim
|
|
170
171
|
on_error e
|
171
172
|
end
|
172
173
|
rescue ::Redis::CommandError => e
|
173
|
-
|
174
|
+
e.message =~ /loading/i || e.message =~ /connection/i ? on_error(e) : raise(e)
|
174
175
|
end
|
175
176
|
|
176
177
|
def on_error(e)
|
177
|
-
raise Error
|
178
|
+
raise Error, e
|
178
179
|
end
|
179
|
-
|
180
180
|
end
|
181
181
|
end
|
182
182
|
end
|