simple-feed 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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