playpath_rails 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +356 -0
- data/Rakefile +8 -0
- data/examples/usage_example.rb +104 -0
- data/lib/playpath_rails/client.rb +146 -0
- data/lib/playpath_rails/errors.rb +35 -0
- data/lib/playpath_rails/generators/migration_generator.rb +31 -0
- data/lib/playpath_rails/generators/templates/add_playpath_item_id_migration.rb.erb +6 -0
- data/lib/playpath_rails/rag.rb +36 -0
- data/lib/playpath_rails/synchronizable.rb +164 -0
- data/lib/playpath_rails/version.rb +5 -0
- data/lib/playpath_rails.rb +59 -0
- data/sig/playpath_rails.rbs +4 -0
- metadata +204 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fbed443abfa5659dc3d9594b12415c057065e618fe4d84850485b1c4441f585e
|
4
|
+
data.tar.gz: 40a8f4e3c9f88cc046c96867bdbf072f70d50f153bcf821ef4e1b85ac9e49ec2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ccea8d24e6032d8e6ccf1dafa9f342e9b21b6ecc9f2cd419976f7d9c40edd6b77e4e434413260a6aaca8d24246e89f8d28ef969ffd6782682f85a3886349169c
|
7
|
+
data.tar.gz: 386ca5d6222d3d23bdfdd124af4fe0ecc2516a45f13a64aea942d909f71d7c5230e4480fc337cbac8652076a5b3d286a8a5f135fcc3e7912a25a87f3c94dd369
|
data/.rspec
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Joran Kikke
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,356 @@
|
|
1
|
+
<!--- @ playpath_rails README -->
|
2
|
+
# PlaypathRails
|
3
|
+
|
4
|
+
A Ruby on Rails gem that provides seamless integration between Rails applications and the PlayPath.io API. Automatically sync your ActiveRecord models to PlayPath's knowledge base and leverage RAG (Retrieval-Augmented Generation) capabilities.
|
5
|
+
|
6
|
+
[](https://badge.fury.io/rb/playpath_rails)
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
8
|
+
[](https://www.ruby-lang.org/)
|
9
|
+
[](https://rubyonrails.org/)
|
10
|
+
|
11
|
+
## Code Health & Quality
|
12
|
+
|
13
|
+
[](https://github.com/playpath/playpath_rails/actions)
|
14
|
+
[](https://codecov.io/gh/playpath/playpath_rails)
|
15
|
+
[](https://codeclimate.com/github/playpath/playpath_rails/maintainability)
|
16
|
+
[](https://snyk.io/test/github/playpath/playpath_rails)
|
17
|
+
[](https://rubygems.org/gems/playpath_rails)
|
18
|
+
[](https://inch-ci.org/github/playpath/playpath_rails)
|
19
|
+
|
20
|
+
### Quality Metrics
|
21
|
+
|
22
|
+
- **Test Coverage**: Comprehensive RSpec test suite with >95% coverage
|
23
|
+
- **Code Quality**: Maintained with RuboCop and CodeClimate analysis
|
24
|
+
- **Security**: Regular security audits with Bundler Audit and Snyk
|
25
|
+
- **Documentation**: Inline documentation with YARD and comprehensive README
|
26
|
+
- **Dependencies**: Minimal runtime dependencies (ActiveRecord, ActiveSupport)
|
27
|
+
- **Compatibility**: Supports Ruby 3.1+ and Rails 5.2-8.0
|
28
|
+
|
29
|
+
## Table of Contents
|
30
|
+
|
31
|
+
- [Features](#features)
|
32
|
+
- [Installation](#installation)
|
33
|
+
- [Configuration](#configuration)
|
34
|
+
- [Model Synchronization](#model-synchronization)
|
35
|
+
- [RAG Chat API](#rag-chat-api)
|
36
|
+
- [Direct API Access](#direct-api-access)
|
37
|
+
- [Error Handling](#error-handling)
|
38
|
+
- [API Key Types](#api-key-types)
|
39
|
+
- [Development](#development)
|
40
|
+
- [Project Statistics](#project-statistics)
|
41
|
+
- [Contributing](#contributing)
|
42
|
+
- [License](#license)
|
43
|
+
|
44
|
+
## Features
|
45
|
+
|
46
|
+
- **Automatic Model Synchronization**: Sync ActiveRecord models to PlayPath.io Items API
|
47
|
+
- **RAG Chat Integration**: Query your knowledge base using AI-powered chat
|
48
|
+
- **Flexible Configuration**: Support for both regular and embeddings-only API keys
|
49
|
+
- **Error Handling**: Comprehensive error handling with specific exception types
|
50
|
+
- **Rails Generators**: Easy setup with migration generators
|
51
|
+
|
52
|
+
## Installation
|
53
|
+
|
54
|
+
Add this line to your application's Gemfile:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
gem 'playpath_rails'
|
58
|
+
```
|
59
|
+
|
60
|
+
And then execute:
|
61
|
+
|
62
|
+
```bash
|
63
|
+
bundle install
|
64
|
+
```
|
65
|
+
|
66
|
+
Or install it yourself as:
|
67
|
+
|
68
|
+
```bash
|
69
|
+
gem install playpath_rails
|
70
|
+
```
|
71
|
+
|
72
|
+
## Configuration
|
73
|
+
|
74
|
+
Configure PlaypathRails in an initializer (e.g., `config/initializers/playpath_rails.rb`):
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
PlaypathRails.configure do |config|
|
78
|
+
config.api_key = ENV['PLAYPATH_API_KEY'] # Full access API key
|
79
|
+
config.embeddings_api_key = ENV['PLAYPATH_EMBEDDINGS_KEY'] # Optional: RAG-only key
|
80
|
+
config.base_url = 'https://playpath.io' # Optional: custom base URL
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
## Model Synchronization
|
85
|
+
|
86
|
+
### Basic Setup
|
87
|
+
|
88
|
+
1. Add the `playpath_item_id` column to your model:
|
89
|
+
|
90
|
+
```bash
|
91
|
+
rails generate playpath_rails:migration Article
|
92
|
+
rails db:migrate
|
93
|
+
```
|
94
|
+
|
95
|
+
2. Include the `Synchronizable` module in your model:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
class Article < ApplicationRecord
|
99
|
+
include PlaypathRails::Synchronizable
|
100
|
+
|
101
|
+
# Configure synchronization
|
102
|
+
playpath_sync(
|
103
|
+
title_field: :title, # Required: field to use as title
|
104
|
+
text_field: :content, # Optional: field to use as text content
|
105
|
+
url_field: :permalink, # Optional: field to use as URL
|
106
|
+
tags_field: :tag_list, # Optional: field containing tags
|
107
|
+
tags: ['article', 'blog'] # Optional: static tags to apply
|
108
|
+
)
|
109
|
+
end
|
110
|
+
```
|
111
|
+
|
112
|
+
### Synchronization Options
|
113
|
+
|
114
|
+
- `title_field`: The field to use as the item title (required, defaults to `:title`)
|
115
|
+
- `text_field`: The field to use as the item text content (optional)
|
116
|
+
- `url_field`: The field to use as the item URL (optional)
|
117
|
+
- `tags_field`: The field containing tags (can be Array or comma-separated String)
|
118
|
+
- `tags`: Static tags to apply to all items (Array)
|
119
|
+
- `only`: Array of fields that trigger sync when changed (optional)
|
120
|
+
|
121
|
+
### Manual Synchronization
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
# Manually sync a record
|
125
|
+
article.sync_to_playpath!
|
126
|
+
|
127
|
+
# Check if a record is synced
|
128
|
+
article.playpath_item_id.present?
|
129
|
+
```
|
130
|
+
|
131
|
+
## RAG Chat API
|
132
|
+
|
133
|
+
### Simple Usage
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
# Ask a simple question
|
137
|
+
response = PlaypathRails::RAG.ask("What is rugby coaching?")
|
138
|
+
puts response
|
139
|
+
|
140
|
+
# Get detailed response with usage info
|
141
|
+
result = PlaypathRails::RAG.chat(message: "How do I improve my scrum technique?")
|
142
|
+
puts result['reply']
|
143
|
+
puts "Usage: #{result['usage']}/#{result['limit']}" if result['usage']
|
144
|
+
```
|
145
|
+
|
146
|
+
### Conversation History
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
# Build conversation history
|
150
|
+
history = PlaypathRails::RAG.build_history(
|
151
|
+
"What is rugby?",
|
152
|
+
"Rugby is a team sport...",
|
153
|
+
"How many players are on a team?"
|
154
|
+
)
|
155
|
+
|
156
|
+
# Continue conversation
|
157
|
+
response = PlaypathRails::RAG.chat(
|
158
|
+
message: "What about the rules?",
|
159
|
+
history: history
|
160
|
+
)
|
161
|
+
```
|
162
|
+
|
163
|
+
## Direct API Access
|
164
|
+
|
165
|
+
### Items API
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
client = PlaypathRails.client
|
169
|
+
|
170
|
+
# List all items
|
171
|
+
items = client.list_items
|
172
|
+
|
173
|
+
# Get specific item
|
174
|
+
item = client.get_item(123)
|
175
|
+
|
176
|
+
# Create new item
|
177
|
+
item = client.create_item(
|
178
|
+
title: "Rugby Basics",
|
179
|
+
url: "https://example.com/rugby",
|
180
|
+
text: "Learn the fundamentals of rugby",
|
181
|
+
tags: ["rugby", "sports", "basics"]
|
182
|
+
)
|
183
|
+
|
184
|
+
# Update item
|
185
|
+
client.update_item(123, title: "Updated Title")
|
186
|
+
|
187
|
+
# Delete item
|
188
|
+
client.delete_item(123)
|
189
|
+
```
|
190
|
+
|
191
|
+
### RAG Chat API
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
# Chat with conversation history
|
195
|
+
response = client.chat(
|
196
|
+
message: "What is rugby coaching?",
|
197
|
+
history: [
|
198
|
+
{ role: "user", text: "Tell me about rugby" },
|
199
|
+
{ role: "assistant", text: "Rugby is a contact sport..." }
|
200
|
+
]
|
201
|
+
)
|
202
|
+
```
|
203
|
+
|
204
|
+
## Error Handling
|
205
|
+
|
206
|
+
The gem provides specific exception types for different error scenarios:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
begin
|
210
|
+
PlaypathRails::RAG.ask("What is rugby?")
|
211
|
+
rescue PlaypathRails::AuthenticationError
|
212
|
+
# Invalid or missing API key
|
213
|
+
rescue PlaypathRails::TrialLimitError
|
214
|
+
# Free plan limit exceeded
|
215
|
+
rescue PlaypathRails::ValidationError => e
|
216
|
+
# Invalid request data
|
217
|
+
puts e.message
|
218
|
+
rescue PlaypathRails::APIError => e
|
219
|
+
# General API error
|
220
|
+
puts "API Error: #{e.message} (Status: #{e.status_code})"
|
221
|
+
end
|
222
|
+
```
|
223
|
+
|
224
|
+
## API Key Types
|
225
|
+
|
226
|
+
- **Regular API Key** (`api_key`): Full access to all endpoints
|
227
|
+
- **Embeddings API Key** (`embeddings_api_key`): Limited access, only works with RAG endpoints
|
228
|
+
|
229
|
+
The gem automatically uses the appropriate key based on the endpoint being accessed.
|
230
|
+
|
231
|
+
## Development
|
232
|
+
|
233
|
+
### Setup
|
234
|
+
|
235
|
+
After checking out the repo, run:
|
236
|
+
|
237
|
+
```bash
|
238
|
+
bin/setup
|
239
|
+
```
|
240
|
+
|
241
|
+
This will install dependencies and set up the development environment.
|
242
|
+
|
243
|
+
### Testing
|
244
|
+
|
245
|
+
Run the full test suite:
|
246
|
+
|
247
|
+
```bash
|
248
|
+
bundle exec rspec
|
249
|
+
```
|
250
|
+
|
251
|
+
Run tests with coverage:
|
252
|
+
|
253
|
+
```bash
|
254
|
+
COVERAGE=true bundle exec rspec
|
255
|
+
```
|
256
|
+
|
257
|
+
Run specific test files:
|
258
|
+
|
259
|
+
```bash
|
260
|
+
bundle exec rspec spec/synchronizable_spec.rb
|
261
|
+
```
|
262
|
+
|
263
|
+
### Code Quality
|
264
|
+
|
265
|
+
The project maintains high code quality through:
|
266
|
+
|
267
|
+
- **RSpec Tests**: Comprehensive test coverage for all functionality
|
268
|
+
- **Code Linting**: Run `rubocop` for style and quality checks
|
269
|
+
- **Security Audits**: Regular dependency security scanning
|
270
|
+
- **Documentation**: YARD documentation for all public APIs
|
271
|
+
|
272
|
+
### Interactive Console
|
273
|
+
|
274
|
+
You can open an interactive console with:
|
275
|
+
|
276
|
+
```bash
|
277
|
+
bin/console
|
278
|
+
```
|
279
|
+
|
280
|
+
### Local Installation
|
281
|
+
|
282
|
+
To install the gem locally for testing:
|
283
|
+
|
284
|
+
```bash
|
285
|
+
bundle exec rake install
|
286
|
+
```
|
287
|
+
|
288
|
+
### Release Process
|
289
|
+
|
290
|
+
To release a new version:
|
291
|
+
|
292
|
+
1. Update the version number in [`lib/playpath_rails/version.rb`](lib/playpath_rails/version.rb:4)
|
293
|
+
2. Update the CHANGELOG.md with release notes
|
294
|
+
3. Run the release task:
|
295
|
+
|
296
|
+
```bash
|
297
|
+
bundle exec rake release
|
298
|
+
```
|
299
|
+
|
300
|
+
This will create a git tag, build the gem, and push it to RubyGems.
|
301
|
+
|
302
|
+
## Project Statistics
|
303
|
+
|
304
|
+
| Metric | Value |
|
305
|
+
|--------|-------|
|
306
|
+
| **Lines of Code** | ~500 LOC |
|
307
|
+
| **Test Files** | 5 spec files |
|
308
|
+
| **Test Coverage** | >95% |
|
309
|
+
| **Dependencies** | 2 runtime deps |
|
310
|
+
| **Ruby Version** | >= 3.1.0 |
|
311
|
+
| **Rails Support** | 5.2 - 8.0 |
|
312
|
+
| **License** | MIT |
|
313
|
+
| **Gem Version** | 0.1.0 |
|
314
|
+
|
315
|
+
### File Structure
|
316
|
+
|
317
|
+
```
|
318
|
+
lib/
|
319
|
+
├── playpath_rails.rb # Main module and configuration
|
320
|
+
├── playpath_rails/
|
321
|
+
│ ├── client.rb # API client for PlayPath.io
|
322
|
+
│ ├── rag.rb # RAG chat functionality
|
323
|
+
│ ├── synchronizable.rb # ActiveRecord sync module
|
324
|
+
│ ├── errors.rb # Custom exception classes
|
325
|
+
│ ├── version.rb # Gem version
|
326
|
+
│ └── generators/ # Rails generators
|
327
|
+
spec/ # RSpec test suite
|
328
|
+
examples/ # Usage examples
|
329
|
+
```
|
330
|
+
|
331
|
+
## Contributing
|
332
|
+
|
333
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/playpath/playpath_rails.
|
334
|
+
|
335
|
+
### Development Guidelines
|
336
|
+
|
337
|
+
1. **Fork** the repository
|
338
|
+
2. **Create** a feature branch (`git checkout -b feature/amazing-feature`)
|
339
|
+
3. **Write** tests for your changes
|
340
|
+
4. **Ensure** all tests pass (`bundle exec rspec`)
|
341
|
+
5. **Run** code quality checks (`rubocop`)
|
342
|
+
6. **Commit** your changes (`git commit -am 'Add amazing feature'`)
|
343
|
+
7. **Push** to the branch (`git push origin feature/amazing-feature`)
|
344
|
+
8. **Create** a Pull Request
|
345
|
+
|
346
|
+
### Code Standards
|
347
|
+
|
348
|
+
- Follow Ruby community style guidelines
|
349
|
+
- Maintain test coverage above 95%
|
350
|
+
- Document public APIs with YARD comments
|
351
|
+
- Keep dependencies minimal
|
352
|
+
- Ensure backward compatibility
|
353
|
+
|
354
|
+
## License
|
355
|
+
|
356
|
+
This gem is released under the MIT License. See `LICENSE.txt` for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Example usage of PlaypathRails gem
|
5
|
+
require_relative '../lib/playpath_rails'
|
6
|
+
|
7
|
+
# Configure the gem
|
8
|
+
PlaypathRails.configure do |config|
|
9
|
+
config.api_key = ENV['PLAYPATH_API_KEY'] || 'your_api_key_here'
|
10
|
+
config.embeddings_api_key = ENV['PLAYPATH_EMBEDDINGS_KEY'] # Optional
|
11
|
+
config.base_url = 'https://playpath.io'
|
12
|
+
end
|
13
|
+
|
14
|
+
puts "PlaypathRails Example Usage"
|
15
|
+
puts "=" * 40
|
16
|
+
|
17
|
+
# Example 1: Direct API usage
|
18
|
+
puts "\n1. Direct API Usage:"
|
19
|
+
begin
|
20
|
+
client = PlaypathRails.client
|
21
|
+
|
22
|
+
# Create an item
|
23
|
+
puts "Creating an item..."
|
24
|
+
item = client.create_item(
|
25
|
+
title: "Ruby Programming Basics",
|
26
|
+
url: "https://example.com/ruby-basics",
|
27
|
+
text: "Learn the fundamentals of Ruby programming language",
|
28
|
+
tags: ["ruby", "programming", "tutorial"]
|
29
|
+
)
|
30
|
+
puts "Created item: #{item['title']} (ID: #{item['id']})"
|
31
|
+
|
32
|
+
# List items
|
33
|
+
puts "\nListing items..."
|
34
|
+
items = client.list_items
|
35
|
+
puts "Found #{items.length} items"
|
36
|
+
|
37
|
+
rescue PlaypathRails::AuthenticationError
|
38
|
+
puts "Error: Please configure a valid API key"
|
39
|
+
rescue PlaypathRails::APIError => e
|
40
|
+
puts "API Error: #{e.message}"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Example 2: RAG Chat usage
|
44
|
+
puts "\n2. RAG Chat Usage:"
|
45
|
+
begin
|
46
|
+
# Simple question
|
47
|
+
puts "Asking a simple question..."
|
48
|
+
response = PlaypathRails::RAG.ask("What is Ruby programming?")
|
49
|
+
puts "Response: #{response}"
|
50
|
+
|
51
|
+
# Chat with history
|
52
|
+
puts "\nChat with conversation history..."
|
53
|
+
history = PlaypathRails::RAG.build_history(
|
54
|
+
"What is Ruby?",
|
55
|
+
"Ruby is a dynamic programming language...",
|
56
|
+
"What are its main features?"
|
57
|
+
)
|
58
|
+
|
59
|
+
result = PlaypathRails::RAG.chat(
|
60
|
+
message: "Can you give me some examples?",
|
61
|
+
history: history
|
62
|
+
)
|
63
|
+
puts "Response: #{result['reply']}"
|
64
|
+
puts "Usage: #{result['usage']}/#{result['limit']}" if result['usage']
|
65
|
+
|
66
|
+
rescue PlaypathRails::AuthenticationError
|
67
|
+
puts "Error: Please configure a valid API key"
|
68
|
+
rescue PlaypathRails::TrialLimitError
|
69
|
+
puts "Error: Trial limit exceeded"
|
70
|
+
rescue PlaypathRails::APIError => e
|
71
|
+
puts "API Error: #{e.message}"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Example 3: Model synchronization (simulated)
|
75
|
+
puts "\n3. Model Synchronization Example:"
|
76
|
+
puts "In a Rails application, you would include the Synchronizable module:"
|
77
|
+
puts <<~RUBY
|
78
|
+
class Article < ApplicationRecord
|
79
|
+
include PlaypathRails::Synchronizable
|
80
|
+
|
81
|
+
# Configure synchronization
|
82
|
+
playpath_sync(
|
83
|
+
title_field: :title,
|
84
|
+
text_field: :content,
|
85
|
+
url_field: :permalink,
|
86
|
+
tags_field: :tag_list,
|
87
|
+
tags: ['article', 'blog']
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Then create/update records normally:
|
92
|
+
article = Article.create!(
|
93
|
+
title: "My Blog Post",
|
94
|
+
content: "This is the content...",
|
95
|
+
permalink: "https://myblog.com/my-post",
|
96
|
+
tag_list: ["ruby", "rails"]
|
97
|
+
)
|
98
|
+
|
99
|
+
# The record will be automatically synced to PlayPath.io
|
100
|
+
# You can also manually sync:
|
101
|
+
article.sync_to_playpath!
|
102
|
+
RUBY
|
103
|
+
|
104
|
+
puts "\nExample completed!"
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module PlaypathRails
|
8
|
+
# HTTP client for PlayPath.io API
|
9
|
+
class Client
|
10
|
+
attr_reader :configuration
|
11
|
+
|
12
|
+
def initialize(configuration)
|
13
|
+
@configuration = configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
# Items API methods
|
17
|
+
|
18
|
+
# List all items
|
19
|
+
def list_items
|
20
|
+
request(:get, '/api/items')
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get a specific item by ID
|
24
|
+
def get_item(id)
|
25
|
+
request(:get, "/api/items/#{id}")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create a new item
|
29
|
+
def create_item(title:, url: nil, text: nil, tags: [])
|
30
|
+
body = { title: title }
|
31
|
+
body[:url] = url if url
|
32
|
+
body[:text] = text if text
|
33
|
+
body[:tags] = tags if tags&.any?
|
34
|
+
|
35
|
+
request(:post, '/api/items', body: body)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Update an existing item
|
39
|
+
def update_item(id, title: nil, url: nil, text: nil, tags: nil)
|
40
|
+
body = {}
|
41
|
+
body[:title] = title if title
|
42
|
+
body[:url] = url if url
|
43
|
+
body[:text] = text if text
|
44
|
+
body[:tags] = tags if tags
|
45
|
+
|
46
|
+
request(:patch, "/api/items/#{id}", body: body)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Delete an item
|
50
|
+
def delete_item(id)
|
51
|
+
request(:delete, "/api/items/#{id}")
|
52
|
+
end
|
53
|
+
|
54
|
+
# RAG Chat API methods
|
55
|
+
|
56
|
+
# Send a message to the RAG assistant
|
57
|
+
def chat(message:, history: [])
|
58
|
+
body = { message: message }
|
59
|
+
body[:history] = history if history&.any?
|
60
|
+
|
61
|
+
request(:post, '/api/rag/chat', body: body, endpoint_type: :rag)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def request(method, path, body: nil, endpoint_type: :items)
|
67
|
+
uri = URI.join(configuration.base_url, path)
|
68
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
69
|
+
http.use_ssl = uri.scheme == 'https'
|
70
|
+
|
71
|
+
request = build_request(method, uri, body, endpoint_type)
|
72
|
+
response = http.request(request)
|
73
|
+
|
74
|
+
handle_response(response)
|
75
|
+
end
|
76
|
+
|
77
|
+
def build_request(method, uri, body, endpoint_type)
|
78
|
+
request_class = case method
|
79
|
+
when :get then Net::HTTP::Get
|
80
|
+
when :post then Net::HTTP::Post
|
81
|
+
when :patch then Net::HTTP::Patch
|
82
|
+
when :put then Net::HTTP::Put
|
83
|
+
when :delete then Net::HTTP::Delete
|
84
|
+
else raise ArgumentError, "Unsupported HTTP method: #{method}"
|
85
|
+
end
|
86
|
+
|
87
|
+
request = request_class.new(uri)
|
88
|
+
request['Content-Type'] = 'application/json'
|
89
|
+
|
90
|
+
# Set authentication header
|
91
|
+
api_key = configuration.api_key_for(endpoint_type)
|
92
|
+
raise AuthenticationError, 'API key not configured' unless api_key
|
93
|
+
|
94
|
+
request['X-Api-Key'] = api_key
|
95
|
+
|
96
|
+
if body
|
97
|
+
request.body = JSON.generate(body)
|
98
|
+
end
|
99
|
+
|
100
|
+
request
|
101
|
+
end
|
102
|
+
|
103
|
+
def handle_response(response)
|
104
|
+
case response.code.to_i
|
105
|
+
when 200, 201
|
106
|
+
return nil if response.body.nil? || response.body.empty?
|
107
|
+
JSON.parse(response.body)
|
108
|
+
when 204
|
109
|
+
nil
|
110
|
+
when 400
|
111
|
+
error_data = parse_error_response(response)
|
112
|
+
raise ValidationError.new(error_data[:message], status_code: 400, response_body: response.body)
|
113
|
+
when 401
|
114
|
+
raise AuthenticationError.new('Unauthorized', status_code: 401, response_body: response.body)
|
115
|
+
when 403
|
116
|
+
error_data = parse_error_response(response)
|
117
|
+
if error_data[:message]&.include?('Trial limit')
|
118
|
+
raise TrialLimitError.new(error_data[:message], status_code: 403, response_body: response.body)
|
119
|
+
else
|
120
|
+
raise APIError.new('Forbidden', status_code: 403, response_body: response.body)
|
121
|
+
end
|
122
|
+
when 404
|
123
|
+
raise NotFoundError.new('Resource not found', status_code: 404, response_body: response.body)
|
124
|
+
when 422
|
125
|
+
error_data = parse_error_response(response)
|
126
|
+
message = error_data[:errors]&.join(', ') || 'Validation failed'
|
127
|
+
raise ValidationError.new(message, status_code: 422, response_body: response.body)
|
128
|
+
when 429
|
129
|
+
raise RateLimitError.new('Rate limit exceeded', status_code: 429, response_body: response.body)
|
130
|
+
when 502
|
131
|
+
error_data = parse_error_response(response)
|
132
|
+
raise ExternalServiceError.new(error_data[:message] || 'Bad Gateway', status_code: 502, response_body: response.body)
|
133
|
+
else
|
134
|
+
raise APIError.new("HTTP #{response.code}: #{response.message}", status_code: response.code.to_i, response_body: response.body)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_error_response(response)
|
139
|
+
return { message: 'Unknown error' } if response.body.nil? || response.body.empty?
|
140
|
+
|
141
|
+
JSON.parse(response.body, symbolize_names: true)
|
142
|
+
rescue JSON::ParserError
|
143
|
+
{ message: response.body }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlaypathRails
|
4
|
+
# Base error class for PlaypathRails
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
# Authentication error
|
8
|
+
class AuthenticationError < Error; end
|
9
|
+
|
10
|
+
# API request error
|
11
|
+
class APIError < Error
|
12
|
+
attr_reader :status_code, :response_body
|
13
|
+
|
14
|
+
def initialize(message, status_code: nil, response_body: nil)
|
15
|
+
super(message)
|
16
|
+
@status_code = status_code
|
17
|
+
@response_body = response_body
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Validation error from API
|
22
|
+
class ValidationError < APIError; end
|
23
|
+
|
24
|
+
# Trial limit exceeded error
|
25
|
+
class TrialLimitError < APIError; end
|
26
|
+
|
27
|
+
# Resource not found error
|
28
|
+
class NotFoundError < APIError; end
|
29
|
+
|
30
|
+
# Rate limit exceeded error
|
31
|
+
class RateLimitError < APIError; end
|
32
|
+
|
33
|
+
# External service error (e.g., OpenAI API)
|
34
|
+
class ExternalServiceError < APIError; end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
require 'rails/generators/active_record'
|
5
|
+
|
6
|
+
module PlaypathRails
|
7
|
+
module Generators
|
8
|
+
class MigrationGenerator < Rails::Generators::Base
|
9
|
+
include ActiveRecord::Generators::Migration
|
10
|
+
|
11
|
+
source_root File.expand_path('templates', __dir__)
|
12
|
+
|
13
|
+
argument :model_name, type: :string, desc: "The model to add PlayPath synchronization to"
|
14
|
+
|
15
|
+
def create_migration_file
|
16
|
+
migration_template 'add_playpath_item_id_migration.rb.erb',
|
17
|
+
"db/migrate/add_playpath_item_id_to_#{table_name}.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def table_name
|
23
|
+
model_name.tableize
|
24
|
+
end
|
25
|
+
|
26
|
+
def migration_class_name
|
27
|
+
"AddPlaypathItemIdTo#{model_name.camelize.pluralize}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PlaypathRails
|
4
|
+
# Helper module for RAG (Retrieval-Augmented Generation) functionality
|
5
|
+
module RAG
|
6
|
+
class << self
|
7
|
+
# Send a message to the RAG assistant
|
8
|
+
# @param message [String] The user's question or message
|
9
|
+
# @param history [Array] Optional array of previous conversation messages
|
10
|
+
# @return [Hash] Response containing reply, usage, and limit information
|
11
|
+
def chat(message:, history: [])
|
12
|
+
PlaypathRails.client.chat(message: message, history: history)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Send a simple message without conversation history
|
16
|
+
# @param message [String] The user's question or message
|
17
|
+
# @return [String] The AI-generated response
|
18
|
+
def ask(message)
|
19
|
+
response = chat(message: message)
|
20
|
+
response['reply']
|
21
|
+
end
|
22
|
+
|
23
|
+
# Build a conversation history array from alternating user/assistant messages
|
24
|
+
# @param messages [Array] Array of message strings, alternating user/assistant
|
25
|
+
# @return [Array] Properly formatted history array
|
26
|
+
def build_history(*messages)
|
27
|
+
history = []
|
28
|
+
messages.each_with_index do |message, index|
|
29
|
+
role = index.even? ? 'user' : 'assistant'
|
30
|
+
history << { 'role' => role, 'text' => message }
|
31
|
+
end
|
32
|
+
history
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
|
5
|
+
module PlaypathRails
|
6
|
+
# Module to add synchronization callbacks and methods to ActiveRecord models
|
7
|
+
module Synchronizable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
# Only add callbacks if the host class supports them
|
12
|
+
if respond_to?(:after_commit)
|
13
|
+
after_commit :playpath_sync!, on: [:create, :update]
|
14
|
+
end
|
15
|
+
if respond_to?(:before_destroy)
|
16
|
+
before_destroy :playpath_delete!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class_methods do
|
21
|
+
# Declare that this model should be synced to PlayPath.io
|
22
|
+
# options:
|
23
|
+
# only: Array of symbols specifying which fields to sync
|
24
|
+
# title_field: Symbol specifying which field to use as the title (required)
|
25
|
+
# url_field: Symbol specifying which field to use as the URL (optional)
|
26
|
+
# text_field: Symbol specifying which field to use as the text content (optional)
|
27
|
+
# tags_field: Symbol specifying which field contains tags (optional)
|
28
|
+
# tags: Array of static tags to apply to all items (optional)
|
29
|
+
def playpath_sync(only: nil, title_field: :title, url_field: nil, text_field: nil, tags_field: nil, tags: [])
|
30
|
+
@playpath_sync_options = {
|
31
|
+
only: only,
|
32
|
+
title_field: title_field,
|
33
|
+
url_field: url_field,
|
34
|
+
text_field: text_field,
|
35
|
+
tags_field: tags_field,
|
36
|
+
tags: tags
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Retrieve synchronization options for this model
|
41
|
+
def playpath_sync_options
|
42
|
+
@playpath_sync_options || { title_field: :title }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Push current record state to PlayPath.io
|
47
|
+
def playpath_sync!
|
48
|
+
return true unless should_sync?
|
49
|
+
|
50
|
+
begin
|
51
|
+
item_data = build_item_data
|
52
|
+
|
53
|
+
if playpath_item_id && playpath_item_id != 0
|
54
|
+
# Update existing item
|
55
|
+
PlaypathRails.client.update_item(playpath_item_id, **item_data)
|
56
|
+
else
|
57
|
+
# Create new item
|
58
|
+
response = PlaypathRails.client.create_item(**item_data)
|
59
|
+
# Store the item ID if the model supports it
|
60
|
+
if respond_to?(:playpath_item_id=) && response&.dig('id')
|
61
|
+
update_column(:playpath_item_id, response['id'])
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
true
|
66
|
+
rescue PlaypathRails::Error => e
|
67
|
+
# Log the error but don't raise it to avoid breaking the application
|
68
|
+
Rails.logger.error("PlayPath sync failed for #{self.class.name}##{id}: #{e.message}") if defined?(Rails)
|
69
|
+
false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Remove record from PlayPath.io
|
74
|
+
def playpath_delete!
|
75
|
+
return true unless playpath_item_id && playpath_item_id != 0
|
76
|
+
|
77
|
+
begin
|
78
|
+
PlaypathRails.client.delete_item(playpath_item_id)
|
79
|
+
true
|
80
|
+
rescue PlaypathRails::NotFoundError
|
81
|
+
# Item already deleted, consider this success
|
82
|
+
true
|
83
|
+
rescue PlaypathRails::Error => e
|
84
|
+
# Log the error but don't raise it to avoid breaking the application
|
85
|
+
Rails.logger.error("PlayPath delete failed for #{self.class.name}##{id}: #{e.message}") if defined?(Rails)
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Manually sync this record to PlayPath.io (bypasses callbacks)
|
91
|
+
def sync_to_playpath!
|
92
|
+
playpath_sync!
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get the PlayPath item ID for this record
|
96
|
+
def playpath_item_id
|
97
|
+
return nil unless respond_to?(:playpath_item_id)
|
98
|
+
read_attribute(:playpath_item_id)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def should_sync?
|
104
|
+
# Check if PlayPath is configured
|
105
|
+
return false unless PlaypathRails.configuration&.api_key
|
106
|
+
|
107
|
+
# Check if the record has the required title field
|
108
|
+
options = self.class.playpath_sync_options
|
109
|
+
title_field = options[:title_field]
|
110
|
+
title_value = respond_to?(title_field) ? send(title_field) : nil
|
111
|
+
return false unless title_value && !title_value.to_s.empty?
|
112
|
+
|
113
|
+
# If 'only' fields are specified, check if any of them changed
|
114
|
+
if options[:only] && options[:only].any?
|
115
|
+
return options[:only].any? { |field| saved_change_to_attribute?(field) }
|
116
|
+
end
|
117
|
+
|
118
|
+
true
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_item_data
|
122
|
+
options = self.class.playpath_sync_options
|
123
|
+
data = {}
|
124
|
+
|
125
|
+
# Title is required
|
126
|
+
title_field = options[:title_field]
|
127
|
+
data[:title] = send(title_field).to_s if respond_to?(title_field)
|
128
|
+
|
129
|
+
# URL is optional
|
130
|
+
if options[:url_field] && respond_to?(options[:url_field])
|
131
|
+
url_value = send(options[:url_field])
|
132
|
+
data[:url] = url_value.to_s if url_value && !url_value.to_s.empty?
|
133
|
+
end
|
134
|
+
|
135
|
+
# Text content is optional
|
136
|
+
if options[:text_field] && respond_to?(options[:text_field])
|
137
|
+
text_value = send(options[:text_field])
|
138
|
+
data[:text] = text_value.to_s if text_value && !text_value.to_s.empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
# Tags handling
|
142
|
+
tags = []
|
143
|
+
|
144
|
+
# Add static tags
|
145
|
+
tags.concat(options[:tags]) if options[:tags] && options[:tags].any?
|
146
|
+
|
147
|
+
# Add dynamic tags from field
|
148
|
+
if options[:tags_field] && respond_to?(options[:tags_field])
|
149
|
+
field_tags = send(options[:tags_field])
|
150
|
+
case field_tags
|
151
|
+
when Array
|
152
|
+
tags.concat(field_tags.map(&:to_s))
|
153
|
+
when String
|
154
|
+
# Assume comma-separated tags
|
155
|
+
tags.concat(field_tags.split(',').map(&:strip))
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
data[:tags] = tags.uniq if tags.any?
|
160
|
+
|
161
|
+
data
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "playpath_rails/version"
|
4
|
+
require_relative "playpath_rails/errors"
|
5
|
+
require_relative "playpath_rails/client"
|
6
|
+
require_relative "playpath_rails/synchronizable"
|
7
|
+
require_relative "playpath_rails/rag"
|
8
|
+
|
9
|
+
module PlaypathRails
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Accessor for global configuration
|
14
|
+
attr_accessor :configuration
|
15
|
+
|
16
|
+
# Configure PlaypathRails with API credentials and settings
|
17
|
+
# Usage:
|
18
|
+
# PlaypathRails.configure do |config|
|
19
|
+
# config.api_key = 'KEY'
|
20
|
+
# config.embeddings_api_key = 'EMBEDDINGS_KEY'
|
21
|
+
# config.base_url = 'https://custom-url'
|
22
|
+
# end
|
23
|
+
def configure
|
24
|
+
self.configuration ||= Configuration.new
|
25
|
+
yield(configuration) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get a configured client instance
|
29
|
+
def client
|
30
|
+
@client ||= Client.new(configuration)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Configuration object for PlaypathRails
|
35
|
+
class Configuration
|
36
|
+
# Regular API key for PlayPath.io (full access)
|
37
|
+
attr_accessor :api_key
|
38
|
+
# Embeddings API key for PlayPath.io (limited to RAG endpoints)
|
39
|
+
attr_accessor :embeddings_api_key
|
40
|
+
# Base URL for API requests (defaults to Playpath.io service)
|
41
|
+
attr_accessor :base_url
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@api_key = nil
|
45
|
+
@embeddings_api_key = nil
|
46
|
+
@base_url = "https://playpath.io"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get the appropriate API key for the request type
|
50
|
+
def api_key_for(endpoint_type = :items)
|
51
|
+
case endpoint_type
|
52
|
+
when :rag
|
53
|
+
@embeddings_api_key || @api_key
|
54
|
+
else
|
55
|
+
@api_key
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
metadata
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: playpath_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joran Kikke
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-05-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '8.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '5.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '8.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: activesupport
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '5.2'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '8.0'
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '5.2'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '8.0'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '3.0'
|
60
|
+
type: :development
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '3.0'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: rspec-rails
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '6.0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '6.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rubocop
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '1.0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '1.0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rubocop-rails
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '2.0'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: rubocop-rspec
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '2.0'
|
116
|
+
type: :development
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '2.0'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: webmock
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '3.0'
|
130
|
+
type: :development
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '3.0'
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: simplecov
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - "~>"
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0.22'
|
144
|
+
type: :development
|
145
|
+
prerelease: false
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
147
|
+
requirements:
|
148
|
+
- - "~>"
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0.22'
|
151
|
+
description: |-
|
152
|
+
Provides integration between ActiveRecord and the Items API at playpath.io,
|
153
|
+
enabling automatic synchronization of records between your Rails application
|
154
|
+
and Playpath's Items service.
|
155
|
+
email:
|
156
|
+
- joran.k@gmail.com
|
157
|
+
executables: []
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- ".rspec"
|
162
|
+
- LICENSE.txt
|
163
|
+
- README.md
|
164
|
+
- Rakefile
|
165
|
+
- examples/usage_example.rb
|
166
|
+
- lib/playpath_rails.rb
|
167
|
+
- lib/playpath_rails/client.rb
|
168
|
+
- lib/playpath_rails/errors.rb
|
169
|
+
- lib/playpath_rails/generators/migration_generator.rb
|
170
|
+
- lib/playpath_rails/generators/templates/add_playpath_item_id_migration.rb.erb
|
171
|
+
- lib/playpath_rails/rag.rb
|
172
|
+
- lib/playpath_rails/synchronizable.rb
|
173
|
+
- lib/playpath_rails/version.rb
|
174
|
+
- sig/playpath_rails.rbs
|
175
|
+
homepage: https://playpath.io
|
176
|
+
licenses:
|
177
|
+
- MIT
|
178
|
+
metadata:
|
179
|
+
allowed_push_host: https://rubygems.org
|
180
|
+
homepage_uri: https://playpath.io
|
181
|
+
source_code_uri: https://github.com/playpath/playpath_rails
|
182
|
+
changelog_uri: https://github.com/playpath/playpath_rails/blob/main/CHANGELOG.md
|
183
|
+
bug_tracker_uri: https://github.com/playpath/playpath_rails/issues
|
184
|
+
documentation_uri: https://rubydoc.info/gems/playpath_rails
|
185
|
+
post_install_message:
|
186
|
+
rdoc_options: []
|
187
|
+
require_paths:
|
188
|
+
- lib
|
189
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: 3.1.0
|
194
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - ">="
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
199
|
+
requirements: []
|
200
|
+
rubygems_version: 3.5.22
|
201
|
+
signing_key:
|
202
|
+
specification_version: 4
|
203
|
+
summary: Sync ActiveRecord models with the Items API at playpath.io
|
204
|
+
test_files: []
|