og_pilot_ruby 0.4.3 → 0.4.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e2a983c2b0ced955a1d89e715316050384d6879ec31e328b391e1b7d6e788e9
4
- data.tar.gz: a2e254954c6f260f493f10e86fd19b8a7d2a64781f9a970ad730bd51fc411962
3
+ metadata.gz: ee4898069ea1b40ff62e9db35ca4d5c726aaa27cf7b658c233c9f5f9bcc70bb2
4
+ data.tar.gz: 4c38cee9d0e4753208e3b65c37c4b33a6b478fa1b34c9fd16cd98aca56836c56
5
5
  SHA512:
6
- metadata.gz: 8fe9e289d37a64f41c9d578aba09477ce5e0fa9ea29a422ded261b9ab01b45c02cb7aff88581ed446bd2e5aafeb94c9f49b5762e374bfe6b63d6063985ec462b
7
- data.tar.gz: 96d7f35d05dae228f048bae8e7ee55a046fc4480f2546b5153c3bf72763ccd7116efa41d59c6b9f71b9d651cd0d17aca71636dfaba6a62123d8a71a2c8257ef3
6
+ metadata.gz: 2c1772c79ca7847a027611e42869888017b53e2e829a1262c519fb708d7e30e9fd4bce3bb0eb1ccc997c022a250b314ceadc88d3db5bcdb6c415c6aa23d5a177
7
+ data.tar.gz: dced72f71149af6c1544bd11ff0803e10159d81e1f0039a58df025009e8527f7b4a1f4eca2bfe934d86e8d37f47a353bf9c6dfb1e32b4ba387d1ed8f4b01a66a
data/README.md CHANGED
@@ -29,6 +29,8 @@ OgPilotRuby.configure do |config|
29
29
  config.api_key = ENV.fetch("OG_PILOT_API_KEY")
30
30
  config.domain = ENV.fetch("OG_PILOT_DOMAIN")
31
31
  # config.strip_extensions = true
32
+ # config.cache_store = Rails.cache
33
+ # config.cache_ttl = 86_400
32
34
  end
33
35
  ```
34
36
 
@@ -61,6 +63,12 @@ image_url = OgPilotRuby.create_image(
61
63
  If you omit `iat`, OG Pilot will cache the image indefinitely. Provide an `iat` to
62
64
  refresh the cache daily.
63
65
 
66
+ When `config.cache_store` is set, the client can also cache generated responses
67
+ locally:
68
+
69
+ - With `iat`: cache for `cache_ttl` seconds
70
+ - Without `iat`: cache for `7 * cache_ttl` seconds
71
+
64
72
  ### Fail-safe behavior
65
73
 
66
74
  `create_image` is non-blocking. If any error occurs (request, configuration,
@@ -558,6 +566,9 @@ The gem handles `iss` (domain) and `sub` (API key prefix) automatically.
558
566
  | `open_timeout` | `5` | Connection timeout in seconds |
559
567
  | `read_timeout` | `10` | Read timeout in seconds |
560
568
  | `strip_extensions` | `true` | When `true`, file extensions are stripped from resolved paths (see [Strip extensions](#strip-extensions)) |
569
+ | `strip_query_parameters` | `false` | When `true`, query strings are removed from resolved paths before signing (see [Strip query parameters](#strip-query-parameters)) |
570
+ | `cache_store` | `nil` | Optional cache backend with `read`/`write` (for example `Rails.cache`) |
571
+ | `cache_ttl` | `86400` | Cache TTL in seconds when `cache_store` is enabled |
561
572
 
562
573
  ### Ruby options
563
574
 
@@ -624,6 +635,26 @@ payload = {
624
635
  data = OgPilotRuby.create_image(**payload, json: true)
625
636
  ```
626
637
 
638
+ ### Local caching
639
+
640
+ You can optionally enable client-side caching to avoid repeated API calls for
641
+ identical payloads:
642
+
643
+ ```ruby
644
+ OgPilotRuby.configure do |config|
645
+ config.cache_store = Rails.cache
646
+ config.cache_ttl = 86_400 # 1 day
647
+ end
648
+ ```
649
+
650
+ Cache key inputs include the normalized payload, `iat`, output mode (`json`
651
+ vs URL), and configured domain.
652
+
653
+ TTL behavior:
654
+
655
+ - If `iat` is present: cached for `cache_ttl`
656
+ - If `iat` is omitted: cached for `7 * cache_ttl`
657
+
627
658
  ### Strip extensions
628
659
 
629
660
  When `strip_extensions` is enabled, the client removes file extensions from the
@@ -649,6 +680,25 @@ OgPilotRuby.create_image(title: "Docs", path: "/docs.php")
649
680
  # Dotfiles are unchanged: /.hidden stays /.hidden
650
681
  ```
651
682
 
683
+ ### Strip query parameters
684
+
685
+ When `strip_query_parameters` is enabled, the client drops the query string from
686
+ every resolved path before it signs the payload. This keeps analytics grouped
687
+ under the canonical path even when links include tracking or pagination
688
+ parameters. It works alongside `strip_extensions`, so
689
+ `/archive.tar.gz?ref=campaign` resolves to `"/archive"` when both are enabled.
690
+
691
+ ```ruby
692
+ OgPilotRuby.configure do |config|
693
+ config.strip_query_parameters = true
694
+ end
695
+
696
+ # These resolve to "/docs":
697
+ OgPilotRuby.create_image(title: "Docs", path: "/docs")
698
+ OgPilotRuby.create_image(title: "Docs", path: "/docs?ref=main")
699
+ OgPilotRuby.create_image(title: "Docs", path: "https://example.com/docs?ref=campaign")
700
+ ```
701
+
652
702
  ## Development
653
703
 
654
704
  Run tests with:
@@ -9,4 +9,5 @@ OgPilotRuby.configure do |config|
9
9
  # config.open_timeout = 5
10
10
  # config.read_timeout = 10
11
11
  # config.strip_extensions = true
12
+ # config.strip_query_parameters = true
12
13
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "digest"
3
4
  require "json"
4
5
  require "logger"
5
6
  require "net/http"
@@ -25,14 +26,24 @@ module OgPilotRuby
25
26
  params.delete("path") if params.key?("path")
26
27
  params[:path] = manual_path.to_s.strip.empty? ? resolved_path(default:) : normalize_path(manual_path)
27
28
 
29
+ cache_key = cache_key_for(params, iat:, json:)
30
+ if config.cache_store && cache_key
31
+ cached = read_cached(cache_key)
32
+ return cached if cached
33
+ end
34
+
28
35
  uri = build_uri(params, iat:)
29
36
  response, final_uri = request(uri, json:, headers:)
30
37
 
31
- if json
38
+ result = if json
32
39
  JSON.parse(response.body)
33
40
  else
34
41
  response["Location"] || final_uri.to_s
35
42
  end
43
+
44
+ write_cached(cache_key, result, iat:) if config.cache_store && cache_key && result
45
+
46
+ result
36
47
  rescue StandardError => e
37
48
  log_create_image_failure(e, json:)
38
49
  json ? { "image_url" => nil } : nil
@@ -42,6 +53,25 @@ module OgPilotRuby
42
53
 
43
54
  attr_reader :config
44
55
 
56
+ def cache_key_for(params, iat:, json:)
57
+ key_data = params.transform_keys(&:to_sym).merge(iat: iat, json: json, domain: domain!)
58
+ normalized = key_data.sort_by { |k, _| k.to_s }.to_h
59
+ "og_pilot:#{Digest::SHA256.hexdigest(normalized.to_json)[0, 16]}"
60
+ end
61
+
62
+ def read_cached(cache_key)
63
+ config.cache_store.read(cache_key)
64
+ rescue StandardError
65
+ nil
66
+ end
67
+
68
+ def write_cached(cache_key, result, iat:)
69
+ ttl = iat ? config.cache_ttl : (7 * config.cache_ttl)
70
+ config.cache_store.write(cache_key, result, expires_in: ttl)
71
+ rescue StandardError
72
+ nil
73
+ end
74
+
45
75
  def log_create_image_failure(error, json:)
46
76
  mode = json ? "json" : "url"
47
77
  message = "OgPilotRuby create_image failed (mode=#{mode}): #{error.class}: #{error.message}"
@@ -223,6 +253,7 @@ module OgPilotRuby
223
253
  cleaned = extract_request_uri(cleaned)
224
254
  cleaned = "/#{cleaned}" unless cleaned.start_with?("/")
225
255
  cleaned = strip_extension(cleaned) if config.strip_extensions
256
+ cleaned = remove_query_parameters(cleaned) if config.strip_query_parameters
226
257
  cleaned
227
258
  end
228
259
 
@@ -248,6 +279,11 @@ module OgPilotRuby
248
279
  query ? "#{stripped}?#{query}" : stripped
249
280
  end
250
281
 
282
+ def remove_query_parameters(path)
283
+ stripped = path.split("?", 2).first
284
+ stripped.empty? ? "/" : stripped
285
+ end
286
+
251
287
  def extract_request_uri(value)
252
288
  return value unless value.start_with?("http://", "https://")
253
289
 
@@ -5,7 +5,7 @@ module OgPilotRuby
5
5
  DEFAULT_BASE_URL = "https://ogpilot.com"
6
6
  private_constant :DEFAULT_BASE_URL
7
7
 
8
- attr_accessor :api_key, :domain, :base_url, :open_timeout, :read_timeout, :strip_extensions
8
+ attr_accessor :api_key, :domain, :base_url, :open_timeout, :read_timeout, :strip_extensions, :strip_query_parameters, :cache_store, :cache_ttl
9
9
 
10
10
  def initialize
11
11
  @api_key = ENV.fetch("OG_PILOT_API_KEY", nil)
@@ -14,6 +14,9 @@ module OgPilotRuby
14
14
  @open_timeout = 5
15
15
  @read_timeout = 10
16
16
  @strip_extensions = true
17
+ @strip_query_parameters = false
18
+ @cache_store = nil
19
+ @cache_ttl = 86400
17
20
  end
18
21
  end
19
22
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OgPilotRuby
4
- VERSION = "0.4.3"
4
+ VERSION = "0.4.5"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: og_pilot_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sunergos IT LLC
@@ -97,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  requirements: []
100
- rubygems_version: 4.0.3
100
+ rubygems_version: 4.0.6
101
101
  specification_version: 4
102
102
  summary: Ruby client for the OG Pilot Open Graph image generator.
103
103
  test_files: []