og_pilot_ruby 0.1.3 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +75 -0
- data/lib/og_pilot_ruby/client.rb +82 -1
- data/lib/og_pilot_ruby/rails_helper.rb +2 -2
- data/lib/og_pilot_ruby/railtie.rb +6 -0
- data/lib/og_pilot_ruby/request_store_middleware.rb +19 -0
- data/lib/og_pilot_ruby/version.rb +1 -1
- data/lib/og_pilot_ruby.rb +2 -2
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 34c76008dde8c56d665a3eece4869fa7f28ec0ad656ef2cae798da373956d055
|
|
4
|
+
data.tar.gz: 1a2e055c83968ed81d0ca747457403c865c045d37acd5694f9ac5fc221497d57
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: efb3af2ede82a7519c855dfd6e9bbbfc17940a22ce91a12f3bac1e3e63e65fba92e61b9554ba8e5f2a39cc47a2557ef215595b4776d71dbef1c232333cc5d16b
|
|
7
|
+
data.tar.gz: e3f0bc81c47bfce15490c8348c0db2a32c601b6761200dd25487baf0294799890ebb5b7d864ebf44ea800f5d3a2f9393b433a19f8c8a0df117c2ed3d8a934dc2
|
data/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# OG Pilot Ruby
|
|
2
2
|
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> An active [OG Pilot](https://ogpilot.com?ref=og_pilot_ruby) subscription is required to use this gem.
|
|
5
|
+
|
|
3
6
|
A small Ruby client for generating OG Pilot Open Graph images via signed JWTs.
|
|
4
7
|
|
|
5
8
|
## Installation
|
|
@@ -57,6 +60,78 @@ image_url = OgPilotRuby.create_image(
|
|
|
57
60
|
If you omit `iat`, OG Pilot will cache the image indefinitely. Provide an `iat` to
|
|
58
61
|
refresh the cache daily.
|
|
59
62
|
|
|
63
|
+
## Parameters
|
|
64
|
+
|
|
65
|
+
All parameters are embedded in the signed JWT payload; the only query param is `token`.
|
|
66
|
+
The gem handles `iss` (domain) and `sub` (API key prefix) automatically.
|
|
67
|
+
|
|
68
|
+
### Core parameters
|
|
69
|
+
|
|
70
|
+
| Parameter | Required | Default | Description |
|
|
71
|
+
|---------------|----------|----------|---------------------------------------------------------------|
|
|
72
|
+
| `template` | No | `"page"` | Template name |
|
|
73
|
+
| `title` | Yes | — | Primary title text |
|
|
74
|
+
| `description` | No | — | Subtitle or supporting text |
|
|
75
|
+
| `logo_url` | No | — | Logo image URL |
|
|
76
|
+
| `image_url` | No | — | Hero image URL |
|
|
77
|
+
| `bg_color` | No | — | Background color (hex format) |
|
|
78
|
+
| `text_color` | No | — | Text color (hex format) |
|
|
79
|
+
| `iat` | No | — | Issued-at timestamp for daily cache busting |
|
|
80
|
+
| `path` | No | auto-set | Request path for image rendering context. When provided, it overrides auto-resolution (see [Path handling](#path-handling)) |
|
|
81
|
+
|
|
82
|
+
### Ruby options
|
|
83
|
+
|
|
84
|
+
| Option | Default | Description |
|
|
85
|
+
|-----------|---------|--------------------------------------------------------------------------|
|
|
86
|
+
| `json` | `false` | When `true`, sends `Accept: application/json` and parses the JSON response |
|
|
87
|
+
| `headers` | — | Additional HTTP headers to include with the request |
|
|
88
|
+
| `default` | `false` | Forces `path` to `/` when `true`, unless a manual `path` is provided (see [Path handling](#path-handling)) |
|
|
89
|
+
|
|
90
|
+
### Template-specific parameters
|
|
91
|
+
|
|
92
|
+
| Template | Parameters |
|
|
93
|
+
|-------------|------------------------------------------------------------------------------------|
|
|
94
|
+
| `page` | `title`, `description` |
|
|
95
|
+
| `blog_post` | `title`, `author_name`, `author_avatar_url`, `publish_date` (ISO 8601) |
|
|
96
|
+
| `podcast` | `title`, `episode_date` (ISO 8601) |
|
|
97
|
+
| `product` | `title`, `unique_selling_point` |
|
|
98
|
+
| `event` | `title`, `event_date`, `event_location` |
|
|
99
|
+
| `book` | `title`, `description`, `book_author`, `book_series_number`, `book_description`, `book_genre` |
|
|
100
|
+
| `portfolio` | `title` |
|
|
101
|
+
| `company` | `title`, `company_logo_url`, `description` (note: `image_url` is ignored) |
|
|
102
|
+
|
|
103
|
+
### Path handling
|
|
104
|
+
|
|
105
|
+
The `path` parameter enhances OG Pilot analytics by tracking which OG images perform better across different pages on your site. Without it, all analytics would be aggregated under the `/` path, making it difficult to understand how individual pages or content types are performing. By automatically capturing the request path, you get granular insights into click-through rates and engagement for each OG image.
|
|
106
|
+
|
|
107
|
+
The client automatically injects a `path` parameter on every request:
|
|
108
|
+
|
|
109
|
+
| Option | Behavior |
|
|
110
|
+
|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
111
|
+
| `default: false` | Uses the current request path when available. In Rails, prefers `request.fullpath` (via the per-request store), then falls back to Rack/CGI env vars (`REQUEST_URI`, `PATH_INFO`). If no path can be determined, uses `/`. |
|
|
112
|
+
| `default: true` | Forces the `path` parameter to `/`, regardless of the current request (unless `path` is provided explicitly). |
|
|
113
|
+
| `path: "/..."` | Uses the provided path verbatim (normalized to start with `/`), overriding auto-resolution. |
|
|
114
|
+
|
|
115
|
+
**Example:**
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
image_url = OgPilotRuby.create_image(
|
|
119
|
+
template: "blog_post",
|
|
120
|
+
title: "How to Build Amazing OG Images",
|
|
121
|
+
default: true
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Manual override:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
image_url = OgPilotRuby.create_image(
|
|
129
|
+
template: "page",
|
|
130
|
+
title: "Hello OG Pilot",
|
|
131
|
+
path: "/pricing?plan=pro"
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
60
135
|
Fetch JSON metadata instead:
|
|
61
136
|
|
|
62
137
|
```ruby
|
data/lib/og_pilot_ruby/client.rb
CHANGED
|
@@ -15,7 +15,14 @@ module OgPilotRuby
|
|
|
15
15
|
@config = config
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def create_image(params = {}, json: false, iat: nil, headers: {})
|
|
18
|
+
def create_image(params = {}, json: false, iat: nil, headers: {}, default: false)
|
|
19
|
+
params ||= {}
|
|
20
|
+
params = params.dup
|
|
21
|
+
# Always include a path; manual overrides win, otherwise resolve from the current request.
|
|
22
|
+
manual_path = params.key?(:path) ? params[:path] : params["path"]
|
|
23
|
+
params.delete("path") if params.key?("path")
|
|
24
|
+
params[:path] = manual_path.to_s.strip.empty? ? resolved_path(default:) : normalize_path(manual_path)
|
|
25
|
+
|
|
19
26
|
uri = build_uri(params, iat:)
|
|
20
27
|
response = request(uri, json:, headers:)
|
|
21
28
|
|
|
@@ -96,5 +103,79 @@ module OgPilotRuby
|
|
|
96
103
|
def api_key_prefix
|
|
97
104
|
api_key!.slice(0, 8)
|
|
98
105
|
end
|
|
106
|
+
|
|
107
|
+
# Rails-first path resolution with Rack/CGI env fallback.
|
|
108
|
+
def resolved_path(default:)
|
|
109
|
+
return "/" if default
|
|
110
|
+
|
|
111
|
+
path = rails_fullpath
|
|
112
|
+
path = env_fullpath if path.nil? || path.empty?
|
|
113
|
+
normalize_path(path)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def rails_fullpath
|
|
117
|
+
return unless defined?(::Rails)
|
|
118
|
+
|
|
119
|
+
request = rails_request_from_store || rails_request_from_thread
|
|
120
|
+
fullpath = request.fullpath if request&.respond_to?(:fullpath)
|
|
121
|
+
fullpath unless fullpath.nil? || fullpath.empty?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def rails_request_from_store
|
|
125
|
+
return unless defined?(::RequestStore) && ::RequestStore.respond_to?(:store)
|
|
126
|
+
|
|
127
|
+
store = ::RequestStore.store
|
|
128
|
+
store[:action_dispatch_request] ||
|
|
129
|
+
store[:"action_dispatch.request"] ||
|
|
130
|
+
store[:request]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def rails_request_from_thread
|
|
134
|
+
Thread.current[:og_pilot_request] ||
|
|
135
|
+
Thread.current[:action_dispatch_request] ||
|
|
136
|
+
Thread.current[:"action_dispatch.request"] ||
|
|
137
|
+
Thread.current[:request]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def env_fullpath
|
|
141
|
+
request_uri = ENV["REQUEST_URI"]
|
|
142
|
+
return request_uri unless request_uri.nil? || request_uri.empty?
|
|
143
|
+
|
|
144
|
+
original_fullpath = ENV["ORIGINAL_FULLPATH"]
|
|
145
|
+
return original_fullpath unless original_fullpath.nil? || original_fullpath.empty?
|
|
146
|
+
|
|
147
|
+
path_info = ENV["PATH_INFO"]
|
|
148
|
+
return build_fullpath_from_path_info(path_info) unless path_info.nil? || path_info.empty?
|
|
149
|
+
|
|
150
|
+
request_path = ENV["REQUEST_PATH"]
|
|
151
|
+
return request_path unless request_path.nil? || request_path.empty?
|
|
152
|
+
|
|
153
|
+
nil
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def build_fullpath_from_path_info(path_info)
|
|
157
|
+
query = ENV["QUERY_STRING"].to_s
|
|
158
|
+
return path_info if query.empty?
|
|
159
|
+
|
|
160
|
+
"#{path_info}?#{query}"
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def normalize_path(path)
|
|
164
|
+
cleaned = path.to_s.strip
|
|
165
|
+
return "/" if cleaned.empty?
|
|
166
|
+
|
|
167
|
+
cleaned = extract_request_uri(cleaned)
|
|
168
|
+
cleaned = "/#{cleaned}" unless cleaned.start_with?("/")
|
|
169
|
+
cleaned
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def extract_request_uri(value)
|
|
173
|
+
return value unless value.start_with?("http://", "https://")
|
|
174
|
+
|
|
175
|
+
uri = URI.parse(value)
|
|
176
|
+
uri.request_uri || "/"
|
|
177
|
+
rescue URI::InvalidURIError
|
|
178
|
+
value
|
|
179
|
+
end
|
|
99
180
|
end
|
|
100
181
|
end
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module OgPilotRuby
|
|
4
4
|
module RailsHelper
|
|
5
|
-
def create_image(params = {}, json: false, iat: nil, headers: {}, **keyword_params)
|
|
6
|
-
OgPilotRuby.create_image(params, json:, iat:, headers:, **keyword_params)
|
|
5
|
+
def create_image(params = {}, json: false, iat: nil, headers: {}, default: false, **keyword_params)
|
|
6
|
+
OgPilotRuby.create_image(params, json:, iat:, headers:, default:, **keyword_params)
|
|
7
7
|
end
|
|
8
8
|
end
|
|
9
9
|
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
if defined?(Rails::Railtie)
|
|
4
|
+
require_relative "request_store_middleware"
|
|
5
|
+
|
|
4
6
|
module OgPilotRuby
|
|
5
7
|
# Rails integration for OG Pilot Ruby
|
|
6
8
|
class Railtie < Rails::Railtie
|
|
@@ -9,6 +11,10 @@ if defined?(Rails::Railtie)
|
|
|
9
11
|
inflect.acronym 'OgPilotRuby'
|
|
10
12
|
end
|
|
11
13
|
end
|
|
14
|
+
|
|
15
|
+
initializer 'og_pilot_ruby.middleware' do |app|
|
|
16
|
+
app.middleware.use OgPilotRuby::RequestStoreMiddleware
|
|
17
|
+
end
|
|
12
18
|
end
|
|
13
19
|
end
|
|
14
20
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OgPilotRuby
|
|
4
|
+
# Middleware that stores the current request in Thread.current
|
|
5
|
+
# so OgPilotRuby::Client can access it for path resolution.
|
|
6
|
+
class RequestStoreMiddleware
|
|
7
|
+
def initialize(app)
|
|
8
|
+
@app = app
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
request = ActionDispatch::Request.new(env)
|
|
13
|
+
Thread.current[:og_pilot_request] = request
|
|
14
|
+
@app.call(env)
|
|
15
|
+
ensure
|
|
16
|
+
Thread.current[:og_pilot_request] = nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/og_pilot_ruby.rb
CHANGED
|
@@ -25,9 +25,9 @@ module OgPilotRuby
|
|
|
25
25
|
Client.new(config)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def create_image(params = {}, json: false, iat: nil, headers: {}, **keyword_params)
|
|
28
|
+
def create_image(params = {}, json: false, iat: nil, headers: {}, default: false, **keyword_params)
|
|
29
29
|
params ||= {}
|
|
30
|
-
client.create_image(params.merge(keyword_params), json:, iat:, headers:)
|
|
30
|
+
client.create_image(params.merge(keyword_params), json:, iat:, headers:, default:)
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
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.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sunergos IT LLC
|
|
@@ -72,6 +72,7 @@ files:
|
|
|
72
72
|
- lib/og_pilot_ruby/jwt_encoder.rb
|
|
73
73
|
- lib/og_pilot_ruby/rails_helper.rb
|
|
74
74
|
- lib/og_pilot_ruby/railtie.rb
|
|
75
|
+
- lib/og_pilot_ruby/request_store_middleware.rb
|
|
75
76
|
- lib/og_pilot_ruby/version.rb
|
|
76
77
|
homepage: https://ogpilot.com
|
|
77
78
|
licenses:
|