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.
- checksums.yaml +4 -4
- data/.envrc +3 -0
- data/.github/workflows/rubocop.yml +20 -20
- data/.github/workflows/ruby.yml +19 -20
- data/.gitignore +2 -1
- data/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +102 -33
- data/.travis.yml +3 -13
- data/CHANGELOG.md +120 -0
- data/Gemfile +2 -0
- data/README.adoc +197 -76
- data/Rakefile +1 -1
- data/codecov.yml +28 -0
- data/examples/shared/provider_example.rb +43 -25
- data/lib/simplefeed/activity/single_user.rb +2 -2
- data/lib/simplefeed/dsl/formatter.rb +67 -28
- data/lib/simplefeed/event.rb +48 -10
- data/lib/simplefeed/feed.rb +20 -9
- data/lib/simplefeed/providers/base/provider.rb +1 -1
- data/lib/simplefeed/providers/key.rb +57 -42
- data/lib/simplefeed/providers/proxy.rb +20 -7
- data/lib/simplefeed/providers/redis/driver.rb +2 -4
- data/lib/simplefeed/providers/redis/provider.rb +6 -6
- data/lib/simplefeed/providers.rb +0 -1
- data/lib/simplefeed/response.rb +3 -5
- data/lib/simplefeed/version.rb +1 -1
- data/lib/simplefeed.rb +20 -16
- data/man/running-example-redis-debug.png +0 -0
- data/man/running-example.png +0 -0
- data/man/sf-color-dump.png +0 -0
- data/simple-feed.gemspec +7 -2
- metadata +67 -13
- data/examples/hash_provider_example.rb +0 -24
- data/lib/simplefeed/key/template.rb +0 -48
- data/lib/simplefeed/key/type.rb +0 -28
- data/lib/simplefeed/providers/hash/paginator.rb +0 -33
- data/lib/simplefeed/providers/hash/provider.rb +0 -188
data/README.adoc
CHANGED
@@ -6,64 +6,106 @@
|
|
6
6
|
|
7
7
|
== Scalable, Easy to Use Activity Feed Implementation.
|
8
8
|
|
9
|
-
|
10
|
-
image:https://img.shields.io/badge/license-MIT-blue.svg[MIT licensed,link=https://github.com/kigster/simple-feed/master/LICENSE.txt]
|
9
|
+
=== Build & Gem Status
|
11
10
|
|
12
|
-
image:https://
|
13
|
-
image:https://
|
11
|
+
image:https://img.shields.io/badge/license-MIT-blue.svg[MIT licensed,link=https://github.com/kigster/simple-feed/master/LICENSE.txt]
|
12
|
+
image:https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkigster%2Fsimple-feed.svg?type=shield[License Scan OK, link=https://app.fossa.com/projects/git%2Bgithub.com%2Fkigster%2Fsimple-feed?ref=badge_shield]
|
14
13
|
|
15
|
-
image:https://
|
16
|
-
image:https://
|
17
|
-
image:https://api.codeclimate.com/v1/badges/a11061820895fcde635e/test_coverage[Test Coverage,link=https://codeclimate.com/github/kigster/simple-feed/test_coverage]
|
14
|
+
image:https://github.com/kigster/simple-feed/workflows/RSpec/badge.svg?branch=master[RSpec,link=https://github.com/kigster/simple-feed/actions?query=workflow%3ARSpec]
|
15
|
+
image:https://github.com/kigster/simple-feed/workflows/Rubocop/badge.svg?branch=master[Rubocop,link=https://github.com/kigster/simple-feed/actions?query=workflow%3ARubocop]
|
18
16
|
|
17
|
+
image:https://img.shields.io/gem/v/simple-feed.svg[Gem Version,link=https://rubygems.org/gems/simple-feed]
|
19
18
|
image:http://inch-ci.org/github/kigster/simple-feed.svg?branch=master[Inline docs,link=http://inch-ci.org/github/kigster/simple-feed]
|
20
|
-
|
19
|
+
|
20
|
+
image:https://codecov.io/gh/kigster/simple-feed/branch/master/graph/badge.svg[Coverage,link=https://codecov.io/gh/kigster/simple-feed]
|
21
|
+
|
22
|
+
=== Test Coverage Map
|
23
|
+
|
24
|
+
image:https://codecov.io/gh/kigster/simple-feed/branch/master/graphs/sunburst.svg[Coverage Map,link=https://codecov.io/gh/kigster/simple-feed/branch/master]
|
21
25
|
|
22
26
|
IMPORTANT: Please read the (somewhat outdated) blog post http://kig.re/2017/02/19/feeding-frenzy-with-simple-feed-activity-feed-ruby-gem.html[Feeding Frenzy with SimpleFeed] launching this library. Please leave comments or questions in the discussion thread at the bottom of that post. Thanks!
|
23
27
|
|
24
28
|
If you like to see this project grow, your donation of any amount is much appreciated.
|
25
29
|
|
26
30
|
image::https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif[Donate,link=https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FSFYYNEQ8RKWU]
|
27
|
-
'''
|
28
31
|
|
29
|
-
This is a fast, pure-ruby implementation of an activity feed concept commonly used in social networking applications. The implementation is optimized for *read-time performance* and high concurrency (lots of users), and can be extended with custom backend providers.
|
32
|
+
This is a fast, pure-ruby implementation of an activity feed concept commonly used in social networking applications. The implementation is optimized for *read-time performance* and high concurrency (lots of users), and can be extended with custom backend providers. One data provider come bundled: the production-ready Redis provider.
|
30
33
|
|
31
34
|
*Important Notes and Acknowledgements:*
|
32
35
|
|
33
36
|
* SimpleFeed _does not depend on Ruby on Rails_ and is a *pure-ruby* implementation
|
34
|
-
* SimpleFeed requires
|
37
|
+
* SimpleFeed requires MRI Ruby 2.3 or later
|
35
38
|
* SimpleFeed is currently live in production
|
36
39
|
* SimpleFeed is open source thanks to the generosity of *http://simbi.com[Simbi, Inc]*.
|
37
40
|
|
38
41
|
== Features
|
39
42
|
|
40
|
-
SimpleFeed offers the following features:
|
43
|
+
SimpleFeed is a Ruby Library that can be plugged into any application to power a fast, Redis-based activity feed implementation so common on social networking sites. SimpleFeed offers the following features:
|
41
44
|
|
42
|
-
*
|
45
|
+
* Scalable and well performing Redis-based activity feed
|
43
46
|
|
44
|
-
* Scales to millions of users (
|
47
|
+
* Scales to millions of users (will need to use Twemproxy to shard across several Redis instances)
|
45
48
|
|
46
|
-
* Stores a fixed number of events
|
49
|
+
* Stores a fixed number of events for each unique "user" — the default is 1000. When the feed reaches 1001 events, the oldest event is offloaded from the activity.
|
47
50
|
|
48
|
-
*
|
51
|
+
* Thread-safe implementation.
|
49
52
|
|
50
|
-
*
|
53
|
+
* Zero assumptions about what you are storing: the "data" is just a string. Serialize it with JSON, Marshall, YAML, or whatever.
|
51
54
|
|
52
|
-
*
|
55
|
+
* You can create as many different types of feeds per application as you like. No Singletons are used.
|
53
56
|
|
54
|
-
*
|
57
|
+
* Customize mapping from `user_id` to the activity id based on your business logic (more on this later).
|
55
58
|
|
56
|
-
|
59
|
+
=== Publishing Events
|
57
60
|
|
58
|
-
|
61
|
+
Pushing events to the feed requires the following:
|
59
62
|
|
60
|
-
*
|
63
|
+
* An `Event` consisting of:
|
64
|
+
** String `data` that, most commonly, is a foreign key to a database table, but can really be anything you like.
|
65
|
+
** Float `at` (typically, the timestamp, but can be any `float` number)
|
66
|
+
* One or more user IDs, or event consumers: basically — who should see the event being published in their feed.
|
61
67
|
|
62
|
-
|
68
|
+
You publish an event by choosing a set of users whose feed should be updated. For example, were you re-implementing Twitter, your array of `user_ids` when publishing an event would be all followers of the Tweet's author. While the `data` would probably be the Tweet ID.
|
63
69
|
|
64
|
-
* Zero assumptions about what you are storing: the "data" is just a string. Serialize it with JSON, Marshall, YAML, or whatever.
|
65
70
|
|
66
|
-
|
71
|
+
NOTE: Publishing an event to the feeds of N users is roughly a O(N * log(N)) operation
|
72
|
+
|
73
|
+
=== Consuming Events (Reading / Rendering the Feed)
|
74
|
+
|
75
|
+
You can fetch the chronologically ordered events for a particular user, using:
|
76
|
+
|
77
|
+
* Methods on the `activity` such as `paginate`, `fetch`.
|
78
|
+
|
79
|
+
** Reading feed for one user (or one type of user) is a `O(1)` operation
|
80
|
+
|
81
|
+
* For each activity (user) you can fetch the `total_count` and the `unread_count` — the number of total and new items in the feed, where `unread_count` is computed since the user last reset their `read status`.
|
82
|
+
|
83
|
+
** Note: `total_count` can never exceed the maximum size of the feed that you configured. The default is 1000 items.
|
84
|
+
|
85
|
+
** The `last_read` timestamp can be automatically reset when the user is shown the feed via `paginate` method (whether or not its reset is controlled via a method argument).
|
86
|
+
|
87
|
+
=== Modifying User's Feed
|
88
|
+
|
89
|
+
For any given user, you can:
|
90
|
+
|
91
|
+
* Wipe their feed with `wipe`
|
92
|
+
|
93
|
+
* Selectively remove items from the feed with `delete_if`.
|
94
|
+
** For instance, if a user un-follows someone they shouldn't see their events anymore, so you'd have to call `delete_if` and remove any events published by the unfollowed user.
|
95
|
+
|
96
|
+
=== Aggregating Events
|
97
|
+
|
98
|
+
This is a feature planned for future versions.
|
99
|
+
|
100
|
+
Help us much appreciated, even if you are not a developer, but have a clear idea about how it should work.
|
101
|
+
|
102
|
+
== Commercial & Enterprise Support
|
103
|
+
|
104
|
+
Commercial Support plans are available for SimpleFeed through author's https://reinvent.one[ReinventONE Inc] consulting company. Please reach out to kig AT reinvent.one for more information.
|
105
|
+
|
106
|
+
== Usage
|
107
|
+
|
108
|
+
=== Example
|
67
109
|
|
68
110
|
Please read the additional documentation, including the examples, on the https://github.com/kigster/simple-feed/wiki[project's Github Wiki].
|
69
111
|
|
@@ -71,14 +113,11 @@ Below is a screen shot of an actual activity feed powered by this library.
|
|
71
113
|
|
72
114
|
image::https://raw.githubusercontent.com/kigster/simple-feed/master/man/activity-feed-action.png[usage]
|
73
115
|
|
74
|
-
|
116
|
+
=== Providers
|
75
117
|
|
76
118
|
A key concept to understanding SimpleFeed gem, is that of a _provider_, which is effectively a persistence implementation for the events belonging to each user.
|
77
119
|
|
78
|
-
|
79
|
-
|
80
|
-
* The production-ready `:redis` provider, which uses the https://redislabs.com/ebook/redis-in-action/part-2-core-concepts-2/chapter-3-commands-in-redis/3-5-sorted-sets[sorted set Redis data type] to store and fetch the events, scored by time (but not necessarily).
|
81
|
-
* The naïve `:hash` provider based on the ruby `Hash` class, that can be useful in unit tests, or in simple simulations.
|
120
|
+
One providers are supplied with this gem: the production-ready `:redis` provider, which uses the https://redislabs.com/ebook/redis-in-action/part-2-core-concepts-2/chapter-3-commands-in-redis/3-5-sorted-sets[sorted set Redis data type] to store and fetch the events, scored by time (but not necessarily).
|
82
121
|
|
83
122
|
You initialize a provider by using the `SimpleFeed.provider([Symbol])` method.
|
84
123
|
|
@@ -91,6 +130,7 @@ Below we configure a feed called `:newsfeed`, which in this example will be popu
|
|
91
130
|
require 'simplefeed'
|
92
131
|
|
93
132
|
# Let's define a Redis-based feed, and wrap Redis in a in a ConnectionPool.
|
133
|
+
|
94
134
|
SimpleFeed.define(:newsfeed) do |f|
|
95
135
|
f.provider = SimpleFeed.provider(:redis,
|
96
136
|
redis: -> { ::Redis.new },
|
@@ -108,55 +148,130 @@ After the feed is defined, the gem creates a similarly named method under the `S
|
|
108
148
|
|
109
149
|
You can also get a full list of currently defined feeds with `SimpleFeed.feed_names` method.
|
110
150
|
|
151
|
+
=== Reading from and writing to the feed
|
152
|
+
|
153
|
+
For the impatient, here is a quick way to get started with the `SimpleFeed`.
|
154
|
+
|
155
|
+
[source,ruby]
|
156
|
+
----
|
157
|
+
# Let's use the feed we defined earlier and create activity for all followers of the current user
|
158
|
+
publish_activity = SimpleFeed.newsfeed.activity(@current_user.followers.map(&:id))
|
159
|
+
|
160
|
+
# Store directly the value and the optional time stamp
|
161
|
+
publish_activity.store(value: 'hello', at: Time.now)
|
162
|
+
# => true # indicates that value 'hello' was not yet in the feed (all events must be unique)
|
163
|
+
|
164
|
+
# Or, using the event form:
|
165
|
+
publish_activity.store(event: SimpleFeed::Event.new('good bye', Time.now))
|
166
|
+
# => true
|
167
|
+
----
|
168
|
+
|
169
|
+
As we've added the two events for these users, we can now read them back, sorted by
|
170
|
+
the time and paginated:
|
171
|
+
|
172
|
+
[source,ruby]
|
173
|
+
----
|
174
|
+
# Let's grab the first follower
|
175
|
+
user_activity = SimpleFeed.newsfeed.activity(@current_user.followers.first.id)
|
176
|
+
|
177
|
+
# Now we can paginate the events, while resetting this user's last-read timestamp:
|
178
|
+
user_activity.paginate(page: 1, reset_last_read: true)
|
179
|
+
# [
|
180
|
+
# [0] #<SimpleFeed::Event: value=hello, at=1480475294.0579991>,
|
181
|
+
# [1] #<SimpleFeed::Event: value=good bye, at=1480472342.8979871>,
|
182
|
+
# ]
|
183
|
+
----
|
184
|
+
|
185
|
+
IMPORTANT: Note that we stored the activity by passing an array of users, but read the activity for just one user. This is how you'd use SimpleFeed most of the time, with the exception of the alternative mapping described below.
|
186
|
+
|
111
187
|
=== User IDs
|
112
188
|
|
113
|
-
In the
|
189
|
+
In the previous section you saw the examples of publishing events to many feeds, and then reading the activity for a given user.
|
114
190
|
|
115
|
-
SimpleFeed supports user IDs that are either numeric (integer) or string-based (eg, UUID).
|
191
|
+
SimpleFeed supports user IDs that are either numeric (integer) or string-based (eg, UUID). Numeric IDs are best for simplest cases, and are the most compact. String keys offer the most flexibility.
|
116
192
|
|
117
|
-
|
193
|
+
==== Activity Keys
|
118
194
|
|
119
|
-
|
195
|
+
In the next section we'll talk about generating `keys` from user_ids. We mean — Redis Hash keys that uniquely map a user (or a set of users) to the activity feed they should see.
|
196
|
+
|
197
|
+
There are up to two keys that are computed depending on the situation:
|
198
|
+
|
199
|
+
* `data_key` is used to store the actual feed events
|
200
|
+
* `meta_key` is used to store user's `last_read` status
|
120
201
|
|
121
202
|
==== Partitioning Schema
|
122
203
|
|
123
|
-
|
204
|
+
NOTE: This feature is only available in **SimpleFeed Version 3+**.
|
205
|
+
|
206
|
+
You can take advantage of string user IDs for situations where your feed requires keys to be composite for instance. Just remember that SimpleFeed does not care about what's in your user ID, and even what you call "a user". It's convenient to think of the activities in terms of users, because typically each user has a unique feed that only they see.
|
124
207
|
|
125
208
|
But you can just as easily use zip code as the unique activity ID, and create one feed of events per geographical location, that all folks living in that zip code share. But what about other countries?
|
126
209
|
|
127
|
-
Now you use
|
210
|
+
Now you use partitioning scheme: make the "user_id" argument a combination `iso_country_code.postal_code`, eg for San Francisco, you'd use `us.94107`, but for Australia you could use, eg `au.3148`.
|
128
211
|
|
129
|
-
|
212
|
+
==== Relationship between an Activity and a User
|
130
213
|
|
131
|
-
|
214
|
+
===== One to One
|
215
|
+
|
216
|
+
In the most common case, you will have one activity per user.
|
217
|
+
|
218
|
+
For instance, in the Twitter example, each Twitter user has a unique tweeter feed that only they see.
|
219
|
+
|
220
|
+
The events are published when someone posts a tweet, to the array of all users that follow the Tweet author.
|
221
|
+
|
222
|
+
===== One to Many
|
223
|
+
|
224
|
+
However, SimpleFeed supports one additional use-case, where you might have one activity shared among many users.
|
225
|
+
|
226
|
+
Imagine a service that notifies residents of important announcements based on user's zip code of residence.
|
227
|
+
|
228
|
+
We want this feed to work as follows:
|
229
|
+
|
230
|
+
* All users that share a zip-code should see the same exact feed.
|
231
|
+
* However, all users should never share the individual's `last_read` status: so if two people read the same activity from the same zip code, their `unread_count` should change independently.
|
232
|
+
|
233
|
+
In terms of the activity keys, this means:
|
234
|
+
|
235
|
+
* `data_key` should be based on the zip-code of each user, and be one to many with users.
|
236
|
+
* `meta_key` should be based on the user ID as we want it to be 1-1 with users.
|
237
|
+
|
238
|
+
To support this use-case, SimpleFeed supports two optional transformer lambdas that can be applied to each user object when computing their activity feed hash key:
|
132
239
|
|
133
240
|
[source,ruby]
|
134
241
|
----
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
#
|
140
|
-
|
141
|
-
# or equivalent:
|
142
|
-
@event = SimpleFeed::Event.new('hello', Time.now)
|
143
|
-
activity.store(event: @event)
|
144
|
-
# => false # false indicates that the same event is already in the feed.
|
242
|
+
SimpleFeed.define(:zipcode_alerts) do |f|
|
243
|
+
f.provider = SimpleFeed.provider(:redis, redis: -> { ::Redis.new }, pool_size: 10)
|
244
|
+
f.namespace = 'zc'
|
245
|
+
f.data_key_transformer = ->(user) { user.zip_code } # actual feed data is stored once per zip code
|
246
|
+
f.meta_key_transformer = ->(user) { user.id } # last_read status is stored once per user
|
247
|
+
end
|
145
248
|
----
|
146
249
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
250
|
+
When you publish events into this feed, you would need to provide `User` objects that all respond to `.zip_code` method (based on the above configuration). Since the data is only defined by Zip Code, you probably don't want to be publishing it via a giant array of users. Most likely, you'll want to publish event based on the zip code, and consume them based on the user ID.
|
251
|
+
|
252
|
+
To support this user-case, let's modify our transformer lambda (only the `data` one) as follows — so that it can support both the consuming read by a user case, and the publishing a feed by zip code case:
|
253
|
+
|
254
|
+
Alternatively, you could do something like this:
|
152
255
|
|
153
256
|
[source,ruby]
|
154
257
|
----
|
155
|
-
|
156
|
-
|
258
|
+
f.data_key_transformer = ->(entity) do
|
259
|
+
case entity
|
260
|
+
when User
|
261
|
+
entity.zip_code.to_i
|
262
|
+
when String # UUIDs
|
263
|
+
User.find(entity)&.zip_code.to_i
|
264
|
+
when ZipCode, Numeric
|
265
|
+
entity.to_i
|
266
|
+
else
|
267
|
+
raise ArgumentError, "Invalid type #{entity.class.name}"
|
268
|
+
end
|
269
|
+
end
|
157
270
|
----
|
158
271
|
|
159
|
-
|
272
|
+
Just make sure that your users always have `.zip_code` defined, and that `ZipCode.new(94107).to_i` returns exactly the same thing as `@user.zip_code.to_i` or your users won't see the feeds they are supposed to see.
|
273
|
+
|
274
|
+
=== The Two Forms of the Feed API
|
160
275
|
|
161
276
|
The feed API is offered in two forms:
|
162
277
|
|
@@ -170,9 +285,8 @@ The method names and signatures are the same. The only difference is in what the
|
|
170
285
|
|
171
286
|
Please see further below the details about the <<batch-api,Batch API>>.
|
172
287
|
|
173
|
-
+++<a name="single-user-api">++++++</a>+++
|
174
|
-
|
175
288
|
[discrete]
|
289
|
+
|
176
290
|
===== Single-User API
|
177
291
|
|
178
292
|
In the examples below we show responses based on a single-user usage. As previously mentioned, the multi-user usage is the same, except what the response values are, and is discussed further down below.
|
@@ -183,10 +297,10 @@ Let's take a look at a ruby session, which demonstrates return values of the fee
|
|
183
297
|
----
|
184
298
|
require 'simplefeed'
|
185
299
|
|
186
|
-
# Define the feed using
|
300
|
+
# Define the feed using Redis provider, which uses
|
187
301
|
# SortedSet to keep user's events sorted.
|
188
302
|
SimpleFeed.define(:followers) do |f|
|
189
|
-
f.provider = SimpleFeed.provider(:
|
303
|
+
f.provider = SimpleFeed.provider(:redis)
|
190
304
|
f.per_page = 50
|
191
305
|
f.per_page = 2
|
192
306
|
end
|
@@ -316,7 +430,7 @@ The DSL context has access to two additional methods:
|
|
316
430
|
|
317
431
|
Below is an example output of `color_dump` method, which is intended for the debugging purposes.
|
318
432
|
|
319
|
-
|
433
|
+
image::https://raw.githubusercontent.com/kigster/simple-feed/master/man/sf-color-dump.png[title=#color_dump method output, width=659, link=https://raw.githubusercontent.com/kigster/simple-feed/master/man/sf-color-dump.png]
|
320
434
|
|
321
435
|
+++<a name="api">++++++</a>+++
|
322
436
|
|
@@ -393,9 +507,7 @@ It is the intention of this gem that:
|
|
393
507
|
* it should be easy to write new providers
|
394
508
|
* it should be easy to swap out providers
|
395
509
|
|
396
|
-
|
397
|
-
|
398
|
-
Two providers are available with this gem:
|
510
|
+
One provider is included with this gem:
|
399
511
|
|
400
512
|
=== `SimpleFeed::Providers::Redis::Provider`
|
401
513
|
|
@@ -403,36 +515,43 @@ Redis Provider is a production-ready persistence adapter that uses the https://r
|
|
403
515
|
|
404
516
|
This provider is optimized for large writes and can use either a single Redis instance for all users of your application, or any number of Redis https://en.wikipedia.org/wiki/Shard_(database_architecture)[shards] by using a https://github.com/twitter/twemproxy[_Twemproxy_] in front of the Redis shards.
|
405
517
|
|
406
|
-
=== `SimpleFeed::Providers::HashProvider`
|
407
|
-
|
408
|
-
This is a pure Hash-like implementation of a provider that can be useful in unit tests of a host application. This provider could be used to write and read events within a single ruby process, can be serialized to and from a YAML file, and is therefore intended primarily for Feed emulations in automated tests.
|
409
|
-
|
410
|
-
== Redis Provider
|
411
|
-
|
412
518
|
If you set environment variable `REDIS_DEBUG` to `true` and run the example (see below) you will see every operation redis performs. This could be useful in debugging an issue or submitting a bug report.
|
413
519
|
|
414
|
-
== Running the Examples
|
520
|
+
== Running the Examples and Specs
|
415
521
|
|
416
522
|
Source code for the gem contains the `examples` folder with an example file that can be used to test out the providers, and see what they do under the hood.
|
417
523
|
|
524
|
+
Both the specs and the example requires a local redis instance to be available.
|
525
|
+
|
418
526
|
To run it, checkout the source of the library, and then:
|
419
527
|
|
420
|
-
[source,bash]
|
421
528
|
----
|
422
529
|
git clone https://github.com/kigster/simple-feed.git
|
423
530
|
cd simple-feed
|
424
|
-
|
425
|
-
|
531
|
+
|
532
|
+
# on OSX with HomeBrew:
|
533
|
+
brew install redis
|
534
|
+
brew services start redis
|
535
|
+
|
536
|
+
# check that your redis is up:
|
537
|
+
redis-cli info
|
538
|
+
|
539
|
+
# install bundler and other dependencies
|
540
|
+
gem install bundler --version 2.1.4
|
541
|
+
bundle install
|
542
|
+
bundle exec rspec # make sure tests are passing
|
543
|
+
|
544
|
+
# run the example:
|
426
545
|
ruby examples/redis_provider_example.rb
|
427
546
|
----
|
428
547
|
|
429
548
|
The above command will help you download, setup all dependencies, and run the examples for a single user:
|
430
549
|
|
431
|
-
image::https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example.png[Example,link=https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example.png]
|
550
|
+
image::https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example.png[title=Running Redis Example in a Terminal, width=663, link=https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example.png]
|
432
551
|
|
433
552
|
If you set `REDIS_DEBUG` variable prior to running the example, you will be able to see every single Redis command executed as the example works its way through. Below is a sample output:
|
434
553
|
|
435
|
-
image::https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example-redis-debug.png[Example with
|
554
|
+
image::https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example-redis-debug.png[title=Running Redis Example with REDIS_DEBUG set, width=918, link=https://raw.githubusercontent.com/kigster/simple-feed/master/man/running-example-redis-debug.png]
|
436
555
|
|
437
556
|
=== Generating Ruby API Documentation
|
438
557
|
|
@@ -474,6 +593,8 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/kigste
|
|
474
593
|
|
475
594
|
The gem is available as open source under the terms of the http://opensource.org/licenses/MIT[MIT License].
|
476
595
|
|
596
|
+
image:https://app.fossa.com/api/projects/git%2Bgithub.com%2Fkigster%2Fsimple-feed.svg?type=large[FOSSA Scan Status, link=https://app.fossa.com/projects/git%2Bgithub.com%2Fkigster%2Fsimple-feed?ref=badge_large]
|
597
|
+
|
477
598
|
=== Acknowledgements
|
478
599
|
|
479
600
|
* This project is conceived and sponsored by https://simbi.com[Simbi, Inc.].
|
data/Rakefile
CHANGED
data/codecov.yml
ADDED
@@ -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 "
|
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')
|
40
|
-
store('value two')
|
41
|
-
store('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
|
46
|
-
unread_count
|
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
|
-
|
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
|
-
|
55
|
+
total_count { |r| p 'total_count ', "#{r[@uid]._v}" }
|
56
|
+
unread_count { |r| p 'unread_count ', "#{r[@uid]._v}" }
|
54
57
|
|
55
|
-
|
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')
|
66
|
-
|
67
|
-
|
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
|