simple-feed 2.1.0 → 3.1.2

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