promptly 0.1.7 → 0.1.17
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 +24 -428
- data/Rakefile +1 -0
- data/app/prompts/test/prompt.en.erb +1 -0
- data/app/prompts/test/prompt_with_metadata.en.erb +6 -0
- data/lib/promptly/helper.rb +19 -0
- data/lib/promptly/railtie.rb +15 -0
- data/lib/promptly/renderer.rb +6 -0
- data/lib/promptly/tasks/ai_prompts.rake +123 -0
- data/lib/promptly/validator.rb +24 -0
- data/lib/promptly/version.rb +1 -1
- data/lib/promptly.rb +79 -2
- data/wiki/Configuration.md +63 -0
- data/wiki/Functional-Prompt-Tests.md +36 -0
- data/wiki/Generators.md +32 -0
- data/wiki/Helper-render_prompt.md +25 -0
- data/wiki/Home.md +29 -0
- data/wiki/I18n-Prompts-Usage.md +60 -0
- data/wiki/Linting-Templates.md +38 -0
- data/wiki/Liquid-Templates.md +49 -0
- data/wiki/Prompt-Version-Metadata.md +34 -0
- data/wiki/Quick-Start.md +116 -0
- data/wiki/Rails-App-Integration.md +116 -0
- data/wiki/Schema-Validation.md +27 -0
- data/wiki/_Sidebar.md +11 -0
- metadata +35 -5
- data/promptly.gemspec +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a23a4bcc5d7efe0f5c83ec8cd21d0badc3d6a09b6ab6be98b0b89178ee1f9ac
|
|
4
|
+
data.tar.gz: 5136d03f17b3c1372abc31146da3771bf719ea0a71869a1233685421f22ea9b1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 42f82b2467f10f8851aada866267cd047a95dd99f592b50bbd0189ccddfd58ff619f0973f981f3f88be803bbc213ac3a2b645f2d9ed59a8bc0967c715ceb8030
|
|
7
|
+
data.tar.gz: 368c7a84f7215db1415a3d2165e6061917977ac924127c937ebf9e48db641d35f373dbb8df3f857145bb9e00881faa6b15048872ded0fc968a4922defad35337
|
data/README.md
CHANGED
|
@@ -1,37 +1,3 @@
|
|
|
1
|
-
## Generators
|
|
2
|
-
|
|
3
|
-
Create prompt templates following conventions.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
# ERB with multiple locales
|
|
7
|
-
rails g promptly:prompt user_onboarding/welcome_email --locales en es --engine erb
|
|
8
|
-
|
|
9
|
-
# Liquid with a single locale
|
|
10
|
-
rails g promptly:prompt ai_coaching/goal_review --locales en --engine liquid
|
|
11
|
-
|
|
12
|
-
# Fallback-only (no locale suffix)
|
|
13
|
-
rails g promptly:prompt content_generation/outline --no-locale
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Options:
|
|
17
|
-
|
|
18
|
-
- `--engine` erb|liquid (default: erb)
|
|
19
|
-
- `--locales` space-separated list (default: I18n.available_locales if available, else `en`)
|
|
20
|
-
- `--no-locale` create only fallback file (e.g., `welcome_email.erb`)
|
|
21
|
-
- `--force` overwrite existing files
|
|
22
|
-
|
|
23
|
-
Generated files are placed under `app/prompts/` and directories are created as needed.
|
|
24
|
-
|
|
25
|
-
Examples:
|
|
26
|
-
|
|
27
|
-
- `app/prompts/user_onboarding/welcome_email.en.erb`
|
|
28
|
-
- `app/prompts/user_onboarding/welcome_email.es.erb`
|
|
29
|
-
- `app/prompts/ai_coaching/goal_review.en.liquid`
|
|
30
|
-
- `app/prompts/content_generation/outline.erb` (fallback-only)
|
|
31
|
-
|
|
32
|
-
The generator seeds a minimal, intention-revealing scaffold you can edit immediately.
|
|
33
|
-
|
|
34
|
-
## API Reference
|
|
35
1
|
# Promptly
|
|
36
2
|
|
|
37
3
|
Opinionated Rails integration for reusable AI prompt templates. Build maintainable, localized, and testable AI prompts using ERB or Liquid templates with Rails conventions.
|
|
@@ -44,6 +10,11 @@ Opinionated Rails integration for reusable AI prompt templates. Build maintainab
|
|
|
44
10
|
- **Render & CLI**: Test prompts in Rails console or via rake tasks
|
|
45
11
|
- **Minimal setup**: Auto-loads via Railtie, zero configuration required
|
|
46
12
|
- **Prompt caching**: Configurable cache store, TTL, and cache-bypass options
|
|
13
|
+
- **Schema Validation**: Ensure all locals passed to templates match a defined schema.
|
|
14
|
+
|
|
15
|
+
## Documentation
|
|
16
|
+
|
|
17
|
+
For detailed documentation, please visit the [Promptly Wiki](https://github.com/wilburhimself/promptly/wiki).
|
|
47
18
|
|
|
48
19
|
## Install
|
|
49
20
|
|
|
@@ -67,54 +38,6 @@ bundle install
|
|
|
67
38
|
|
|
68
39
|
## Quick Start
|
|
69
40
|
|
|
70
|
-
### 1. Create prompt templates
|
|
71
|
-
|
|
72
|
-
Create `app/prompts/user_onboarding/welcome_email.en.erb`:
|
|
73
|
-
|
|
74
|
-
```erb
|
|
75
|
-
You are a friendly customer success manager writing a personalized welcome email.
|
|
76
|
-
|
|
77
|
-
Context:
|
|
78
|
-
- User name: <%= name %>
|
|
79
|
-
- App name: <%= app_name %>
|
|
80
|
-
- User's role: <%= user_role %>
|
|
81
|
-
- Available features for this user: <%= features.join(", ") %>
|
|
82
|
-
- User signed up <%= days_since_signup %> days ago
|
|
83
|
-
|
|
84
|
-
Task: Write a warm, personalized welcome email that:
|
|
85
|
-
1. Addresses the user by name
|
|
86
|
-
2. Explains the key benefits specific to their role
|
|
87
|
-
3. Highlights 2-3 most relevant features they should try first
|
|
88
|
-
4. Includes a clear call-to-action to get started
|
|
89
|
-
5. Maintains a professional but friendly tone
|
|
90
|
-
|
|
91
|
-
Keep the email concise (under 200 words) and actionable.
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Create `app/prompts/user_onboarding/welcome_email.es.erb`:
|
|
95
|
-
|
|
96
|
-
```erb
|
|
97
|
-
Eres un gerente de éxito del cliente amigable escribiendo un email de bienvenida personalizado.
|
|
98
|
-
|
|
99
|
-
Contexto:
|
|
100
|
-
- Nombre del usuario: <%= name %>
|
|
101
|
-
- Nombre de la app: <%= app_name %>
|
|
102
|
-
- Rol del usuario: <%= user_role %>
|
|
103
|
-
- Funciones disponibles para este usuario: <%= features.join(", ") %>
|
|
104
|
-
- El usuario se registró hace <%= days_since_signup %> días
|
|
105
|
-
|
|
106
|
-
Tarea: Escribe un email de bienvenida cálido y personalizado que:
|
|
107
|
-
1. Se dirija al usuario por su nombre
|
|
108
|
-
2. Explique los beneficios clave específicos para su rol
|
|
109
|
-
3. Destaque 2-3 funciones más relevantes que debería probar primero
|
|
110
|
-
4. Incluya una llamada a la acción clara para comenzar
|
|
111
|
-
5. Mantenga un tono profesional pero amigable
|
|
112
|
-
|
|
113
|
-
Mantén el email conciso (menos de 200 palabras) y orientado a la acción.
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### 2. Render in your Rails app
|
|
117
|
-
|
|
118
41
|
```ruby
|
|
119
42
|
# In a controller, service, or anywhere in Rails
|
|
120
43
|
prompt = Promptly.render(
|
|
@@ -139,365 +62,38 @@ puts ai_response.dig("choices", 0, "message", "content")
|
|
|
139
62
|
# => AI-generated personalized welcome email in Spanish
|
|
140
63
|
```
|
|
141
64
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
```ruby
|
|
145
|
-
rails console
|
|
146
|
-
|
|
147
|
-
# Render the prompt before sending to AI
|
|
148
|
-
prompt = Promptly.render(
|
|
149
|
-
"user_onboarding/welcome_email",
|
|
150
|
-
locale: :en,
|
|
151
|
-
locals: {
|
|
152
|
-
name: "John Smith",
|
|
153
|
-
app_name: "ProjectHub",
|
|
154
|
-
user_role: "Developer",
|
|
155
|
-
features: ["API access", "Code reviews", "Deployment tools"],
|
|
156
|
-
days_since_signup: 1
|
|
157
|
-
}
|
|
158
|
-
)
|
|
159
|
-
puts prompt
|
|
160
|
-
|
|
161
|
-
# Uses I18n.locale by default
|
|
162
|
-
I18n.locale = :es
|
|
163
|
-
prompt = Promptly.render(
|
|
164
|
-
"user_onboarding/welcome_email",
|
|
165
|
-
locals: {
|
|
166
|
-
name: "María García",
|
|
167
|
-
app_name: "ProjectHub",
|
|
168
|
-
user_role: "Team Lead",
|
|
169
|
-
features: ["Crear proyectos", "Invitar miembros", "Seguimiento"],
|
|
170
|
-
days_since_signup: 3
|
|
171
|
-
}
|
|
172
|
-
)
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
### 4. CLI rendering
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
# Render specific locale (shows the prompt, not AI output)
|
|
179
|
-
rails ai_prompts:render[user_onboarding/welcome_email,es]
|
|
180
|
-
|
|
181
|
-
# Uses default locale
|
|
182
|
-
rails ai_prompts:render[user_onboarding/welcome_email]
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## Rails App Integration
|
|
186
|
-
|
|
187
|
-
### Service Object Pattern
|
|
188
|
-
|
|
189
|
-
```ruby
|
|
190
|
-
# app/services/ai_prompt_service.rb
|
|
191
|
-
class AiPromptService
|
|
192
|
-
def self.generate_welcome_email(user, locale: I18n.locale)
|
|
193
|
-
prompt = Promptly.render(
|
|
194
|
-
"user_onboarding/welcome_email",
|
|
195
|
-
locale: locale,
|
|
196
|
-
locals: {
|
|
197
|
-
name: user.full_name,
|
|
198
|
-
app_name: Rails.application.class.module_parent_name,
|
|
199
|
-
user_role: user.role.humanize,
|
|
200
|
-
features: available_features_for(user),
|
|
201
|
-
days_since_signup: (Date.current - user.created_at.to_date).to_i
|
|
202
|
-
}
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
# Send to AI service and return generated content
|
|
206
|
-
openai_client.chat(
|
|
207
|
-
model: "gpt-4",
|
|
208
|
-
messages: [{role: "user", content: prompt}]
|
|
209
|
-
).dig("choices", 0, "message", "content")
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
private
|
|
213
|
-
|
|
214
|
-
def self.available_features_for(user)
|
|
215
|
-
# Return features based on user's plan, role, etc.
|
|
216
|
-
case user.plan
|
|
217
|
-
when "basic"
|
|
218
|
-
["Create projects", "Basic reporting"]
|
|
219
|
-
when "pro"
|
|
220
|
-
["Create projects", "Team collaboration", "Advanced analytics", "API access"]
|
|
221
|
-
else
|
|
222
|
-
["Create projects"]
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
def self.openai_client
|
|
227
|
-
@openai_client ||= OpenAI::Client.new(access_token: Rails.application.credentials.openai_api_key)
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Mailer Integration
|
|
233
|
-
|
|
234
|
-
```ruby
|
|
235
|
-
# app/mailers/user_mailer.rb
|
|
236
|
-
class UserMailer < ApplicationMailer
|
|
237
|
-
def welcome_email(user)
|
|
238
|
-
@user = user
|
|
239
|
-
@ai_content = AiPromptService.generate_welcome_email(user, locale: user.locale)
|
|
240
|
-
|
|
241
|
-
mail(
|
|
242
|
-
to: user.email,
|
|
243
|
-
subject: t('mailer.welcome.subject')
|
|
244
|
-
)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### Background Job Usage
|
|
250
|
-
|
|
251
|
-
```ruby
|
|
252
|
-
# app/jobs/generate_ai_content_job.rb
|
|
253
|
-
class GenerateAiContentJob < ApplicationJob
|
|
254
|
-
def perform(user_id, prompt_identifier, locals = {})
|
|
255
|
-
user = User.find(user_id)
|
|
256
|
-
|
|
257
|
-
prompt = Promptly.render(
|
|
258
|
-
prompt_identifier,
|
|
259
|
-
locale: user.locale,
|
|
260
|
-
locals: locals.merge(
|
|
261
|
-
user_name: user.full_name,
|
|
262
|
-
user_role: user.role,
|
|
263
|
-
account_type: user.account_type
|
|
264
|
-
)
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
# Generate AI content
|
|
268
|
-
ai_response = openai_client.chat(
|
|
269
|
-
model: "gpt-4",
|
|
270
|
-
messages: [{role: "user", content: prompt}]
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
generated_content = ai_response.dig("choices", 0, "message", "content")
|
|
274
|
-
|
|
275
|
-
# Store or send the generated content
|
|
276
|
-
user.notifications.create!(
|
|
277
|
-
title: "AI Generated Content Ready",
|
|
278
|
-
content: generated_content,
|
|
279
|
-
notification_type: prompt_identifier.split('/').last
|
|
280
|
-
)
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
private
|
|
284
|
-
|
|
285
|
-
def openai_client
|
|
286
|
-
@openai_client ||= OpenAI::Client.new(access_token: Rails.application.credentials.openai_api_key)
|
|
287
|
-
end
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
# Usage
|
|
291
|
-
GenerateAiContentJob.perform_later(
|
|
292
|
-
user.id,
|
|
293
|
-
"coaching/goal_review",
|
|
294
|
-
{
|
|
295
|
-
current_goals: user.goals.active.pluck(:title),
|
|
296
|
-
progress_summary: "Made good progress on fitness goals",
|
|
297
|
-
challenges: ["Time management", "Consistency"]
|
|
298
|
-
}
|
|
299
|
-
)
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
## I18n Prompts Usage
|
|
303
|
-
|
|
304
|
-
### Directory Structure
|
|
65
|
+
## Structured Outputs (Response Schema Validation)
|
|
305
66
|
|
|
306
|
-
|
|
307
|
-
app/prompts/
|
|
308
|
-
├── user_onboarding/
|
|
309
|
-
│ ├── welcome_email.en.erb # English AI prompt
|
|
310
|
-
│ ├── welcome_email.es.erb # Spanish AI prompt
|
|
311
|
-
│ └── onboarding_checklist.erb # Fallback (any locale)
|
|
312
|
-
├── content_generation/
|
|
313
|
-
│ ├── blog_post_outline.en.erb
|
|
314
|
-
│ ├── social_media_post.es.erb
|
|
315
|
-
│ └── product_description.erb
|
|
316
|
-
└── ai_coaching/
|
|
317
|
-
├── goal_review.en.liquid # Liquid AI prompt
|
|
318
|
-
└── goal_review.es.liquid
|
|
319
|
-
```
|
|
67
|
+
Promptly supports OpenAI's structured outputs (`guided_json` style) by defining `.response.json` files alongside your templates.
|
|
320
68
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
Promptly follows this resolution order:
|
|
324
|
-
|
|
325
|
-
1. **Requested locale**: `welcome.es.erb` (if `locale: :es` specified)
|
|
326
|
-
2. **Default locale**: `welcome.en.erb` (if `I18n.default_locale == :en`)
|
|
327
|
-
3. **Fallback**: `welcome.erb` (no locale suffix)
|
|
69
|
+
For example, given an output schema `app/prompts/user_onboarding/welcome.response.json`, you can pass it directly to an AI service:
|
|
328
70
|
|
|
329
71
|
```ruby
|
|
330
|
-
#
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
# Will try: welcome_email.es.erb → welcome_email.en.erb → welcome_email.erb
|
|
340
|
-
prompt = Promptly.render(
|
|
341
|
-
"user_onboarding/welcome_email",
|
|
342
|
-
locals: {
|
|
343
|
-
name: "María García",
|
|
344
|
-
app_name: "ProjectHub",
|
|
345
|
-
user_role: "Manager",
|
|
346
|
-
features: ["Team management", "Analytics", "Reporting"],
|
|
347
|
-
days_since_signup: 1
|
|
348
|
-
}
|
|
349
|
-
)
|
|
350
|
-
|
|
351
|
-
# Force specific locale for AI prompt generation
|
|
352
|
-
prompt = Promptly.render(
|
|
353
|
-
"content_generation/blog_post_outline",
|
|
354
|
-
locale: :fr,
|
|
355
|
-
locals: {
|
|
356
|
-
topic: "Intelligence Artificielle",
|
|
357
|
-
target_audience: "Développeurs",
|
|
358
|
-
word_count: 1500
|
|
72
|
+
# Returns the schema wrapped in expected OpenAI format
|
|
73
|
+
response_format = Promptly.response_format("user_onboarding/welcome", strict: true)
|
|
74
|
+
|
|
75
|
+
ai_response = openai_client.chat(
|
|
76
|
+
parameters: {
|
|
77
|
+
model: "gpt-4o",
|
|
78
|
+
messages: [{role: "user", content: prompt}],
|
|
79
|
+
response_format: response_format
|
|
359
80
|
}
|
|
360
81
|
)
|
|
361
82
|
```
|
|
362
83
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
For more complex templating needs, use Liquid:
|
|
366
|
-
|
|
367
|
-
```liquid
|
|
368
|
-
<!-- app/prompts/ai_coaching/goal_review.en.liquid -->
|
|
369
|
-
You are an experienced life coach conducting a goal review session.
|
|
370
|
-
|
|
371
|
-
Context:
|
|
372
|
-
- Client name: {{ user_name }}
|
|
373
|
-
- Goals being reviewed: {% for goal in current_goals %}{{ goal }}{% unless forloop.last %}, {% endunless %}{% endfor %}
|
|
374
|
-
- Recent progress: {{ progress_summary }}
|
|
375
|
-
- Current challenges: {% for challenge in challenges %}{{ challenge }}{% unless forloop.last %}, {% endunless %}{% endfor %}
|
|
376
|
-
- Review period: {{ review_period | default: "monthly" }}
|
|
377
|
-
|
|
378
|
-
Task: Provide a personalized goal review that:
|
|
379
|
-
1. Acknowledges their progress and celebrates wins
|
|
380
|
-
2. Addresses each challenge with specific, actionable advice
|
|
381
|
-
3. Suggests 2-3 concrete next steps for the coming {{ review_period }}
|
|
382
|
-
4. Asks 1-2 thoughtful questions to help them reflect
|
|
383
|
-
5. Maintains an encouraging but realistic tone
|
|
384
|
-
|
|
385
|
-
{% if current_goals.size > 5 %}
|
|
386
|
-
Note: The client has many goals. Help them prioritize the most important ones.
|
|
387
|
-
{% endif %}
|
|
388
|
-
|
|
389
|
-
Format your response as a conversational coaching session, not a formal report.
|
|
390
|
-
```
|
|
84
|
+
You can also natively validate the returned JSON string in Ruby to ensure it conforms exactly to the schema:
|
|
391
85
|
|
|
392
86
|
```ruby
|
|
393
|
-
|
|
394
|
-
prompt = Promptly.render(
|
|
395
|
-
"ai_coaching/goal_review",
|
|
396
|
-
locale: :en,
|
|
397
|
-
locals: {
|
|
398
|
-
user_name: "Alex",
|
|
399
|
-
current_goals: ["Run 5K under 25min", "Gym 3x/week", "Read 12 books/year"],
|
|
400
|
-
progress_summary: "Consistent with gym, behind on running pace, ahead on reading",
|
|
401
|
-
challenges: ["Time management", "Motivation on rainy days"],
|
|
402
|
-
review_period: "monthly"
|
|
403
|
-
}
|
|
404
|
-
)
|
|
405
|
-
|
|
406
|
-
# Send to AI service for personalized coaching
|
|
407
|
-
ai_coaching_session = openai_client.chat(
|
|
408
|
-
model: "gpt-4",
|
|
409
|
-
messages: [{role: "user", content: prompt}]
|
|
410
|
-
).dig("choices", 0, "message", "content")
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
## Configuration
|
|
87
|
+
raw_json = ai_response.dig("choices", 0, "message", "content")
|
|
414
88
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### Caching
|
|
423
|
-
|
|
424
|
-
Promptly supports optional caching for rendered prompts.
|
|
425
|
-
|
|
426
|
-
- Default: enabled, TTL = 3600 seconds (1 hour).
|
|
427
|
-
- In Rails, the Railtie auto-uses `Rails.cache` if present.
|
|
428
|
-
|
|
429
|
-
Configure globally:
|
|
430
|
-
|
|
431
|
-
```ruby
|
|
432
|
-
# config/initializers/promptly.rb
|
|
433
|
-
Promptly::Cache.configure do |c|
|
|
434
|
-
c.store = Rails.cache # or any ActiveSupport::Cache store
|
|
435
|
-
c.ttl = 3600 # default TTL in seconds
|
|
436
|
-
c.enabled = true # globally enable/disable caching
|
|
89
|
+
begin
|
|
90
|
+
# Validates and parses the JSON, or raises Promptly::ValidationError
|
|
91
|
+
parsed_output = Promptly.validate_response!("user_onboarding/welcome", raw_json)
|
|
92
|
+
rescue Promptly::ValidationError => e
|
|
93
|
+
# Handle invalid response
|
|
437
94
|
end
|
|
438
95
|
```
|
|
439
96
|
|
|
440
|
-
Per-call options:
|
|
441
|
-
|
|
442
|
-
```ruby
|
|
443
|
-
# Bypass cache for this render only
|
|
444
|
-
Promptly.render("user_onboarding/welcome_email", locals: {...}, cache: false)
|
|
445
|
-
|
|
446
|
-
# Custom TTL for this render only
|
|
447
|
-
Promptly.render("user_onboarding/welcome_email", locals: {...}, ttl: 5.minutes)
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
Invalidation:
|
|
451
|
-
|
|
452
|
-
```ruby
|
|
453
|
-
# Clear entire cache store (if supported by the store)
|
|
454
|
-
Promptly::Cache.clear
|
|
455
|
-
|
|
456
|
-
# Delete a specific cached entry
|
|
457
|
-
Promptly::Cache.delete(
|
|
458
|
-
identifier: "user_onboarding/welcome_email",
|
|
459
|
-
locale: :en,
|
|
460
|
-
locals: {name: "John"},
|
|
461
|
-
prompts_path: Promptly.prompts_path
|
|
462
|
-
)
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### Direct Template Rendering
|
|
466
|
-
|
|
467
|
-
```ruby
|
|
468
|
-
# Render ERB directly (without file lookup)
|
|
469
|
-
template = "Hello <%= name %>, welcome to <%= app %>!"
|
|
470
|
-
output = Promptly.render_template(template, locals: {name: "John", app: "MyApp"})
|
|
471
|
-
|
|
472
|
-
# Render Liquid directly
|
|
473
|
-
template = "Hello {{ name }}, welcome to {{ app }}!"
|
|
474
|
-
output = Promptly.render_template(template, locals: {name: "John", app: "MyApp"}, engine: :liquid)
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
## API Reference
|
|
478
|
-
|
|
479
|
-
### `Promptly.render(identifier, locale: nil, locals: {}, cache: true, ttl: nil)`
|
|
480
|
-
|
|
481
|
-
Renders a template by identifier with locale fallback and optional caching.
|
|
482
|
-
|
|
483
|
-
- **identifier**: Template path like `"user_onboarding/welcome"`
|
|
484
|
-
- **locale**: Specific locale (defaults to `I18n.locale`)
|
|
485
|
-
- **locals**: Hash of variables for template
|
|
486
|
-
- **cache**: Enable/disable caching for this call (defaults to `true`)
|
|
487
|
-
- **ttl**: Time-to-live in seconds for cache entry (overrides default TTL)
|
|
488
|
-
|
|
489
|
-
### `Promptly.render_template(template, locals: {}, engine: :erb)`
|
|
490
|
-
|
|
491
|
-
Renders template string directly.
|
|
492
|
-
|
|
493
|
-
- **template**: Template string
|
|
494
|
-
- **locals**: Hash of variables
|
|
495
|
-
- **engine**: `:erb` or `:liquid`
|
|
496
|
-
|
|
497
|
-
### `Promptly.prompts_path`
|
|
498
|
-
|
|
499
|
-
Get/set the root directory for prompt templates (defaults to `Rails.root/app/prompts`).
|
|
500
|
-
|
|
501
97
|
## Development
|
|
502
98
|
|
|
503
99
|
```bash
|
|
@@ -524,4 +120,4 @@ rake build
|
|
|
524
120
|
|
|
525
121
|
## License
|
|
526
122
|
|
|
527
|
-
MIT
|
|
123
|
+
MIT
|
data/Rakefile
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This is a test prompt.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Promptly
|
|
4
|
+
module Helper
|
|
5
|
+
# Render a prompt template by identifier.
|
|
6
|
+
#
|
|
7
|
+
# Example:
|
|
8
|
+
# # app/prompts/welcome_email.erb
|
|
9
|
+
# # Hello <%= @user.name %>, welcome!
|
|
10
|
+
#
|
|
11
|
+
# # In a mailer, job, or service including this module:
|
|
12
|
+
# # render_prompt("welcome_email", user: @user)
|
|
13
|
+
#
|
|
14
|
+
# Supports locale-aware lookup and caching.
|
|
15
|
+
def render_prompt(identifier, locale: nil, cache: true, ttl: nil, **locals)
|
|
16
|
+
Promptly.render(identifier, locale: locale, locals: locals, cache: cache, ttl: ttl)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/promptly/railtie.rb
CHANGED
|
@@ -11,6 +11,21 @@ module Promptly
|
|
|
11
11
|
if defined?(Rails.cache)
|
|
12
12
|
Promptly::Cache.store = Rails.cache
|
|
13
13
|
end
|
|
14
|
+
|
|
15
|
+
# Make render_prompt available in mailers, jobs, and controllers
|
|
16
|
+
if defined?(ActiveSupport)
|
|
17
|
+
ActiveSupport.on_load(:action_mailer) do
|
|
18
|
+
include Promptly::Helper
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
ActiveSupport.on_load(:active_job) do
|
|
22
|
+
include Promptly::Helper
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
ActiveSupport.on_load(:action_controller) do
|
|
26
|
+
include Promptly::Helper
|
|
27
|
+
end
|
|
28
|
+
end
|
|
14
29
|
end
|
|
15
30
|
|
|
16
31
|
rake_tasks do
|
data/lib/promptly/renderer.rb
CHANGED
|
@@ -26,6 +26,12 @@ module Promptly
|
|
|
26
26
|
lookup = ActionView::LookupContext.new(ActionView::PathSet.new([]))
|
|
27
27
|
av = view_class.new(lookup, {}, nil)
|
|
28
28
|
|
|
29
|
+
# Make locals available both as locals and instance variables (e.g., @user)
|
|
30
|
+
(locals || {}).each do |k, v|
|
|
31
|
+
ivar = "@#{k}"
|
|
32
|
+
av.instance_variable_set(ivar, v)
|
|
33
|
+
end
|
|
34
|
+
|
|
29
35
|
av.render(inline: template, type: :erb, locals: locals || {})
|
|
30
36
|
end
|
|
31
37
|
|