simple-feed 2.1.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.

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