simple-feed 1.0.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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +30 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +1156 -0
  6. data/.travis.yml +14 -0
  7. data/.yardopts +3 -0
  8. data/Gemfile +4 -0
  9. data/Guardfile +18 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +457 -0
  12. data/Rakefile +16 -0
  13. data/bin/console +14 -0
  14. data/bin/setup +8 -0
  15. data/examples/hash_provider_example.rb +24 -0
  16. data/examples/redis_provider_example.rb +28 -0
  17. data/examples/shared/provider_example.rb +66 -0
  18. data/lib/simple-feed.rb +1 -0
  19. data/lib/simple_feed.rb +1 -0
  20. data/lib/simplefeed.rb +61 -0
  21. data/lib/simplefeed/activity/base.rb +14 -0
  22. data/lib/simplefeed/activity/multi_user.rb +71 -0
  23. data/lib/simplefeed/activity/single_user.rb +70 -0
  24. data/lib/simplefeed/dsl.rb +38 -0
  25. data/lib/simplefeed/dsl/activities.rb +70 -0
  26. data/lib/simplefeed/dsl/formatter.rb +109 -0
  27. data/lib/simplefeed/event.rb +87 -0
  28. data/lib/simplefeed/feed.rb +78 -0
  29. data/lib/simplefeed/providers.rb +45 -0
  30. data/lib/simplefeed/providers/base/provider.rb +84 -0
  31. data/lib/simplefeed/providers/hash.rb +8 -0
  32. data/lib/simplefeed/providers/hash/paginator.rb +31 -0
  33. data/lib/simplefeed/providers/hash/provider.rb +169 -0
  34. data/lib/simplefeed/providers/proxy.rb +38 -0
  35. data/lib/simplefeed/providers/redis.rb +9 -0
  36. data/lib/simplefeed/providers/redis/boot_info.yml +99 -0
  37. data/lib/simplefeed/providers/redis/driver.rb +158 -0
  38. data/lib/simplefeed/providers/redis/provider.rb +255 -0
  39. data/lib/simplefeed/providers/redis/stats.rb +85 -0
  40. data/lib/simplefeed/providers/serialization/key.rb +82 -0
  41. data/lib/simplefeed/response.rb +77 -0
  42. data/lib/simplefeed/version.rb +3 -0
  43. data/man/running-the-example.png +0 -0
  44. data/man/sf-example.png +0 -0
  45. data/simple-feed.gemspec +44 -0
  46. metadata +333 -0
@@ -0,0 +1,109 @@
1
+ require 'simplefeed/dsl'
2
+ require 'simplefeed/activity/single_user'
3
+ require 'simplefeed/activity/multi_user'
4
+ module SimpleFeed
5
+ module DSL
6
+ module Formatter
7
+ include SimpleFeed::DSL
8
+
9
+ attr_accessor :activity, :feed
10
+
11
+ def color_dump(_activity = activity)
12
+ _activity = if _activity.is_a?(SimpleFeed::Activity::SingleUser)
13
+ _activity.feed.activity([_activity.user_id])
14
+ else
15
+ _activity
16
+ end
17
+ _puts
18
+
19
+ header do
20
+ field('Feed Name', feed.name, "\n")
21
+ field('Provider', feed.provider.provider.class, "\n")
22
+ field('Max Size', feed.max_size, "\n")
23
+ end
24
+
25
+ with_activity(_activity) do
26
+ _activity.each do |user_id|
27
+ _last_event_at = nil
28
+ _last_read = (last_read[user_id] || 0.0).to_f
29
+
30
+ [['User ID', user_id, "\n"],
31
+ ['Activities', sprintf('%d total, %d unread', total_count[user_id], unread_count[user_id]), "\n"],
32
+ ['Last Read', _last_read ? Time.at(_last_read) : 'N/A'],
33
+ ].each do |field, value, *args|
34
+ field(field, value, *args)
35
+ end
36
+
37
+ _puts; hr '¨'
38
+
39
+ _events = fetch[user_id]
40
+ _events_count = _events.size
41
+ _events.each_with_index do |_event, _index|
42
+
43
+ if _last_event_at.nil? && _event.at < _last_read
44
+ print_last_read_separator(_last_read)
45
+ elsif _last_event_at && _last_read < _last_event_at && _last_read > _event.at
46
+ print_last_read_separator(_last_read)
47
+ end
48
+
49
+ _last_event_at = _event.at # float
50
+ _print "[%2d] %16s %s\n", _index, _event.time.strftime(TIME_FORMAT).blue.bold, _event.value
51
+ if _index == _events_count - 1 && _last_read < _event.at
52
+ print_last_read_separator(_last_read)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def print_last_read_separator(lr)
60
+ _print ">>>> %16s <<<< last read\n", Time.at(lr).strftime(TIME_FORMAT).red.bold
61
+ end
62
+ end
63
+
64
+ @print_method = :printf
65
+
66
+ class << self
67
+ attr_accessor :print_method
68
+ end
69
+
70
+ def _print(*args, **opts, &block)
71
+ send(SimpleFeed::DSL.print_method, *args, **opts, &block)
72
+ end
73
+
74
+ def _puts(*args)
75
+ send(SimpleFeed::DSL.print_method, "\n" + args.join)
76
+ end
77
+
78
+ def field_label(text)
79
+ sprintf ' %20s ', text
80
+ end
81
+
82
+ TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
83
+
84
+ def field_value(value)
85
+ case value
86
+ when Numeric
87
+ sprintf '%-20d', value
88
+ when Time
89
+ sprintf '%-30s', value.strftime(TIME_FORMAT)
90
+ else
91
+ sprintf '%-20s', value.to_s
92
+ end
93
+ end
94
+
95
+ def field(label, value, sep = '')
96
+ _print field_label(label).italic + field_value(value).cyan.bold + sep
97
+ end
98
+
99
+ def hr(char = '—')
100
+ _print (char * 75 + "\n").magenta
101
+ end
102
+
103
+ def header(message = nil)
104
+ hr
105
+ block_given? ? yield : _print(message.capitalize.magenta.bold + "\n")
106
+ hr
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,87 @@
1
+ require 'json'
2
+
3
+ module SimpleFeed
4
+ class Event
5
+ attr_accessor :value, :at
6
+ include Comparable
7
+
8
+ def initialize(*args, value: nil, at: Time.now)
9
+ if args && args.size > 0
10
+ self.value = args[0]
11
+ self.at = args[1] || at
12
+ else
13
+ self.value = value
14
+ self.at = at
15
+ end
16
+
17
+ self.at = self.at.to_f
18
+
19
+ validate!
20
+ end
21
+
22
+ def time
23
+ Time.at(at)
24
+ end
25
+
26
+ def <=>(other)
27
+ -self.at <=> -other.at
28
+ end
29
+
30
+ def eql?(other)
31
+ self == other
32
+ end
33
+
34
+ def ==(other)
35
+ other.is_a?(SimpleFeed::Event) &&
36
+ self.value == other.value
37
+ end
38
+
39
+ def to_h
40
+ { value: value, at: at, time: time }
41
+ end
42
+
43
+ def hash
44
+ self.value.hash
45
+ end
46
+
47
+ def to_json
48
+ to_h.to_json
49
+ end
50
+
51
+ def to_yaml
52
+ YAML.dump(to_h)
53
+ end
54
+
55
+ def to_s
56
+ "Event Value: [#{value}], epoch: [#{at}], time: [#{time}]"
57
+ end
58
+
59
+ def to_color_s
60
+ counter = 0
61
+ to_s.gsub(/,/, '-').split(/[\[\]]/).map do |word|
62
+ counter += 1
63
+ counter.even? ? word.bold.yellow + "\n": word.blue
64
+ end.join('').gsub(/[\n] /, '')
65
+ end
66
+
67
+ def inspect
68
+ super
69
+ end
70
+
71
+
72
+ private
73
+
74
+ def validate!
75
+ unless self.value && self.at
76
+ raise ArgumentError, "Required arguments missing, value=[#{value}], at=[#{at}]"
77
+ end
78
+ end
79
+
80
+ def copy(&block)
81
+ copy = self.clone
82
+ copy.instance_eval(&block)
83
+ copy
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,78 @@
1
+ require_relative 'providers'
2
+ require_relative 'activity/base'
3
+ module SimpleFeed
4
+ class Feed
5
+
6
+ attr_accessor :per_page, :max_size, :batch_size, :meta, :namespace
7
+ attr_reader :name
8
+
9
+ SimpleFeed::Providers.define_provider_methods(self) do |feed, method, opts, &block|
10
+ feed.provider.send(method, **opts, &block)
11
+ end
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ @name = name.underscore.to_sym unless name.is_a?(Symbol)
16
+ # set the defaults if not passed in
17
+ @meta = {}
18
+ @namespace = nil
19
+ @per_page ||= 50
20
+ @max_size ||= 1000
21
+ @batch_size||= 10
22
+ @proxy = nil
23
+ end
24
+
25
+ def provider=(definition)
26
+ @proxy = Providers::Proxy.from(definition)
27
+ @proxy.feed = self
28
+ @proxy
29
+ end
30
+
31
+ def provider
32
+ @proxy
33
+ end
34
+
35
+ def provider_type
36
+ SimpleFeed::Providers::Base::Provider.class_to_registry(@proxy.provider.class)
37
+ end
38
+
39
+ def user_activity(user_id)
40
+ Activity::SingleUser.new(user_id: user_id, feed: self)
41
+ end
42
+
43
+ def users_activity(user_ids)
44
+ Activity::MultiUser.new(user_ids: user_ids, feed: self)
45
+ end
46
+
47
+ # Depending on the argument returns either +SingleUserActivity+ or +MultiUserActivity+
48
+ def activity(one_or_more_users)
49
+ one_or_more_users.is_a?(Array) ?
50
+ users_activity(one_or_more_users) :
51
+ user_activity(one_or_more_users)
52
+ end
53
+
54
+ # @deprecated Please use {#activity} instead
55
+ def for(*args)
56
+ STDERR.puts 'WARNING: method #for is deprecated, please use #activity'
57
+ activity(*args)
58
+ end
59
+
60
+ def configure(hash = {})
61
+ SimpleFeed.symbolize!(hash)
62
+ class_attrs.each do |attr|
63
+ self.send("#{attr}=", hash[attr]) if hash.key?(attr)
64
+ end
65
+ yield self if block_given?
66
+ end
67
+
68
+ def eql?(other)
69
+ other.class == self.class &&
70
+ %i(per_page max_size name).all? { |m| self.send(m).equal?(other.send(m)) } &&
71
+ self.provider.provider.class == other.provider.provider.class
72
+ end
73
+
74
+ def class_attrs
75
+ SimpleFeed.class_attributes(self.class)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'providers/serialization/key'
2
+ require_relative 'providers/proxy'
3
+
4
+ module SimpleFeed
5
+ module Providers
6
+ @registry = {}
7
+
8
+ def self.registry
9
+ @registry
10
+ end
11
+
12
+ def self.register(provider_name, provider_class)
13
+ self.registry[provider_name] = provider_class
14
+ end
15
+
16
+ def self.key(*args)
17
+ SimpleFeed::Providers::Serialization::Key.new(*args)
18
+ end
19
+
20
+ # These methods must be implemented by each Provider, and operation on a given
21
+ # set of users passed via the user_ids: parameter.
22
+ ACTIVITY_METHODS = %i(store delete delete_if wipe reset_last_read last_read paginate fetch total_count unread_count)
23
+
24
+ # These methods must be implemented in order to gather statistics about each provider's
25
+ # memory consumption and state.
26
+ FEED_METHODS = %i(total_memory_bytes total_users)
27
+
28
+ REQUIRED_METHODS = ACTIVITY_METHODS + FEED_METHODS
29
+
30
+ def self.define_provider_methods(klass, prefix = nil, &block)
31
+ # Methods on the class instance
32
+ klass.class_eval do
33
+ SimpleFeed::Providers::REQUIRED_METHODS.each do |m|
34
+ method_name = prefix ? "#{prefix}_#{m.to_s}".to_sym : m
35
+ define_method(method_name) do |*args, **opts, &b|
36
+ block.call(self, m, *args, **opts, &b)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ require_relative 'providers/hash'
45
+ require_relative 'providers/redis'
@@ -0,0 +1,84 @@
1
+ require 'simplefeed/providers/serialization/key'
2
+
3
+ module SimpleFeed
4
+ module Providers
5
+ class NotImplementedError < ::StandardError
6
+ def initialize(klass, method)
7
+ super("Class #{klass.name} did not implement abstract method #{method}, but was called.")
8
+ end
9
+ end
10
+
11
+ module Base
12
+ class Provider
13
+ attr_accessor :feed
14
+
15
+ def self.class_to_registry(klass)
16
+ klass.name.split('::').delete_if { |n| %w(SimpleFeed Providers Provider).include?(n) }.compact.last.downcase.to_sym
17
+ end
18
+
19
+ def self.inherited(klass)
20
+ SimpleFeed::Providers.register(class_to_registry(klass), klass)
21
+ end
22
+
23
+ # SimpleFeed::Providers::REQUIRED_METHODS.each do |m|
24
+ # define_method(m) do |*|
25
+ # raise ::SimpleFeed::Providers::NotImplementedError.new(self, m)
26
+ # end
27
+ # end
28
+
29
+ protected
30
+
31
+ def tap(value)
32
+ yield
33
+ value
34
+ end
35
+
36
+ def key(user_id)
37
+ ::SimpleFeed::Providers.key(user_id, feed.namespace)
38
+ end
39
+
40
+ def to_array(user_ids)
41
+ user_ids.is_a?(Array) ? user_ids : [user_ids]
42
+ end
43
+
44
+ def batch_size
45
+ feed.batch_size
46
+ end
47
+
48
+ def with_response_batched(user_ids, external_response = nil)
49
+ with_response(external_response) do |response|
50
+ batch(user_ids) do |key|
51
+ response.for(key.user_id) { yield(key, response) }
52
+ end
53
+ end
54
+ end
55
+
56
+ def batch(user_ids)
57
+ to_array(user_ids).each_slice(batch_size) do |batch|
58
+ batch.each do |user_id|
59
+ yield(key(user_id))
60
+ end
61
+ end
62
+ end
63
+
64
+ def with_response(response = nil)
65
+ response ||= SimpleFeed::Response.new
66
+ yield(response)
67
+ if self.respond_to?(:transform_response)
68
+ response.transform do |user_id, result|
69
+ # calling into a subclass
70
+ transform_response(user_id, result)
71
+ end
72
+ end
73
+ response
74
+ end
75
+
76
+ def with_result
77
+ result = yield
78
+ result = transform_response(nil, result) if self.respond_to?(:transform_response)
79
+ result
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,8 @@
1
+ module SimpleFeed
2
+ module Providers
3
+ module Hash
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'hash/provider'
@@ -0,0 +1,31 @@
1
+ module SimpleFeed
2
+ module Providers
3
+ module Hash
4
+ # Include this module into any provider etc that has access to the +feed.fetch+
5
+ # methods, and it will provide +paginate+ method based on all.
6
+ #
7
+ # Of course this is not very efficient, because it requires fetching all events for the user.
8
+ module Paginator
9
+
10
+ def paginate(user_ids:, page: nil, per_page: feed.per_page, &block)
11
+ response = feed.fetch(user_ids: user_ids)
12
+ response = SimpleFeed::Response.new(response.to_h)
13
+ response.transform do |*, events|
14
+ paginate_items(order_events(events, &block), page: page, per_page: per_page)
15
+ end
16
+ end
17
+
18
+ def paginate_items(items, page: nil, per_page: nil)
19
+ (page && page > 0) ? items[((page - 1) * per_page)...(page * per_page)] : items
20
+ end
21
+
22
+ def order_events(events, &block)
23
+ return nil unless events
24
+ events.sort do |a, b|
25
+ block ? yield(a, b) : b.at <=> a.at
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end