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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rubocop.yml +33 -0
  3. data/.github/workflows/ruby.yml +35 -0
  4. data/.relaxed-rubocop-2.4.yml +174 -0
  5. data/.rspec +1 -1
  6. data/.rubocop.yml +42 -1149
  7. data/.rubocop_todo.yml +134 -0
  8. data/.travis.yml +15 -8
  9. data/Gemfile +2 -0
  10. data/Guardfile +3 -3
  11. data/{README.md → README.adoc} +205 -140
  12. data/Rakefile +6 -7
  13. data/bin/console +1 -0
  14. data/examples/shared/provider_example.rb +10 -3
  15. data/lib/simple-feed.rb +2 -0
  16. data/lib/simple_feed.rb +2 -0
  17. data/lib/simplefeed.rb +9 -7
  18. data/lib/simplefeed/activity/base.rb +2 -0
  19. data/lib/simplefeed/activity/multi_user.rb +8 -6
  20. data/lib/simplefeed/activity/single_user.rb +5 -4
  21. data/lib/simplefeed/dsl.rb +3 -1
  22. data/lib/simplefeed/dsl/activities.rb +4 -3
  23. data/lib/simplefeed/dsl/formatter.rb +12 -12
  24. data/lib/simplefeed/event.rb +4 -3
  25. data/lib/simplefeed/feed.rb +19 -22
  26. data/lib/simplefeed/key/template.rb +5 -9
  27. data/lib/simplefeed/key/type.rb +4 -2
  28. data/lib/simplefeed/providers.rb +24 -9
  29. data/lib/simplefeed/providers/base/provider.rb +6 -3
  30. data/lib/simplefeed/providers/hash.rb +2 -0
  31. data/lib/simplefeed/providers/hash/paginator.rb +4 -2
  32. data/lib/simplefeed/providers/hash/provider.rb +11 -21
  33. data/lib/simplefeed/providers/key.rb +20 -11
  34. data/lib/simplefeed/providers/proxy.rb +13 -12
  35. data/lib/simplefeed/providers/redis.rb +2 -0
  36. data/lib/simplefeed/providers/redis/driver.rb +23 -23
  37. data/lib/simplefeed/providers/redis/provider.rb +35 -33
  38. data/lib/simplefeed/providers/redis/stats.rb +12 -13
  39. data/lib/simplefeed/response.rb +4 -2
  40. data/lib/simplefeed/version.rb +3 -1
  41. data/simple-feed.gemspec +16 -14
  42. metadata +58 -41
@@ -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
- self.template.render(opts.merge({ 'key_type' => name, 'key_marker' => marker }))
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
@@ -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
- self.registry[provider_name] = provider_class
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(store delete delete_if wipe reset_last_read last_read paginate fetch total_count unread_count)
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(total_memory_bytes total_users)
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.to_s}".to_sym : 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 self.respond_to?(:reset_last_read)
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 self.respond_to?(:transform_response)
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
@@ -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
- (page && page > 0) ? items[((page - 1) * per_page)...(page * per_page)] : items
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
- self.new(YAML.load(File.read(file)))
22
+ new(YAML.parse(File.read(file)))
25
23
  end
26
24
 
27
- def initialize(**opts)
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:, &block)
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).size > 0
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 = (page && page > 0) ? activity[((page - 1) * per_page)...(page * per_page)] : activity
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
- if defined?(::Knj)
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
- self.h.size
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, value = nil)
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 = 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
- unless self.respond_to?(key_name)
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 ||= ::Base62.encode(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| self.send(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' => 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
- self.new(definition[:klass], *definition[:args], **definition[:opts])
11
+ new(definition[:klass], *definition[:args], **definition[:opts])
10
12
  else
11
- self.new(definition)
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
- self.provider = ::Object.const_get(provider_or_klass).new(*args, **options)
19
- else
20
- self.provider = provider_or_klass
21
- end
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 self.provider && provider.respond_to?(name)
31
- self.provider.send(name, *args, &block)
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
  module SimpleFeed
2
4
  module Providers
3
5
  module Redis
@@ -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
- self.debug
17
+ debug
16
18
  end
17
19
 
18
- def self.with_debug(&block)
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
- =begin
75
-
76
- Various ways of defining a new Redis driver:
77
-
78
- SimpleFeed::Redis::Driver.new(pool: ConnectionPool.new(size: 2) { Redis.new })
79
- SimpleFeed::Redis::Driver.new(redis: -> { Redis.new }, pool_size: 2)
80
- SimpleFeed::Redis::Driver.new(redis: Redis.new)
81
- SimpleFeed::Redis::Driver.new(redis: { host: 'localhost', port: 6379, db: 1, timeout: 0.2 }, pool_size: 1)
82
-
83
- =end
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 self.pool && self.pool.respond_to?(:with)
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, **opts, &block)
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(self.debug? ? LoggingRedis.new(redis) : redis)
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
- (e.message =~ /loading/i || e.message =~ /connection/i) ? on_error(e) : raise(e)
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.new(e)
178
+ raise Error, e
178
179
  end
179
-
180
180
  end
181
181
  end
182
182
  end