simple-feed 2.1.0 → 3.1.2

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.
data/lib/simplefeed.rb CHANGED
@@ -9,45 +9,49 @@ Hashie.logger = Logger.new(nil)
9
9
  ::Dir.glob(::File.expand_path('../simplefeed/*.rb', __FILE__)).each { |f| require_relative(f) }
10
10
 
11
11
  require 'simplefeed/providers/redis'
12
- require 'simplefeed/providers/hash'
13
12
  require 'simplefeed/dsl'
13
+ require 'simplefeed/feed'
14
14
 
15
15
  # Main namespace module for the SimpleFeed gem. It provides several shortcuts and entry
16
16
  # points into the library, such as ability to define and fetch new feeds via +define+,
17
17
  # and so on.
18
18
  module SimpleFeed
19
+ TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
20
+
19
21
  @registry = {}
20
22
 
21
23
  class << self
22
- # @return [Hash<Symbol, Feed>] the registry of the defined feeds
24
+ # @return Hash<Symbol, Feed> the registry of the defined feeds
23
25
  attr_reader :registry
24
26
 
25
- # @param name [Symbol] feed name
26
- # @param options [Hash] any key-value pairs to set on the feed
27
+ # @param <Symbol> name of the feed
28
+ # @param <Hash> options any key-value pairs to set on the feed
27
29
  #
28
- # @return [Feed] the feed with the given name, and defined via options and a block
29
- def define(name, **options, &block)
30
+ # @return [Feed] the feed with the given name, and defined via options and a block
31
+ def define(name, **options)
30
32
  name = name.to_sym unless name.is_a?(Symbol)
31
- feed = registry[name] || SimpleFeed::Feed.new(name)
32
- feed.configure(options) do
33
- block&.call(feed)
33
+
34
+ registry[name] ||= Feed.new(name)
35
+ registry[name].tap do |feed|
36
+ feed.configure(options) do
37
+ yield(feed) if block_given?
38
+ end
34
39
  end
35
- registry[name] = feed
36
- feed
37
40
  end
38
41
 
39
- # @return [Feed] the pre-defined feed with the given name
42
+ # @param [Symbol] name
43
+ # @return <Feed> the pre-defined feed with the given name
40
44
  def get(name)
41
45
  registry[name.to_sym]
42
46
  end
43
47
 
44
48
  # A factory method that constructs an instance of a provider based on the provider name and arguments.
45
49
  #
46
- # @param provider_name [Symbol] short name of the provider, eg, :redis, :hash, etc.
47
- # @params args [Array] constructor array arguments of the provider
48
- # @params opts [Hash] constructor hash arguments of the provider
50
+ # @param <Symbol> provider_name short name of the provider, eg, :redis, :hash, etc.
51
+ # @param <Array> args constructor array arguments of the provider
52
+ # @param <Hash, NilClass> opts constructor hash arguments of the provider
49
53
  #
50
- # @return [Provider]
54
+ # @return <Provider>
51
55
  def provider(provider_name, *args, **opts, &block)
52
56
  provider_class = SimpleFeed::Providers.registry[provider_name]
53
57
  raise ArgumentError, "No provider named #{provider_name} was found, #{SimpleFeed::Providers.registry.inspect}" unless provider_class
Binary file
Binary file
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
- require 'simplefeed/version'
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'
@@ -24,16 +25,20 @@ Gem::Specification.new do |spec|
24
25
  spec.require_paths = ['lib']
25
26
 
26
27
  spec.add_dependency 'activesupport'
28
+ spec.add_dependency 'awesome_print'
27
29
  spec.add_dependency 'base62-rb'
28
30
  spec.add_dependency 'colored2'
29
31
  spec.add_dependency 'connection_pool', '~> 2'
30
32
  spec.add_dependency 'hashie'
31
33
  spec.add_dependency 'hiredis'
32
34
  spec.add_dependency 'redis'
35
+ spec.add_dependency 'sorted_set'
36
+ spec.add_dependency 'tty-box'
37
+ spec.add_dependency 'tty-screen'
33
38
 
34
- spec.add_development_dependency 'awesome_print'
35
39
  spec.add_development_dependency 'bundler'
36
40
  spec.add_development_dependency 'codeclimate-test-reporter'
41
+ spec.add_development_dependency 'codecov'
37
42
  spec.add_development_dependency 'rake'
38
43
  spec.add_development_dependency 'rspec'
39
44
  spec.add_development_dependency 'rspec-its'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple-feed
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-24 00:00:00.000000000 Z
11
+ date: 2022-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: awesome_print
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: base62-rb
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -109,13 +123,41 @@ dependencies:
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
- name: awesome_print
126
+ name: sorted_set
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - ">="
116
130
  - !ruby/object:Gem::Version
117
131
  version: '0'
118
- type: :development
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: tty-box
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: tty-screen
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
119
161
  prerelease: false
120
162
  version_requirements: !ruby/object:Gem::Requirement
121
163
  requirements:
@@ -150,6 +192,20 @@ dependencies:
150
192
  - - ">="
151
193
  - !ruby/object:Gem::Version
152
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: codecov
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
153
209
  - !ruby/object:Gem::Dependency
154
210
  name: rake
155
211
  requirement: !ruby/object:Gem::Requirement
@@ -261,6 +317,7 @@ extensions: []
261
317
  extra_rdoc_files: []
262
318
  files:
263
319
  - ".codeclimate.yml"
320
+ - ".envrc"
264
321
  - ".github/workflows/rubocop.yml"
265
322
  - ".github/workflows/ruby.yml"
266
323
  - ".gitignore"
@@ -270,6 +327,7 @@ files:
270
327
  - ".rubocop_todo.yml"
271
328
  - ".travis.yml"
272
329
  - ".yardopts"
330
+ - CHANGELOG.md
273
331
  - Gemfile
274
332
  - Guardfile
275
333
  - LICENSE.txt
@@ -277,7 +335,7 @@ files:
277
335
  - Rakefile
278
336
  - bin/console
279
337
  - bin/setup
280
- - examples/hash_provider_example.rb
338
+ - codecov.yml
281
339
  - examples/redis_provider_example.rb
282
340
  - examples/shared/provider_example.rb
283
341
  - lib/simple-feed.rb
@@ -291,13 +349,9 @@ files:
291
349
  - lib/simplefeed/dsl/formatter.rb
292
350
  - lib/simplefeed/event.rb
293
351
  - lib/simplefeed/feed.rb
294
- - lib/simplefeed/key/template.rb
295
- - lib/simplefeed/key/type.rb
296
352
  - lib/simplefeed/providers.rb
297
353
  - lib/simplefeed/providers/base/provider.rb
298
354
  - lib/simplefeed/providers/hash.rb
299
- - lib/simplefeed/providers/hash/paginator.rb
300
- - lib/simplefeed/providers/hash/provider.rb
301
355
  - lib/simplefeed/providers/key.rb
302
356
  - lib/simplefeed/providers/proxy.rb
303
357
  - lib/simplefeed/providers/redis.rb
@@ -316,7 +370,7 @@ homepage: https://github.com/kigster/simple-feed
316
370
  licenses:
317
371
  - MIT
318
372
  metadata: {}
319
- post_install_message:
373
+ post_install_message:
320
374
  rdoc_options: []
321
375
  require_paths:
322
376
  - lib
@@ -331,8 +385,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
331
385
  - !ruby/object:Gem::Version
332
386
  version: '0'
333
387
  requirements: []
334
- rubygems_version: 3.1.3
335
- signing_key:
388
+ rubygems_version: 3.3.7
389
+ signing_key:
336
390
  specification_version: 4
337
391
  summary: Create multiple types of social networking activity feeds with simple-feed
338
392
  gem, which uses a pluggable backend provider implementation, and ships with a Redis
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # This is an executable example of working with a Redis feed, and storing
4
- # events for various users.
5
- #
6
- # DEPENDENCIES:
7
- # gem install colored2
8
- # gem install awesome_print
9
- #
10
- # RUNNING
11
- # ruby redis-feed.rb
12
- #
13
-
14
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
15
-
16
- require 'simplefeed'
17
-
18
- @feed = SimpleFeed.define(:news) do |f|
19
- f.provider = SimpleFeed.provider(:hash)
20
- f.max_size = 1000
21
- end
22
-
23
- require_relative 'shared/provider_example'
24
-
@@ -1,48 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base62-rb'
4
- require 'hashie/mash'
5
-
6
- module SimpleFeed
7
- module Key
8
- class TextTemplate < Struct.new(:text)
9
- def render(params = {})
10
- output = text.dup
11
- params.each_pair do |key, value|
12
- output.gsub!(/{{\s*#{key}\s*}}/, value.to_s)
13
- end
14
- output
15
- end
16
- end
17
-
18
- DEFAULT_TEXT_TEMPLATE = TextTemplate.new('{{ namespace }}u.{{ base62_user_id }}.{{ key_marker }}')
19
-
20
- class Template
21
- attr_accessor :namespace, :key_types, :text_template
22
-
23
- def initialize(namespace,
24
- key_types = DEFAULT_TYPES,
25
- text_template = DEFAULT_TEXT_TEMPLATE)
26
-
27
- self.namespace = namespace
28
- self.key_types = key_types
29
- self.text_template = text_template
30
-
31
- self.key_types.each do |type|
32
- type.template ||= text_template if text_template
33
- end
34
- end
35
-
36
- def render_options
37
- h = {}
38
- h.merge!({ 'namespace' => namespace ? "#{namespace}|" : '' })
39
- h
40
- end
41
-
42
- # Returns array of key names, such as [:meta, :data]
43
- def key_names
44
- key_types.map(&:name).map(&:to_s).sort
45
- end
46
- end
47
- end
48
- end
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'template'
4
-
5
- module SimpleFeed
6
- module Key
7
- class Type < Struct.new(:name, :marker)
8
- attr_accessor :template
9
-
10
- def initialize(name, marker, template = nil)
11
- super(name, marker)
12
- self.template = template
13
- end
14
-
15
- def render(opts = {})
16
- template.render(opts.merge({ key_type: name,
17
- key_marker: marker }))
18
- end
19
- end
20
-
21
- DEFAULT_TYPES = [
22
- { name: :data, marker: 'd' },
23
- { name: :meta, marker: 'm' }
24
- ].map do |type|
25
- Type.new(type[:name], type[:marker], DEFAULT_TEXT_TEMPLATE)
26
- end
27
- end
28
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SimpleFeed
4
- module Providers
5
- module Hash
6
- # Include this module into any provider etc that has access to the +feed.fetch+
7
- # methods, and it will provide +paginate+ method based on all.
8
- #
9
- # Of course this is not very efficient, because it requires fetching all events for the user.
10
- module Paginator
11
- def paginate(user_ids:, page: nil, per_page: feed.per_page, &block)
12
- response = feed.fetch(user_ids: user_ids)
13
- response = SimpleFeed::Response.new(response.to_h)
14
- response.transform do |*, events|
15
- paginate_items(order_events(events, &block), page: page, per_page: per_page)
16
- end
17
- end
18
-
19
- def paginate_items(items, page: nil, per_page: nil)
20
- page && page > 0 ? items[((page - 1) * per_page)...(page * per_page)] : items
21
- end
22
-
23
- def order_events(events, &block)
24
- return nil unless events
25
-
26
- events.sort do |a, b|
27
- block ? yield(a, b) : b.at <=> a.at
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,188 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base62-rb'
4
- require 'hashie'
5
- require 'set'
6
- require 'objspace'
7
-
8
- require 'simplefeed/event'
9
- require_relative 'paginator'
10
- require_relative '../key'
11
- require_relative '../base/provider'
12
-
13
- module SimpleFeed
14
- module Providers
15
- module Hash
16
- class Provider < ::SimpleFeed::Providers::Base::Provider
17
- attr_accessor :h
18
-
19
- include SimpleFeed::Providers::Hash::Paginator
20
-
21
- def self.from_yaml(file)
22
- new(YAML.parse(File.read(file)))
23
- end
24
-
25
- def initialize(opts)
26
- self.h = {}
27
- h.merge!(opts)
28
- end
29
-
30
- def store(user_ids:, value:, at: Time.now)
31
- event = create_event(value, at)
32
- with_response_batched(user_ids) do |key|
33
- add_event(event, key)
34
- end
35
- end
36
-
37
- def delete(user_ids:, value:, at: nil)
38
- event = create_event(value, at)
39
- with_response_batched(user_ids) do |key|
40
- changed_activity_size?(key) do
41
- __delete(key, event)
42
- end
43
- end
44
- end
45
-
46
- def delete_if(user_ids:)
47
- with_response_batched(user_ids) do |key|
48
- activity(key).map do |event|
49
- if yield(event, key.user_id)
50
- __delete(key, event)
51
- event
52
- end
53
- end.compact
54
- end
55
- end
56
-
57
- def wipe(user_ids:)
58
- with_response_batched(user_ids) do |key|
59
- deleted = !activity(key).empty?
60
- wipe_user_record(key)
61
- deleted
62
- end
63
- end
64
-
65
- def paginate(user_ids:,
66
- page:,
67
- per_page: feed.per_page,
68
- with_total: false,
69
- reset_last_read: false)
70
-
71
- reset_last_read_value(user_ids: user_ids, at: reset_last_read) if reset_last_read
72
-
73
- with_response_batched(user_ids) do |key|
74
- activity = activity(key)
75
- result = page && page > 0 ? activity[((page - 1) * per_page)...(page * per_page)] : activity
76
- with_total ? { events: result, total_count: activity.length } : result
77
- end
78
- end
79
-
80
- def fetch(user_ids:, since: nil, reset_last_read: false)
81
- response = with_response_batched(user_ids) do |key|
82
- if since == :unread
83
- activity(key).reject { |event| event.at < user_record(key).last_read.to_f }
84
- elsif since
85
- activity(key).reject { |event| event.at < since.to_f }
86
- else
87
- activity(key)
88
- end
89
- end
90
- reset_last_read_value(user_ids: user_ids, at: reset_last_read) if reset_last_read
91
-
92
- response
93
- end
94
-
95
- def reset_last_read(user_ids:, at: Time.now)
96
- with_response_batched(user_ids) do |key|
97
- user_record(key)[:last_read] = at
98
- at
99
- end
100
- end
101
-
102
- def total_count(user_ids:)
103
- with_response_batched(user_ids) do |key|
104
- activity(key).size
105
- end
106
- end
107
-
108
- def unread_count(user_ids:)
109
- with_response_batched(user_ids) do |key|
110
- activity(key).count { |event| event.at > user_record(key).last_read.to_f }
111
- end
112
- end
113
-
114
- def last_read(user_ids:)
115
- with_response_batched(user_ids) do |key|
116
- user_record(key).last_read
117
- end
118
- end
119
-
120
- def total_memory_bytes
121
- ObjectSpace.memsize_of(h)
122
- end
123
-
124
- def total_users
125
- h.size
126
- end
127
-
128
- private
129
-
130
- #===================================================================
131
- # Methods below operate on a single user only
132
- #
133
-
134
- def changed_activity_size?(key)
135
- ua = activity(key)
136
- size_before = ua.size
137
- yield(key, ua)
138
- size_after = activity(key).size
139
- (size_before > size_after)
140
- end
141
-
142
- def create_user_record
143
- Hashie::Mash.new(
144
- { last_read: 0, activity: SortedSet.new }
145
- )
146
- end
147
-
148
- def user_record(key)
149
- h[key.data] ||= create_user_record
150
- end
151
-
152
- def wipe_user_record(key)
153
- h[key.data] = create_user_record
154
- end
155
-
156
- def activity(key, event = nil)
157
- user_record(key)[:activity] << event if event
158
- user_record(key)[:activity].to_a
159
- end
160
-
161
- def add_event(event, key)
162
- uas = user_record(key)[:activity]
163
- if uas.include?(event)
164
- false
165
- else
166
- uas << event.dup
167
- if uas.size > feed.max_size
168
- uas.delete(uas.to_a.last)
169
- end
170
- true
171
- end
172
- end
173
-
174
- def __last_read(key, _value = nil)
175
- user_record(key)[:last_read]
176
- end
177
-
178
- def __delete(key, event)
179
- user_record(key)[:activity].delete(event)
180
- end
181
-
182
- def create_event(*args, **opts)
183
- ::SimpleFeed::Event.new(*args, **opts)
184
- end
185
- end
186
- end
187
- end
188
- end