simple-feed 2.1.0 → 3.0.1

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.

Potentially problematic release.


This version of simple-feed might be problematic. Click here for more details.

data/Rakefile CHANGED
@@ -11,7 +11,7 @@ def shell(*args)
11
11
  end
12
12
 
13
13
  task :permissions do
14
- shell('rm -rf pkg/ tmp/ coverage/')
14
+ shell('rm -rf pkg/')
15
15
  shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
16
16
  shell("find . -type d -exec chmod o+x,g+x {} \\;")
17
17
  end
@@ -0,0 +1,28 @@
1
+ codecov:
2
+ require_ci_to_pass: no
3
+
4
+ notify:
5
+ after_n_builds: 30
6
+ wait_for_ci: yes
7
+
8
+ parsers:
9
+ v1:
10
+ include_full_missed_files: true # To use with Ruby so we see files that have NO tests written
11
+
12
+ coverage:
13
+ precision: 1
14
+ status:
15
+ project:
16
+ default: off
17
+ simplefeed:
18
+ target: 90%
19
+ threshold: 100%
20
+ informational: true
21
+ if_not_found: success
22
+ if_ci_failed: error
23
+ paths:
24
+ - lib/
25
+ flags:
26
+ simplefeed:
27
+ paths:
28
+ - lib/
@@ -1,3 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
1
4
  $LOAD_PATH.unshift File.expand_path('../../../lib', __FILE__)
2
5
 
3
6
  # Please set @feed in the enclosing context
@@ -14,8 +17,6 @@ srand(Time.now.to_i % 100003)
14
17
  n % 2 == 0 ? UUID.generate : rand(100003)
15
18
  end
16
19
 
17
- pp @users
18
-
19
20
  @activity = @feed.activity(@users)
20
21
  @uid = @users.first
21
22
 
@@ -28,47 +29,64 @@ class Object
28
29
  end
29
30
 
30
31
  def p(*args)
31
- printf "%-40s %s\n", args[0].blue, args[1].bold.red
32
+ printf "%40s -> %s\n", args[0].strip.blue.bold, args[1].bold.red
32
33
  end
33
34
 
34
35
  with_activity(@activity) do
35
- header "#{@activity.feed.provider_type} provider example"
36
+ header "#{@activity.feed.provider_type} provider example".upcase,
37
+ "Starting with a blank feed, no items",
38
+ align: :center
36
39
 
37
40
  wipe
38
41
 
39
- store('value one') { p 'storing new value', 'value one' }
40
- store('value two') { p 'storing new value', 'value two' }
41
- store('value three') { p 'storing new value', 'value three' }
42
-
43
- hr
42
+ store('value one') { p 'storing new value', 'value one' }
43
+ store('value two') { p 'storing new value', 'value two' }
44
+ store('value three') { p 'storing new value', 'value three' }
44
45
 
45
- total_count { |r| p 'total_count is now', "#{r[@uid]._v}" }
46
- unread_count { |r| p 'unread_count is now', "#{r[@uid]._v}" }
46
+ total_count { |r| p 'total_count is now', "#{r[@uid]._v}" }
47
+ unread_count { |r| p 'unread_count is now', "#{r[@uid]._v}" }
47
48
 
48
- header 'paginate(page: 1, per_page: 2)'
49
+ header 'activity.paginate(page: 1, per_page: 2)'
49
50
  paginate(page: 1, per_page: 2) { |r| puts r[@uid].map(&:to_color_s) }
50
- header 'paginate(page: 2, per_page: 2, reset_last_read: true)'
51
+
52
+ header 'activity.paginate(page: 2, per_page: 2, reset_last_read: true)'
51
53
  paginate(page: 2, per_page: 2, reset_last_read: true) { |r| puts r[@uid].map(&:to_color_s) }
52
54
 
53
- hr
55
+ total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
56
+ unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
54
57
 
55
- total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
56
- unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
57
-
58
- hr
59
- store('value four') { p 'storing', 'value four' }
58
+ store('value four') { p 'storing', 'value four' }
60
59
 
61
60
  color_dump
62
61
 
63
62
  header 'deleting'
64
63
 
65
- delete('value three') { p 'deleting', 'value three' }
66
- total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
67
- unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
64
+ delete('value three') { p 'deleting', 'value three' }
65
+
66
+ total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
67
+ unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
68
+
68
69
  hr
69
- delete('value four') { p 'deleting', 'value four' }
70
- total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
71
- unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
72
70
 
71
+ delete('value four') { p 'deleting', 'value four' }
72
+ total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
73
+ unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
74
+
75
+ puts
76
+ end
77
+
78
+ notes = [
79
+ 'Thanks for trying SimpleFeed!', 'For any questions, reach out to',
80
+ 'kigster@gmail.com',
81
+ ]
82
+
83
+ unless ENV['REDIS_DEBUG']
84
+ notes << [
85
+ '———',
86
+ 'To see REDIS commands, set REDIS_DEBUG environment variable to true,',
87
+ 'and re-run the example.'
88
+ ]
73
89
  end
74
90
 
91
+ header notes.flatten,
92
+ align: :center
@@ -11,43 +11,48 @@ Hashie.logger = Logger.new(nil)
11
11
  require 'simplefeed/providers/redis'
12
12
  require 'simplefeed/providers/hash'
13
13
  require 'simplefeed/dsl'
14
+ require 'simplefeed/feed'
14
15
 
15
16
  # Main namespace module for the SimpleFeed gem. It provides several shortcuts and entry
16
17
  # points into the library, such as ability to define and fetch new feeds via +define+,
17
18
  # and so on.
18
19
  module SimpleFeed
20
+ TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
21
+
19
22
  @registry = {}
20
23
 
21
24
  class << self
22
- # @return [Hash<Symbol, Feed>] the registry of the defined feeds
25
+ # @return Hash<Symbol, Feed> the registry of the defined feeds
23
26
  attr_reader :registry
24
27
 
25
- # @param name [Symbol] feed name
26
- # @param options [Hash] any key-value pairs to set on the feed
28
+ # @param <Symbol> name of the feed
29
+ # @param <Hash> options any key-value pairs to set on the feed
27
30
  #
28
- # @return [Feed] the feed with the given name, and defined via options and a block
29
- def define(name, **options, &block)
31
+ # @return [Feed] the feed with the given name, and defined via options and a block
32
+ def define(name, **options)
30
33
  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)
34
+
35
+ registry[name] ||= Feed.new(name)
36
+ registry[name].tap do |feed|
37
+ feed.configure(options) do
38
+ yield(feed) if block_given?
39
+ end
34
40
  end
35
- registry[name] = feed
36
- feed
37
41
  end
38
42
 
39
- # @return [Feed] the pre-defined feed with the given name
43
+ # @param [Symbol] name
44
+ # @return <Feed> the pre-defined feed with the given name
40
45
  def get(name)
41
46
  registry[name.to_sym]
42
47
  end
43
48
 
44
49
  # A factory method that constructs an instance of a provider based on the provider name and arguments.
45
50
  #
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
51
+ # @param <Symbol> provider_name short name of the provider, eg, :redis, :hash, etc.
52
+ # @param <Array> args constructor array arguments of the provider
53
+ # @param <Hash, NilClass> opts constructor hash arguments of the provider
49
54
  #
50
- # @return [Provider]
55
+ # @return <Provider>
51
56
  def provider(provider_name, *args, **opts, &block)
52
57
  provider_class = SimpleFeed::Providers.registry[provider_name]
53
58
  raise ArgumentError, "No provider named #{provider_name} was found, #{SimpleFeed::Providers.registry.inspect}" unless provider_class
@@ -74,8 +74,8 @@ module SimpleFeed
74
74
  end
75
75
 
76
76
  def initialize(user_id:, feed:)
77
- @feed = feed
78
- @user_id = user_id
77
+ @feed = feed
78
+ @user_id = user_id
79
79
  self.user_activity = MultiUser.new(feed: feed, user_ids: [user_id])
80
80
  end
81
81
  end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tty-box'
4
+ require 'tty-screen'
3
5
  require 'simplefeed/dsl'
4
6
  require 'simplefeed/activity/single_user'
5
7
  require 'simplefeed/activity/multi_user'
8
+
6
9
  module SimpleFeed
7
10
  module DSL
8
11
  # This module exports method #color_dump which receives an activity and
@@ -18,27 +21,30 @@ module SimpleFeed
18
21
  this_activity.feed.activity([this_activity.user_id])
19
22
  else
20
23
  this_activity
21
- end
24
+ end
22
25
  _puts
23
26
 
24
- header do
25
- field('Feed Name', feed.name, "\n")
26
- field('Provider', feed.provider.provider.class, "\n")
27
- field('Max Size', feed.max_size, "\n")
27
+ feed_header(feed) do
28
+ [
29
+ field('Feed Name', feed.name, "\n"),
30
+ field('Provider', feed.provider.provider.class, "\n"),
31
+ field('Max Size', feed.max_size, "\n")
32
+ ]
28
33
  end
29
34
 
30
35
  with_activity(this_activity) do
31
- this_activity.each do |user_id|
36
+ this_activity.each_with_index do |user_id, index|
32
37
  this_last_event_at = nil
33
38
  this_last_read = (last_read[user_id] || 0.0).to_f
34
39
 
40
+ fields = []
35
41
  [['User ID', user_id, "\n"],
36
42
  ['Activities', sprintf('%d total, %d unread', total_count[user_id], unread_count[user_id]), "\n"],
37
43
  ['Last Read', this_last_read ? Time.at(this_last_read) : 'N/A'],].each do |field, value, *args|
38
- field(field, value, *args)
44
+ fields << field(field, value, *args)
39
45
  end
40
46
 
41
- _puts; hr '¨'
47
+ header(title: { top_center: " « User Activity #{index + 1} » " }) { fields.map(&:green) }
42
48
 
43
49
  this_events = fetch[user_id]
44
50
  this_events_count = this_events.size
@@ -50,7 +56,7 @@ module SimpleFeed
50
56
  end
51
57
 
52
58
  this_last_event_at = _event.at # float
53
- _print "[%2d] %16s %s\n", _index, _event.time.strftime(TIME_FORMAT).blue.bold, _event.value
59
+ output "[%2d] %16s %s\n", _index, _event.time.strftime(TIME_FORMAT).blue.bold, _event.value
54
60
  if _index == this_events_count - 1 && this_last_read < _event.at
55
61
  print_last_read_separator(this_last_read)
56
62
  end
@@ -60,17 +66,18 @@ module SimpleFeed
60
66
  end
61
67
 
62
68
  def print_last_read_separator(lr)
63
- _print ">>>> %16s <<<< last read\n", Time.at(lr).strftime(TIME_FORMAT).red.bold
69
+ output ">>>> %16s <<<< last read\n", Time.at(lr).strftime(TIME_FORMAT).red.bold
64
70
  end
65
71
  end
66
72
 
73
+ # This allows redirecting output in tests.
67
74
  @print_method = :printf
68
75
 
69
76
  class << self
70
77
  attr_accessor :print_method
71
78
  end
72
79
 
73
- def _print(*args, **opts, &block)
80
+ def output(*args, **opts, &block)
74
81
  send(SimpleFeed::DSL.print_method, *args, **opts, &block)
75
82
  end
76
83
 
@@ -82,8 +89,6 @@ module SimpleFeed
82
89
  sprintf ' %20s ', text
83
90
  end
84
91
 
85
- TIME_FORMAT = '%Y-%m-%d %H:%M:%S.%L'
86
-
87
92
  def field_value(value)
88
93
  case value
89
94
  when Numeric
@@ -95,22 +100,52 @@ module SimpleFeed
95
100
  end
96
101
  end
97
102
 
98
- def field(label, value, sep = '')
99
- _print field_label(label).italic + field_value(value).cyan.bold + sep
103
+ def field(label, value, _sep = '')
104
+ field_label(label) + ' -> ' + field_value(value)
105
+ end
106
+
107
+ def hr
108
+ output hr_string.magenta
109
+ end
110
+
111
+ def hr_string
112
+ '―' * width + "\n"
113
+ end
114
+
115
+ def width
116
+ @width ||= TTY::Screen.width - 5
100
117
  end
101
118
 
102
- def hr(char = '—')
103
- _print(_hr(char).magenta)
119
+ def feed_header(feed, &block)
120
+ header title: { top_left: " « #{feed.name.capitalize} Feed » " },
121
+ border: :thick,
122
+ style: {
123
+ fg: :bright_red,
124
+ border: { fg: :white }
125
+ }, &block
104
126
  end
105
127
 
106
- def _hr(char = '—')
107
- char * 75 + "\n"
128
+ def header(*args, **opts)
129
+ message = args.join("\n")
130
+ msg = block_given? ? (yield || message) : message + "\n"
131
+ box = TTY::Box.frame(box_config(**opts)) { Array(msg).join("\n") }
132
+ output "\n#{box}"
108
133
  end
109
134
 
110
- def header(message = nil)
111
- _print(_hr.green.bold)
112
- block_given? ? yield : _print(message.green.italic + "\n")
113
- _print(_hr.green.bold)
135
+ private
136
+
137
+ def box_config(**opts)
138
+ {
139
+ width: width,
140
+ align: :left,
141
+ padding: [0, 3],
142
+ style: {
143
+ fg: :bright_yellow,
144
+ border: {
145
+ fg: :bright_magenta,
146
+ }
147
+ }
148
+ }.merge(opts)
114
149
  end
115
150
  end
116
151
  end
@@ -7,6 +7,21 @@ module SimpleFeed
7
7
  attr_accessor :value, :at
8
8
  include Comparable
9
9
 
10
+ class << self
11
+ attr_accessor :is_time
12
+ end
13
+
14
+ # This proc can be overridden in a configuration if needed.
15
+ # @example To always assume this is time, set it like so,
16
+ # before defining your feeds.
17
+ #
18
+ # SimpleFeed::Event.is_time = ->(*) { true }
19
+ #
20
+ self.is_time = ->(float) {
21
+ # assume it's time if epoch is > June 1974 and < December 2040.
22
+ float < 2_237_932_800.0 && float > 139_276_800.0
23
+ }
24
+
10
25
  def initialize(*args, value: nil, at: Time.now)
11
26
  if args && !args.empty?
12
27
  self.value = args[0]
@@ -22,7 +37,11 @@ module SimpleFeed
22
37
  end
23
38
 
24
39
  def time
40
+ return nil unless Event.is_time[at]
41
+
25
42
  Time.at(at)
43
+ rescue ArgumentError
44
+ nil
26
45
  end
27
46
 
28
47
  def <=>(other)
@@ -38,10 +57,6 @@ module SimpleFeed
38
57
  self.value == other.value
39
58
  end
40
59
 
41
- def to_h
42
- { value: value, at: at, time: time }
43
- end
44
-
45
60
  def hash
46
61
  self.value.hash
47
62
  end
@@ -54,16 +69,38 @@ module SimpleFeed
54
69
  YAML.dump(to_h)
55
70
  end
56
71
 
72
+ def to_h
73
+ return @to_h if @to_h
74
+
75
+ @to_h ||= { value: value, at: at }
76
+ @to_h.merge!(time: time) if time
77
+ @to_h
78
+ end
79
+
57
80
  def to_s
58
- "<SimpleFeed::Event: value='#{value}', at='#{at}', time='#{time}'>"
81
+ return @to_s if @to_s
82
+
83
+ output = StringIO.new
84
+ output.print "<SimpleFeed::Event: "
85
+ output.print(time.nil? ? "[#{at}]" : "[#{time&.strftime(::SimpleFeed::TIME_FORMAT)}]")
86
+ output.print " -> [#{value}] "
87
+ @to_s = output.string
59
88
  end
60
89
 
90
+ COLOR_MAP = {
91
+ 1 => ->(word) { word.green.bold },
92
+ 3 => ->(word) { word.yellow.bold },
93
+ }.freeze
94
+
61
95
  def to_color_s
62
- counter = 0
63
- to_s.split(/[']/).map do |word|
64
- counter += 1
65
- counter.even? ? word.yellow.bold : word.blue
66
- end.join('')
96
+ return @to_color_s if @to_color_s
97
+
98
+ output = StringIO.new
99
+ to_s.split(/[\[\]]/).each_with_index do |word, index|
100
+ output.print(COLOR_MAP[index]&.call(word) || word.cyan)
101
+ end
102
+ output.print '>'
103
+ @to_color_s = output.string
67
104
  end
68
105
 
69
106
  def inspect