polylingo_chat 0.1.1 โ†’ 0.4.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.
Files changed (22) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +383 -22
  3. data/lib/generators/polylingo_chat/install/README +72 -0
  4. data/lib/generators/polylingo_chat/install/install_generator.rb +44 -15
  5. data/lib/generators/polylingo_chat/install/templates/channels/polylingo_chat_channel.rb +1 -1
  6. data/lib/generators/polylingo_chat/install/templates/controllers/polylingo_chat/conversations_controller.rb +127 -0
  7. data/lib/generators/polylingo_chat/install/templates/controllers/polylingo_chat/messages_controller.rb +113 -0
  8. data/lib/generators/polylingo_chat/install/templates/create_conversations.rb +2 -2
  9. data/lib/generators/polylingo_chat/install/templates/create_message_translations.rb +13 -0
  10. data/lib/generators/polylingo_chat/install/templates/create_messages.rb +8 -4
  11. data/lib/generators/polylingo_chat/install/templates/create_participants.rb +8 -5
  12. data/lib/generators/polylingo_chat/install/templates/javascript/chat.js +5 -5
  13. data/lib/generators/polylingo_chat/install/templates/jobs/translate_job.rb +93 -0
  14. data/lib/generators/polylingo_chat/install/templates/models/conversation.rb +34 -5
  15. data/lib/generators/polylingo_chat/install/templates/models/message.rb +16 -8
  16. data/lib/generators/polylingo_chat/install/templates/models/participant.rb +19 -4
  17. data/lib/generators/polylingo_chat/install/templates/views/polylingo_chat/conversations/index.html.erb +56 -0
  18. data/lib/generators/polylingo_chat/install/templates/views/polylingo_chat/conversations/show.html.erb +141 -0
  19. data/lib/polylingo_chat/version.rb +1 -1
  20. data/lib/polylingo_chat.rb +0 -1
  21. metadata +11 -4
  22. data/lib/polylingo_chat/translate_job.rb +0 -65
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6491b6c4c4efb8f3a036ca5b603cf6d383d7ffd60804cf080dcb3cde055360d
4
- data.tar.gz: 9998d94782b87daf3a3546221f9f16be51bcd74a959f2eb4b2a823de97222fec
3
+ metadata.gz: 01ec0809c7463514bcbf9249500c51241cf10ed887a0cf3128ce5258922b8861
4
+ data.tar.gz: 90fdaebab28909605dab1da70779094b0283ec1a08c45f24cb597a317e084645
5
5
  SHA512:
6
- metadata.gz: 24c88ccebf1bf762ae31ed874a1d88e1afc45f6f0866c62a5f7ac61ae3b759528bd81738f147f275e0e29d458adf6f34c984b1e94b48f8ebc0760c4131f592be
7
- data.tar.gz: c361b6cfdca10f5952a848d57cc94f32775d0fb85594b0044c7ba277b180571c4bc8cad23bc77b0a012fbd132138716b14242a46673794f1ea8b03854db4d114
6
+ metadata.gz: a7546b1a0a4d4c75339a8a54edc3f487acb03d367ee18e4474926f804e432114a8b86a2f4a874d37c2ce5ad83bc63338f685c766f0251f0c4885206484804660
7
+ data.tar.gz: 4439ee11861544ec14a424be4887b3487b0f8258eae378e1f463ea2df5f0d41301dd793f1d3cece4977a8410dbfa80989f791f062cd5fb4cca3148154073707c
data/README.md CHANGED
@@ -13,11 +13,12 @@ Perfect for marketplaces, SaaS apps, CRMs, support systems, or global communitie
13
13
  - ๐ŸŒ **Optional AI translation** - works with or without translation
14
14
  - โš™๏ธ **Multi-provider support** โ€” OpenAI, Anthropic Claude, or Google Gemini
15
15
  - ๐Ÿงฉ **Rails Engine** โ€” mounts directly inside your app
16
- - ๐Ÿ‘ฅ **Conversations, Participants & Messages** models included
16
+ - ๐Ÿ“ฑ **API-only mode** โ€” works with Rails API applications (auto-detected)
17
+ - ๐Ÿ‘ฅ **Polymorphic associations** โ€” supports multiple participant types (User, Vendor, Customer, etc.)
17
18
  - ๐Ÿ”Œ **Works with any ActiveJob backend** (Sidekiq, Solid Queue, Delayed Job, etc.)
18
19
  - ๐Ÿ” **Secure & scoped** ActionCable channels
19
20
  - ๐Ÿงฑ **Extendable architecture** (custom UI, custom providers, custom storage)
20
- - ๐Ÿ’พ **Built-in caching** support for translations
21
+ - ๐Ÿ’พ **Database-backed translation caching** - translations stored and reused (zero API costs on retrieval)
21
22
  - ๐Ÿงช **RSpec testing** framework included
22
23
  - ๐Ÿ—„๏ธ **No Redis required** โ€” uses Solid Cable for WebSocket persistence
23
24
 
@@ -53,17 +54,24 @@ bin/rails db:migrate
53
54
  ```
54
55
 
55
56
  **The installer automatically:**
56
- - โœ… Creates migrations for Conversations, Participants, and Messages
57
- - โœ… Generates model files with associations
58
- - โœ… Sets up ActionCable channels (PolylinguoChatChannel)
59
- - โœ… Creates JavaScript files for real-time chat
60
- - โœ… Configures Solid Cable in `config/cable.yml`
61
- - โœ… Downloads ActionCable ESM module to `vendor/javascript`
62
- - โœ… Updates `config/routes.rb` with chat routes
63
- - โœ… Updates `config/importmap.rb` with required pins
64
- - โœ… Updates `app/javascript/application.js` with chat import
57
+ - โœ… Creates migrations for prefixed tables (`polylingo_chat_conversations`, `polylingo_chat_participants`, `polylingo_chat_messages`, `polylingo_chat_message_translations`)
58
+ - โœ… Generates namespaced models in `app/models/polylingo_chat/` directory
59
+ - โœ… Generates namespaced controllers in `app/controllers/polylingo_chat/` directory
60
+ - โœ… Sets up ActionCable channels (PolylinguoChatChannel) for full-stack apps
61
+ - โœ… Creates JavaScript files for real-time chat (full-stack only)
62
+ - โœ… Configures Solid Cable in `config/cable.yml` (full-stack only)
63
+ - โœ… Downloads ActionCable ESM module to `vendor/javascript` (full-stack only)
64
+ - โœ… Updates `config/routes.rb` with namespaced routes
65
+ - โœ… Updates `config/importmap.rb` with required pins (full-stack only)
66
+ - โœ… Updates `app/javascript/application.js` with chat import (full-stack only)
65
67
  - โœ… Creates `config/initializers/polylingo_chat.rb` for configuration
66
68
 
69
+ **Note:** Everything is fully namespaced:
70
+ - Models: `PolylingoChat::Conversation`, `PolylingoChat::Participant`, `PolylingoChat::Message`
71
+ - Controllers: `PolylingoChat::ConversationsController`, `PolylingoChat::MessagesController`
72
+ - Tables: `polylingo_chat_conversations`, `polylingo_chat_participants`, `polylingo_chat_messages`
73
+ - Routes: `/polylingo_chat/conversations`, `/polylingo_chat/messages`
74
+
67
75
  ### 3. Configure ActiveJob
68
76
  ```ruby
69
77
  # config/application.rb or config/environments/production.rb
@@ -143,8 +151,198 @@ bundle exec sidekiq # or bin/rails solid_queue:start, or bin/rails jobs:work
143
151
 
144
152
  ---
145
153
 
154
+ ## ๐Ÿ› ๏ธ Advanced Installation
155
+
156
+ ### API-Only Mode
157
+
158
+ PolylingoChat automatically detects Rails API applications and skips ActionCable/frontend setup. You can also explicitly enable API-only mode:
159
+
160
+ ```bash
161
+ bin/rails generate polylingo_chat:install --api-only
162
+ bin/rails db:migrate
163
+ ```
164
+
165
+ **API-only installation:**
166
+ - โœ… Creates migrations and models
167
+ - โœ… Creates API controllers (`Api::ConversationsController`, `Api::MessagesController`)
168
+ - โœ… Adds API routes under `/api` namespace
169
+ - โŒ Skips ActionCable channels
170
+ - โŒ Skips JavaScript files
171
+ - โŒ Skips Solid Cable setup
172
+
173
+ ### Using Polymorphic Associations
174
+
175
+ PolylingoChat supports multiple participant types out of the box. Instead of limiting conversations to just Users, you can have Vendors, Customers, Admins, or any other model as participants.
176
+
177
+ #### Example: Multi-Model Participants
178
+
179
+ ```ruby
180
+ # Create a conversation
181
+ conversation = PolylingoChat::Conversation.create!(title: "Support Ticket #123")
182
+
183
+ # Add different types of participants
184
+ user = User.find(1)
185
+ vendor = Vendor.find(5)
186
+ admin = Admin.find(2)
187
+
188
+ conversation.add_participant(user, role: 'customer')
189
+ conversation.add_participant(vendor, role: 'vendor')
190
+ conversation.add_participant(admin, role: 'support')
191
+
192
+ # Get all participants regardless of type
193
+ conversation.participantables
194
+ # => [#<User id: 1>, #<Vendor id: 5>, #<Admin id: 2>]
195
+
196
+ # Get participants of a specific type
197
+ conversation.participantables_of_type(Vendor)
198
+ # => [#<Vendor id: 5>]
199
+
200
+ # Backward compatibility - still works with User model
201
+ conversation.users # Returns all User participants
202
+ ```
203
+
204
+ #### Sending Messages with Polymorphic Senders
205
+
206
+ ```ruby
207
+ # Any model can send a message
208
+ vendor = Vendor.find(5)
209
+ PolylingoChat::Message.create!(
210
+ conversation: conversation,
211
+ sender: vendor, # Polymorphic - can be User, Vendor, Customer, etc.
212
+ body: "We've shipped your order!"
213
+ )
214
+
215
+ # Message model automatically handles sender_name
216
+ message.sender_name # Tries: name, full_name, email, or "Unknown"
217
+ ```
218
+
219
+ ---
220
+
146
221
  ## ๐ŸŽฏ Usage
147
222
 
223
+ ### Using the API Endpoints
224
+
225
+ PolylingoChat provides JSON API endpoints that work for both API-only and full-stack Rails applications:
226
+
227
+ #### Create a Conversation
228
+
229
+ ```bash
230
+ POST /polylingo_chat/conversations
231
+ Content-Type: application/json
232
+
233
+ {
234
+ "conversation": {
235
+ "title": "Project Discussion"
236
+ },
237
+ "participant_ids": [
238
+ { "type": "User", "id": 1, "role": "owner" },
239
+ { "type": "Vendor", "id": 5, "role": "participant" }
240
+ ]
241
+ }
242
+ ```
243
+
244
+ #### Send a Message
245
+
246
+ ```bash
247
+ POST /polylingo_chat/conversations/:conversation_id/messages
248
+ Content-Type: application/json
249
+
250
+ {
251
+ "message": {
252
+ "body": "Hello! How are you?",
253
+ "language": "en"
254
+ },
255
+ "sender_type": "User",
256
+ "sender_id": 1
257
+ }
258
+ ```
259
+
260
+ #### Get Conversation with Messages
261
+
262
+ ```bash
263
+ # Get conversation without specific language
264
+ GET /polylingo_chat/conversations/:id.json
265
+
266
+ # Get conversation with Spanish translations
267
+ GET /polylingo_chat/conversations/:id.json?lang=es
268
+
269
+ Response:
270
+ {
271
+ "id": 1,
272
+ "title": "Project Discussion",
273
+ "participants": [
274
+ {
275
+ "id": 1,
276
+ "type": "User",
277
+ "participant_id": 1,
278
+ "role": "owner",
279
+ "name": "John Doe"
280
+ },
281
+ {
282
+ "id": 2,
283
+ "type": "Vendor",
284
+ "participant_id": 5,
285
+ "role": "participant",
286
+ "name": "ACME Corp"
287
+ }
288
+ ],
289
+ "messages": [
290
+ {
291
+ "id": 1,
292
+ "body": "Hello! How are you?",
293
+ "language": "en",
294
+ "translated": true,
295
+ "translation": "ยกHola! ยฟCรณmo estรกs?",
296
+ "translation_language": "es",
297
+ "available_translations": [
298
+ {"language": "es", "text": "ยกHola! ยฟCรณmo estรกs?"},
299
+ {"language": "fr", "text": "Bonjour! Comment allez-vous?"}
300
+ ],
301
+ "sender_type": "User",
302
+ "sender_id": 1,
303
+ "sender_name": "John Doe",
304
+ "created_at": "2025-01-15T10:30:00Z"
305
+ }
306
+ ]
307
+ }
308
+ ```
309
+
310
+ **New in v0.4.0:** The `?lang=` parameter returns cached translations from the database with zero API calls. The `available_translations` array shows all cached translations for each message.
311
+
312
+ #### List All Messages in a Conversation
313
+
314
+ ```bash
315
+ # Get messages without specific language
316
+ GET /polylingo_chat/conversations/:conversation_id/messages.json
317
+
318
+ # Get messages with French translations (from cache)
319
+ GET /polylingo_chat/conversations/:conversation_id/messages.json?lang=fr
320
+
321
+ Response:
322
+ [
323
+ {
324
+ "id": 1,
325
+ "body": "Hello! How are you?",
326
+ "language": "en",
327
+ "translated": true,
328
+ "translation": "Bonjour! Comment allez-vous?",
329
+ "translation_language": "fr",
330
+ "available_translations": [
331
+ {"language": "es", "text": "ยกHola! ยฟCรณmo estรกs?"},
332
+ {"language": "fr", "text": "Bonjour! Comment allez-vous?"}
333
+ ],
334
+ "sender_type": "User",
335
+ "sender_id": 1,
336
+ "sender_name": "John Doe",
337
+ "conversation_id": 1,
338
+ "created_at": "2025-01-15T10:30:00Z",
339
+ "updated_at": "2025-01-15T10:30:05Z"
340
+ }
341
+ ]
342
+ ```
343
+
344
+ ### Full-Stack Usage (with ActionCable)
345
+
148
346
  ### 1. Set up your view
149
347
 
150
348
  First, expose the conversation and user IDs to JavaScript in your chat view:
@@ -165,14 +363,14 @@ First, expose the conversation and user IDs to JavaScript in your chat view:
165
363
 
166
364
  ```ruby
167
365
  # Create a conversation
168
- conversation = Conversation.create!(title: "Project Discussion")
366
+ conversation = PolylingoChat::Conversation.create!(title: "Project Discussion")
169
367
 
170
368
  # Add participants
171
- Participant.create!(conversation: conversation, user: user1)
172
- Participant.create!(conversation: conversation, user: user2)
369
+ conversation.add_participant(user1, role: 'member')
370
+ conversation.add_participant(user2, role: 'member')
173
371
 
174
372
  # Send a message
175
- Message.create!(
373
+ PolylingoChat::Message.create!(
176
374
  conversation: conversation,
177
375
  sender: user1,
178
376
  body: "Hello! How are you?"
@@ -280,25 +478,128 @@ Get key: https://ai.google.dev/
280
478
 
281
479
  ---
282
480
 
481
+ ## ๐Ÿ’พ Translation Caching (New in v0.4.0)
482
+
483
+ PolylingoChat now includes **database-backed translation caching** that dramatically reduces AI API costs and improves performance.
484
+
485
+ ### How Translation Caching Works
486
+
487
+ 1. **First Time Translation:**
488
+ - User sends a message โ†’ AI translates it for each participant's language
489
+ - Translations are saved to `polylingo_chat_message_translations` table
490
+ - One translation per language per message
491
+
492
+ 2. **Subsequent Retrievals:**
493
+ - Page reloads, API calls โ†’ translations loaded from database
494
+ - **Zero AI API calls** for cached translations
495
+ - Instant response times
496
+
497
+ ### Benefits
498
+
499
+ - โœ… **Massive cost savings** - Translate once, retrieve unlimited times
500
+ - โœ… **Lightning fast** - Database queries vs AI API calls
501
+ - โœ… **Works offline** - No dependency on AI service availability for viewing
502
+ - โœ… **Perfect for mobile** - Mobile apps get instant cached translations
503
+ - โœ… **Scales effortlessly** - Database caching scales with your infrastructure
504
+
505
+ ### API Usage Example
506
+
507
+ ```bash
508
+ # First time - translates and caches
509
+ curl -X POST http://localhost:3000/polylingo_chat/conversations/1/messages.json \
510
+ -H "Content-Type: application/json" \
511
+ -d '{"message":{"body":"Hello!"},"sender_type":"User","sender_id":1}'
512
+
513
+ # Response includes available_translations
514
+ {
515
+ "id": 15,
516
+ "body": "Hello!",
517
+ "language": "en",
518
+ "available_translations": [
519
+ {"language": "es", "text": "ยกHola!"},
520
+ {"language": "fr", "text": "Bonjour!"}
521
+ ]
522
+ }
523
+
524
+ # Later retrievals - instant, no AI API calls
525
+ curl http://localhost:3000/polylingo_chat/conversations/1/messages.json?lang=es
526
+
527
+ # Returns cached Spanish translation immediately
528
+ {
529
+ "translation": "ยกHola!",
530
+ "translation_language": "es",
531
+ "available_translations": [...]
532
+ }
533
+ ```
534
+
535
+ ### Database Schema
536
+
537
+ The `polylingo_chat_message_translations` table stores all cached translations:
538
+
539
+ ```ruby
540
+ # Schema
541
+ create_table :polylingo_chat_message_translations do |t|
542
+ t.references :message, null: false
543
+ t.string :language, null: false # ISO 639-1 code (e.g., 'es', 'fr')
544
+ t.text :translated_text, null: false
545
+ t.timestamps
546
+
547
+ t.index [:message_id, :language], unique: true
548
+ end
549
+ ```
550
+
551
+ ---
552
+
283
553
  ## ๐Ÿ—๏ธ How It Works
284
554
 
285
555
  ### Message Flow
286
556
 
287
557
  1. User sends a message โ†’ saved to database
288
- 2. `Message#after_create_commit` enqueues job
558
+ 2. `Message#after_create_commit` enqueues translation job
289
559
  3. Job runs in background via your ActiveJob adapter
290
- 4. **If API key present:** Message translated for each participant
560
+ 4. **If API key present:**
561
+ - Message translated for each participant's language
562
+ - Translations saved to `message_translations` table (cached)
563
+ - Future retrievals use cached translations (zero API calls)
291
564
  5. **If no API key:** Original message used for all participants
292
565
  6. Messages broadcast via ActionCable (using Solid Cable)
293
566
  7. Recipients see messages in real-time
294
567
 
295
568
  ### Models
296
569
 
297
- **Conversation** - Chat conversation with many participants and messages
298
-
299
- **Participant** - Join table linking Users to Conversations
300
-
301
- **Message** - Individual chat message with translation tracking
570
+ All models are namespaced under `PolylingoChat::` and use the `polylingo_chat_` table prefix to prevent conflicts:
571
+
572
+ **PolylingoChat::Conversation** - Chat conversation with many participants and messages
573
+ - Table: `polylingo_chat_conversations`
574
+ - Location: `app/models/polylingo_chat/conversation.rb`
575
+ - `participantables` - Returns all participant records (polymorphic)
576
+ - `participantables_of_type(klass)` - Returns participants of a specific type
577
+ - `users` - Returns User participants (backward compatibility)
578
+ - `add_participant(record, role: nil)` - Adds any model as a participant
579
+ - `includes_participant?(record)` - Checks if a record is a participant
580
+
581
+ **PolylingoChat::Participant** - Join table with polymorphic associations
582
+ - Table: `polylingo_chat_participants`
583
+ - Location: `app/models/polylingo_chat/participant.rb`
584
+ - `participantable` - The actual participant (User, Vendor, Customer, etc.)
585
+ - Supports multiple model types via polymorphic association
586
+ - `user` alias for backward compatibility
587
+
588
+ **PolylingoChat::Message** - Individual chat message with translation tracking
589
+ - Table: `polylingo_chat_messages`
590
+ - Location: `app/models/polylingo_chat/message.rb`
591
+ - `sender` - Polymorphic association (User, Vendor, Customer, etc.)
592
+ - `sender_name` - Helper method that works with any sender type
593
+ - `translations` - Has many MessageTranslation records (cached translations)
594
+ - `translation_for(language)` - Returns cached translation or original body
595
+ - Automatic translation via background job if API key present
596
+
597
+ **PolylingoChat::MessageTranslation** - Cached translations (New in v0.4.0)
598
+ - Table: `polylingo_chat_message_translations`
599
+ - Location: `app/models/polylingo_chat/message_translation.rb`
600
+ - Stores one translation per language per message
601
+ - Unique index on `[message_id, language]`
602
+ - Dramatically reduces AI API costs by caching translations
302
603
 
303
604
  ### Solid Cable (Database-Backed WebSockets)
304
605
 
@@ -395,6 +696,66 @@ Perfect for:
395
696
  - Multi-language support systems
396
697
  - Cross-border collaboration
397
698
 
699
+ ### API-Only Mode
700
+ Perfect for:
701
+ - Mobile app backends (iOS, Android, React Native)
702
+ - SPA frontends (React, Vue, Angular)
703
+ - Microservices architectures
704
+ - Custom WebSocket implementations
705
+ - Multi-platform applications
706
+
707
+ ### Polymorphic Participants
708
+ Perfect for:
709
+ - Marketplace chats (Buyers โ†” Sellers โ†” Support)
710
+ - Multi-tenant systems (Users โ†” Vendors โ†” Admins)
711
+ - B2B platforms (Companies โ†” Representatives)
712
+ - Healthcare apps (Patients โ†” Doctors โ†” Nurses)
713
+
714
+ ---
715
+
716
+ ## ๐ŸŒ Frontend Integration
717
+
718
+ PolylingoChat works seamlessly with frontend frameworks like **React**, **Next.js**, **Vue**, **Angular**, and more.
719
+
720
+ For a comprehensive guide on integrating with frontend applications, including:
721
+ - Complete API endpoint documentation
722
+ - React component examples with TypeScript
723
+ - Next.js integration patterns
724
+ - Real-time update strategies (polling, WebSocket, third-party services)
725
+ - Authentication patterns
726
+ - Translation query parameters
727
+
728
+ See the **[Frontend Integration Guide](FRONTEND_INTEGRATION.md)** for complete details.
729
+
730
+ ### Quick Example (React + TypeScript)
731
+
732
+ ```typescript
733
+ // Fetch messages with translation
734
+ const messages = await fetch(
735
+ `/polylingo_chat/conversations/1/messages?translate=true&target_language=es`,
736
+ {
737
+ headers: {
738
+ 'Authorization': `Bearer ${token}`,
739
+ 'Content-Type': 'application/json'
740
+ }
741
+ }
742
+ ).then(r => r.json());
743
+
744
+ // Send a message
745
+ await fetch('/polylingo_chat/conversations/1/messages', {
746
+ method: 'POST',
747
+ headers: {
748
+ 'Authorization': `Bearer ${token}`,
749
+ 'Content-Type': 'application/json'
750
+ },
751
+ body: JSON.stringify({
752
+ message: { body: "Hello!" },
753
+ sender_type: "User",
754
+ sender_id: 1
755
+ })
756
+ });
757
+ ```
758
+
398
759
  ---
399
760
 
400
761
  ## ๐Ÿค Contributing
@@ -0,0 +1,72 @@
1
+ ===============================================================================
2
+
3
+ PolylingoChat has been installed successfully!
4
+
5
+ Models, Controllers, Views, and Jobs are provided by the gem (like Devise).
6
+ They are located in the gem's app/ directory and can be overridden by creating
7
+ files with the same path in your application's app/ directory.
8
+
9
+ Next steps:
10
+
11
+ 1. Run migrations:
12
+ rails db:migrate
13
+
14
+ 2. Ensure your User model has a `preferred_language` column (optional):
15
+ rails g migration AddPreferredLanguageToUsers preferred_language:string
16
+ rails db:migrate
17
+
18
+ 3. Configure your AI provider in config/initializers/polylingo_chat.rb:
19
+ - Set config.api_key with your API key
20
+ - Choose your provider (:openai, :anthropic, or :gemini)
21
+ - Or leave api_key as nil for chat-only mode (no translation)
22
+
23
+ 4. Set up Solid Cable for production:
24
+ - Already configured for development
25
+ - For production, ensure database is configured in config/cable.yml
26
+
27
+ 5. For ActionCable/real-time chat (full-stack apps):
28
+ # Already set up automatically during installation
29
+ # JavaScript files and channels are configured
30
+
31
+ 6. Add this to your User model:
32
+ # app/models/user.rb
33
+ class User < ApplicationRecord
34
+ # Optional: for chat functionality
35
+ has_many :polylingo_chat_participants,
36
+ class_name: "PolylingoChat::Participant",
37
+ as: :participantable,
38
+ dependent: :destroy
39
+ has_many :polylingo_chat_conversations,
40
+ through: :polylingo_chat_participants,
41
+ source: :conversation
42
+ has_many :polylingo_chat_messages,
43
+ class_name: "PolylingoChat::Message",
44
+ as: :sender,
45
+ dependent: :destroy
46
+ end
47
+
48
+ 7. To customize models, controllers, or views, create files in your app:
49
+ # app/models/polylingo_chat/conversation.rb
50
+ module PolylingoChat
51
+ class Conversation < ApplicationRecord
52
+ # Your custom code here - this will override the gem's version
53
+ end
54
+ end
55
+
56
+ # app/controllers/polylingo_chat/conversations_controller.rb
57
+ module PolylingoChat
58
+ class ConversationsController < ApplicationController
59
+ # Your custom code here - this will override the gem's version
60
+ end
61
+ end
62
+
63
+ 8. Visit your conversations:
64
+ - HTML: http://localhost:3000/polylingo_chat/conversations
65
+ - JSON API: http://localhost:3000/polylingo_chat/conversations.json
66
+
67
+ For frontend integration (React, Next.js, Vue, Angular):
68
+ See: FRONTEND_INTEGRATION.md in the gem's directory
69
+
70
+ For more information, visit: https://github.com/AdwareTechnologies/polylingo_chat
71
+
72
+ ===============================================================================
@@ -8,26 +8,30 @@ module PolylingoChat
8
8
 
9
9
  source_root File.expand_path('templates', __dir__)
10
10
 
11
+ class_option :api_only, type: :boolean, default: nil,
12
+ desc: "Install for API-only Rails app (skip ActionCable/frontend)"
13
+
11
14
  desc "Installs PolylingoChat with real-time multilingual chat"
12
15
 
16
+ def initialize(*args)
17
+ super
18
+ @api_only = detect_api_only_mode
19
+ end
20
+
13
21
  def self.next_migration_number(dirname)
14
22
  next_migration_number = current_migration_number(dirname) + 1
15
23
  ActiveRecord::Migration.next_migration_number(next_migration_number)
16
24
  end
17
25
 
18
26
  def copy_migrations
19
- migration_template "create_conversations.rb", "db/migrate/create_conversations.rb"
20
- migration_template "create_participants.rb", "db/migrate/create_participants.rb"
21
- migration_template "create_messages.rb", "db/migrate/create_messages.rb"
22
- end
23
-
24
- def create_models
25
- template "models/conversation.rb", "app/models/conversation.rb"
26
- template "models/participant.rb", "app/models/participant.rb"
27
- template "models/message.rb", "app/models/message.rb"
27
+ migration_template "create_conversations.rb", "db/migrate/create_polylingo_chat_conversations.rb"
28
+ migration_template "create_participants.rb", "db/migrate/create_polylingo_chat_participants.rb"
29
+ migration_template "create_messages.rb", "db/migrate/create_polylingo_chat_messages.rb"
30
+ migration_template "create_message_translations.rb", "db/migrate/create_polylingo_chat_message_translations.rb"
28
31
  end
29
32
 
30
33
  def create_channels
34
+ return if @api_only
31
35
  empty_directory "app/channels/application_cable"
32
36
  template "channels/application_cable/channel.rb", "app/channels/application_cable/channel.rb"
33
37
  template "channels/application_cable/connection.rb", "app/channels/application_cable/connection.rb"
@@ -35,6 +39,7 @@ module PolylingoChat
35
39
  end
36
40
 
37
41
  def create_javascript_files
42
+ return if @api_only
38
43
  empty_directory "app/javascript/channels"
39
44
  template "javascript/chat.js", "app/javascript/chat.js"
40
45
  template "javascript/channels/consumer.js", "app/javascript/channels/consumer.js"
@@ -42,6 +47,7 @@ module PolylingoChat
42
47
  end
43
48
 
44
49
  def configure_cable
50
+ return if @api_only
45
51
  if File.exist?("config/cable.yml")
46
52
  gsub_file "config/cable.yml", /^development:\s*\n\s+adapter:\s+\w+.*$/m do |match|
47
53
  "development:\n adapter: solid_cable\n polling_interval: 0.1.seconds\n message_retention: 1.day"
@@ -50,26 +56,28 @@ module PolylingoChat
50
56
  end
51
57
 
52
58
  def setup_solid_cable
59
+ return if @api_only
53
60
  say "Setting up Solid Cable...", :green
54
61
  rails_command "solid_cable:install", abort_on_failure: false
55
62
  end
56
63
 
57
64
  def download_actioncable
65
+ return if @api_only
58
66
  empty_directory "vendor/javascript"
59
67
  say "Downloading ActionCable ESM module...", :green
60
68
  run "curl -o vendor/javascript/@rails--actioncable.js https://ga.jspm.io/npm:@rails/actioncable@7.1.3/app/assets/javascripts/actioncable.esm.js"
61
69
  end
62
70
 
63
71
  def add_routes
64
- route 'mount ActionCable.server => "/cable"'
65
- route <<~RUBY
66
- resources :conversations do
67
- resources :messages, only: [:create]
68
- end
69
- RUBY
72
+ unless @api_only
73
+ route 'mount ActionCable.server => "/cable"'
74
+ end
75
+
76
+ route 'mount PolylingoChat::Engine => "/polylingo_chat"'
70
77
  end
71
78
 
72
79
  def update_importmap
80
+ return if @api_only
73
81
  if File.exist?("config/importmap.rb")
74
82
  append_to_file "config/importmap.rb" do
75
83
  <<~RUBY
@@ -84,6 +92,7 @@ module PolylingoChat
84
92
  end
85
93
 
86
94
  def update_application_js
95
+ return if @api_only
87
96
  if File.exist?("app/javascript/application.js")
88
97
  append_to_file "app/javascript/application.js" do
89
98
  <<~JS
@@ -102,6 +111,26 @@ module PolylingoChat
102
111
  def show_readme
103
112
  readme "README" if behavior == :invoke
104
113
  end
114
+
115
+ private
116
+
117
+ def detect_api_only_mode
118
+ # Use explicit option if provided
119
+ return options[:api_only] unless options[:api_only].nil?
120
+
121
+ # Auto-detect: Check if this is an API-only Rails app
122
+ # API-only apps typically don't have ActionView or have it explicitly disabled
123
+ api_only = !defined?(ActionView::Base) ||
124
+ (defined?(Rails.application) && Rails.application.config.respond_to?(:api_only) && Rails.application.config.api_only)
125
+
126
+ if api_only
127
+ say "Detected API-only Rails application. Skipping ActionCable/frontend setup.", :yellow
128
+ else
129
+ say "Installing with full-stack support (ActionCable + frontend).", :green
130
+ end
131
+
132
+ api_only
133
+ end
105
134
  end
106
135
  end
107
136
  end
@@ -1,4 +1,4 @@
1
- class PolylinguoChatChannel < ApplicationCable::Channel
1
+ class PolylingoChatChannel < ApplicationCable::Channel
2
2
  def subscribed
3
3
  conversation_id = params[:conversation_id]
4
4