recapi 0.1.3 → 0.1.4
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.
- checksums.yaml +4 -4
- data/README.md +20 -10
- data/lib/recapi.rb +27 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dfb3a3778d3f1733138f9f09c388edfd2e7cb946f6a2f38647b1070aa885d865
|
|
4
|
+
data.tar.gz: cc7d5eed12f41976d3e898a43bfee08364eaf2224b23f36b062b101577f7cb06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b48b41679b86910bb139237e630e5495bebfc4d1aa4fd9f924c2202344e5fbb903fb4c3966e5c608ffa406a90a2cb97c6a3fb75a989091509a41709f257e614f
|
|
7
|
+
data.tar.gz: b711bee7d27e579947e075e02a3e4487a97a2b7fe433e4d067d035d5b747157cd2cec7efa1adf34f6bd62bc1dd4d0e63ceb8f5612eb90bb929b1282a981f4051
|
data/README.md
CHANGED
|
@@ -16,24 +16,28 @@ passes the shared vectors in [`spec/`](../../spec).
|
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
The library is the gem `recapi` on RubyGems (production releases publish it:
|
|
20
|
+
[docs/deployment.md](../../docs/deployment.md#publishing-the-ruby-library)):
|
|
21
|
+
add `gem "recapi"` to your Gemfile, or
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
|
-
|
|
24
|
+
bundle add recapi
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
and `require "recapi"` loads it (Rails does it for you). Without access to
|
|
28
|
+
RubyGems, recapi serves the same file (the dashboard's quick start shows the
|
|
29
|
+
line with your address): save it as `lib/recapi.rb` and
|
|
30
|
+
`require Rails.root.join("lib/recapi").to_s` instead.
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
curl -fLo lib/recapi.rb https://api.<your-domain>/sdk/ruby/recapi-0.1.4.rb
|
|
34
|
+
```
|
|
31
35
|
|
|
32
36
|
## Quick start
|
|
33
37
|
|
|
34
38
|
```ruby
|
|
35
39
|
# config/initializers/recapi.rb: one client per process, shared by its threads.
|
|
36
|
-
require
|
|
40
|
+
require "recapi"
|
|
37
41
|
|
|
38
42
|
# A secret key (rk_sec_…): it stays on your server.
|
|
39
43
|
RECAPI = Recapi::Client.new(ENV.fetch("RECAPI_KEY"), "https://api.example.com",
|
|
@@ -78,7 +82,13 @@ max_batch:, logger:)` sets the options. The default `Recapi::MemoryStore`
|
|
|
78
82
|
keeps the 10,000 most recent users in the process; pass `Rails.cache` (it
|
|
79
83
|
has `read`/`write`/`delete`) or a Redis client (`get`/`set`/`del`) when
|
|
80
84
|
several processes serve the same users. It stores the same JSON as the
|
|
81
|
-
Python library, so both can share one store.
|
|
85
|
+
Python library, so both can share one store. Written to `Rails.cache` or a
|
|
86
|
+
redis-rb client, a user's state lasts 30 days from its last write, with no
|
|
87
|
+
setting to choose: well past the 24 hours a click earns credit, and recapi gives
|
|
88
|
+
a known `user_id` its history back afterwards. Any other store gets a plain
|
|
89
|
+
`set`/`write`, including a client wrapped in `ConnectionPool::Wrapper`: make it
|
|
90
|
+
expire entries itself after about 30 days, or Redis keeps them for ever. Puma's
|
|
91
|
+
threads share one
|
|
82
92
|
client; a forked worker (Puma cluster, Unicorn, Passenger) starts its own
|
|
83
93
|
queue and sender at its first call. Proxies come from `HTTPS_PROXY`,
|
|
84
94
|
`HTTP_PROXY` and `NO_PROXY`, as `Net::HTTP` reads them. Failures are logged
|
data/lib/recapi.rb
CHANGED
|
@@ -25,7 +25,7 @@ rescue LoadError
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
module Recapi
|
|
28
|
-
VERSION = "0.1.
|
|
28
|
+
VERSION = "0.1.4"
|
|
29
29
|
|
|
30
30
|
# Decayed user history, exactly as docs/history-spec.md (times in unix ms).
|
|
31
31
|
# A port of sdk/python's history.py, snapshot merging included.
|
|
@@ -389,6 +389,15 @@ module Recapi
|
|
|
389
389
|
# Shown items remembered per user.
|
|
390
390
|
MAX_SHOWN_ITEMS = 100
|
|
391
391
|
PUBLIC_KEY_PREFIX = "rk_pub_"
|
|
392
|
+
# How long a recognised shared store keeps a user's state after its last
|
|
393
|
+
# write. Far beyond the 24 hours of click credit, and by then a score has
|
|
394
|
+
# halved four times (under 5% is left). A known user_id gets its history
|
|
395
|
+
# back from recapi, which keeps 90 days, so only a visitor gone for a month
|
|
396
|
+
# starts afresh. It bounds a Redis at about one month's visitors instead of
|
|
397
|
+
# growing for ever.
|
|
398
|
+
STATE_LIFETIME_SECONDS = 30 * 24 * 60 * 60
|
|
399
|
+
# Where redis-rb defines set: 4.x and 5 on, older versions, Redis::Distributed.
|
|
400
|
+
REDIS_RB_SET = %w[Redis::Commands::Strings Redis Redis::Distributed].freeze
|
|
392
401
|
# Open connections kept between calls: one per call in flight at the busiest moment.
|
|
393
402
|
MAX_IDLE_CONNECTIONS = 16
|
|
394
403
|
CODE_BY_STATUS = {
|
|
@@ -435,6 +444,7 @@ module Recapi
|
|
|
435
444
|
end
|
|
436
445
|
|
|
437
446
|
@store = store || MemoryStore.new
|
|
447
|
+
@store_lifetime = store_lifetime
|
|
438
448
|
@timeout = timeout.to_f
|
|
439
449
|
@interval = flush_interval.to_f
|
|
440
450
|
@max_batch = max_batch.to_i.clamp(1, MAX_EVENTS_PER_REQUEST)
|
|
@@ -1010,11 +1020,26 @@ module Recapi
|
|
|
1010
1020
|
@logger.error("recapi: store write failed: #{e.class}: #{e.message}")
|
|
1011
1021
|
end
|
|
1012
1022
|
|
|
1023
|
+
# What makes this store's write expire the entry. By class name, since the
|
|
1024
|
+
# library requires neither gem. Any doubt (a subclass's own set, a wrapper
|
|
1025
|
+
# such as ConnectionPool::Wrapper) means no option, as for a store of the
|
|
1026
|
+
# customer's.
|
|
1027
|
+
def store_lifetime
|
|
1028
|
+
klass = @store.class
|
|
1029
|
+
if klass.public_method_defined?(:set)
|
|
1030
|
+
REDIS_RB_SET.include?(klass.instance_method(:set).owner.name) ? { ex: STATE_LIFETIME_SECONDS } : {}
|
|
1031
|
+
elsif klass.ancestors.any? { |ancestor| ancestor.name == "ActiveSupport::Cache::Store" }
|
|
1032
|
+
{ expires_in: STATE_LIFETIME_SECONDS }
|
|
1033
|
+
else
|
|
1034
|
+
{}
|
|
1035
|
+
end
|
|
1036
|
+
end
|
|
1037
|
+
|
|
1013
1038
|
# Redis clients have get/set/del, Rails.cache read/write/delete.
|
|
1014
1039
|
def store_call(action, key, value = nil)
|
|
1015
1040
|
case action
|
|
1016
1041
|
when :get then @store.respond_to?(:get) ? @store.get(key) : @store.read(key)
|
|
1017
|
-
when :set then @store.respond_to?(:set) ? @store.set(key, value) : @store.write(key, value)
|
|
1042
|
+
when :set then @store.respond_to?(:set) ? @store.set(key, value, **@store_lifetime) : @store.write(key, value, **@store_lifetime)
|
|
1018
1043
|
else @store.respond_to?(:delete) ? @store.delete(key) : @store.del(key)
|
|
1019
1044
|
end
|
|
1020
1045
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: recapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- recapi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'Ruby client for the recapi recommendation API: per-user history, event
|
|
14
14
|
batching, retries, popular fallback. Standard library only.'
|