carddb-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/CHANGELOG.md +24 -0
- data/LICENSE.txt +21 -0
- data/README.md +494 -0
- data/lib/carddb/rails/configuration.rb +16 -0
- data/lib/carddb/rails/controller_helpers.rb +11 -0
- data/lib/carddb/rails/deck_controller_helpers.rb +27 -0
- data/lib/carddb/rails/deck_export_service.rb +21 -0
- data/lib/carddb/rails/deck_import_service.rb +20 -0
- data/lib/carddb/rails/deck_sync_job.rb +15 -0
- data/lib/carddb/rails/deck_sync_service.rb +21 -0
- data/lib/carddb/rails/graphql/dataloader/dataset_source.rb +37 -0
- data/lib/carddb/rails/graphql/dataloader/deck_by_external_ref_source.rb +26 -0
- data/lib/carddb/rails/graphql/dataloader/deck_source.rb +26 -0
- data/lib/carddb/rails/graphql/dataloader/game_source.rb +30 -0
- data/lib/carddb/rails/graphql/dataloader/publisher_source.rb +26 -0
- data/lib/carddb/rails/graphql/dataloader/record_source.rb +39 -0
- data/lib/carddb/rails/graphql/dataset_loader.rb +41 -0
- data/lib/carddb/rails/graphql/deck_loader.rb +51 -0
- data/lib/carddb/rails/graphql/game_loader.rb +34 -0
- data/lib/carddb/rails/graphql/helpers.rb +220 -0
- data/lib/carddb/rails/graphql/publisher_loader.rb +30 -0
- data/lib/carddb/rails/graphql/record_loader.rb +43 -0
- data/lib/carddb/rails/has_carddb_dataset.rb +89 -0
- data/lib/carddb/rails/has_carddb_datasets.rb +105 -0
- data/lib/carddb/rails/has_carddb_deck.rb +134 -0
- data/lib/carddb/rails/has_carddb_game.rb +84 -0
- data/lib/carddb/rails/has_carddb_publisher.rb +75 -0
- data/lib/carddb/rails/has_carddb_record.rb +166 -0
- data/lib/carddb/rails/railtie.rb +34 -0
- data/lib/carddb/rails/resolver.rb +54 -0
- data/lib/carddb/rails/test_helper.rb +12 -0
- data/lib/carddb/rails/version.rb +7 -0
- data/lib/carddb/rails.rb +93 -0
- data/lib/carddb-rails.rb +3 -0
- data/lib/generators/carddb/install/install_generator.rb +15 -0
- data/lib/generators/carddb/install/templates/carddb.rb +23 -0
- metadata +142 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d0b3bef990ba77cc06d5bcefe584ce475e8a86a9c760850f85b927477cf2b793
|
|
4
|
+
data.tar.gz: bf78dc4b0c53c3e800ed79a229fdda92ae5e4fa26225674a13919fed7333f3da
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 42c1c12ae51af8f11c2d0ed65c0f7972b50bd6ae5980aa17dd4aaae28b36f61e250d6ebd3b5156b6785085cc9364b4c0e322f11996b16247c7fb24cf67ad9a06
|
|
7
|
+
data.tar.gz: 377b72303e57d2621bacfa851977be257e6104a3acb2134109f12de04fb33ba58f184f7a05ec024336534e0e59a9a0cbee8e504836268771d3b6ffac73919082
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Rails integration for the `carddb` gem via `Railtie`, install generator, and controller helpers.
|
|
13
|
+
- Active Record model helpers for CardDB records, decks, games, datasets, publishers, and game-scoped dataset collections.
|
|
14
|
+
- `GraphQL::Batch` loaders and `GraphQL::Dataloader` sources for CardDB records, decks, games, datasets, and publishers.
|
|
15
|
+
- GraphQL field macros for CardDB-backed resources and dataset list relationships.
|
|
16
|
+
- Explicit deck sync, import, and export service wrappers plus `ActiveJob` support for deck sync jobs.
|
|
17
|
+
- Token-aware deck controller helpers for preview, embed, access, and access-token exchange flows.
|
|
18
|
+
- CI, publish helper script, and gem release metadata aligned with the `carddb` Ruby client.
|
|
19
|
+
|
|
20
|
+
## [0.1.0] - 2026-06-17
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Initial public release of `carddb-rails`.
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CardDB Team
|
|
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,494 @@
|
|
|
1
|
+
# CardDB Rails
|
|
2
|
+
|
|
3
|
+
Rails integration for the [`carddb`](https://github.com/xtda/carddb-ruby) gem.
|
|
4
|
+
|
|
5
|
+
This gem keeps the base CardDB client framework-agnostic and adds Rails-specific conveniences:
|
|
6
|
+
|
|
7
|
+
- initializer and Railtie wiring
|
|
8
|
+
- `carddb_client` controller helper
|
|
9
|
+
- explicit Active Record model helpers
|
|
10
|
+
- `GraphQL::Batch` loaders for CardDB records, games, datasets, and publishers
|
|
11
|
+
- `GraphQL::Dataloader` sources for the same resource lookups
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add this line to your application's Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'carddb'
|
|
19
|
+
gem 'carddb-rails'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Then run:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bundle install
|
|
26
|
+
bin/rails generate carddb:install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Configuration
|
|
30
|
+
|
|
31
|
+
The generated initializer configures CardDB from `Rails.application.credentials[:carddb]` and wires `Rails.cache` and `Rails.logger` by default.
|
|
32
|
+
|
|
33
|
+
Expected credentials shape:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
carddb:
|
|
37
|
+
publishable_key: carddb_pk_xxx
|
|
38
|
+
secret_key: carddb_sk_xxx
|
|
39
|
+
access_token: carddb_oat_xxx
|
|
40
|
+
api_key: carddb_legacy_xxx
|
|
41
|
+
endpoint: https://carddb.xtda.org/query
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Controller Helper
|
|
45
|
+
|
|
46
|
+
Controllers get a `carddb_client` helper:
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
class CardsController < ApplicationController
|
|
50
|
+
def show
|
|
51
|
+
@card = carddb_client.records.get(
|
|
52
|
+
publisher_slug: 'pokemon-company',
|
|
53
|
+
game_key: 'pokemon-tcg',
|
|
54
|
+
dataset_key: 'cards',
|
|
55
|
+
identifier: params[:id]
|
|
56
|
+
)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Model Helper
|
|
62
|
+
|
|
63
|
+
Include `CardDB::Rails::HasCardDBRecord` in models that hold a CardDB identifier locally.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
class DeckEntry < ApplicationRecord
|
|
67
|
+
include CardDB::Rails::HasCardDBRecord
|
|
68
|
+
|
|
69
|
+
has_carddb_record \
|
|
70
|
+
dataset_key: 'cards',
|
|
71
|
+
publisher_slug: 'pokemon-company',
|
|
72
|
+
game_key: 'pokemon-tcg',
|
|
73
|
+
identifier: :card_identifier
|
|
74
|
+
end
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This defines:
|
|
78
|
+
|
|
79
|
+
- `carddb_record`
|
|
80
|
+
- `carddb_record!`
|
|
81
|
+
- `reload_carddb_record`
|
|
82
|
+
- `carddb_record_loaded?`
|
|
83
|
+
|
|
84
|
+
Additional helpers are available for other CardDB resources:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
class DeckTemplate < ApplicationRecord
|
|
88
|
+
include CardDB::Rails::HasCardDBGame
|
|
89
|
+
include CardDB::Rails::HasCardDBDataset
|
|
90
|
+
|
|
91
|
+
has_carddb_game game_key: :game_key, publisher_slug: 'pokemon-company'
|
|
92
|
+
has_carddb_dataset dataset_key: :dataset_key, publisher_slug: 'pokemon-company', game_key: :game_key
|
|
93
|
+
end
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
For deck-backed models, use `HasCardDBDeck`:
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
class TournamentDeck < ApplicationRecord
|
|
100
|
+
include CardDB::Rails::HasCardDBDeck
|
|
101
|
+
|
|
102
|
+
has_carddb_deck id: :carddb_deck_id
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Or map a local record to an app-owned CardDB deck by external reference:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
class TournamentDeck < ApplicationRecord
|
|
110
|
+
include CardDB::Rails::HasCardDBDeck
|
|
111
|
+
|
|
112
|
+
has_carddb_deck external_ref: :external_ref
|
|
113
|
+
end
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
That gives you:
|
|
117
|
+
|
|
118
|
+
- `carddb_deck`
|
|
119
|
+
- `carddb_deck!`
|
|
120
|
+
- `reload_carddb_deck`
|
|
121
|
+
- `carddb_deck_loaded?`
|
|
122
|
+
|
|
123
|
+
To batch-preload deck helpers for many records:
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
TournamentDeck.preload_carddb_decks(records)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If a model should expose the available datasets for its game, use `HasCardDBDatasets`:
|
|
130
|
+
|
|
131
|
+
```ruby
|
|
132
|
+
class DeckTemplate < ApplicationRecord
|
|
133
|
+
include CardDB::Rails::HasCardDBGame
|
|
134
|
+
include CardDB::Rails::HasCardDBDatasets
|
|
135
|
+
|
|
136
|
+
has_carddb_game game_key: :game_key, publisher_slug: 'pokemon-company'
|
|
137
|
+
has_carddb_datasets publisher_slug: 'pokemon-company', game_key: :game_key
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
That gives you:
|
|
142
|
+
|
|
143
|
+
- `carddb_datasets`
|
|
144
|
+
- `reload_carddb_datasets`
|
|
145
|
+
- `carddb_datasets_loaded?`
|
|
146
|
+
- `carddb_dataset('cards')`
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
template.carddb_datasets
|
|
152
|
+
template.carddb_dataset('cards')
|
|
153
|
+
template.carddb_datasets(purpose: 'RULES', search: 'rotation')
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
You can also use dynamic values:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
class CollectionItem < ApplicationRecord
|
|
160
|
+
include CardDB::Rails::HasCardDBRecord
|
|
161
|
+
|
|
162
|
+
has_carddb_record \
|
|
163
|
+
dataset_key: :carddb_dataset_key,
|
|
164
|
+
publisher_slug: :carddb_publisher_slug,
|
|
165
|
+
game_key: :carddb_game_key,
|
|
166
|
+
identifier: :carddb_identifier
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Or define multiple named mappings:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
class MatchResult < ApplicationRecord
|
|
174
|
+
include CardDB::Rails::HasCardDBRecord
|
|
175
|
+
|
|
176
|
+
has_carddb_record :home_card,
|
|
177
|
+
dataset_key: 'cards',
|
|
178
|
+
publisher_slug: 'pokemon-company',
|
|
179
|
+
game_key: 'pokemon-tcg',
|
|
180
|
+
identifier: :home_card_identifier
|
|
181
|
+
|
|
182
|
+
has_carddb_record :away_card,
|
|
183
|
+
dataset_key: 'cards',
|
|
184
|
+
publisher_slug: 'pokemon-company',
|
|
185
|
+
game_key: 'pokemon-tcg',
|
|
186
|
+
identifier: :away_card_identifier
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
To batch-preload a CardDB helper for many records:
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
DeckEntry.preload_carddb_records(entries)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This uses CardDB's batch API and memoizes the loaded records back onto each model instance.
|
|
197
|
+
|
|
198
|
+
## GraphQL::Batch Loader
|
|
199
|
+
|
|
200
|
+
This gem includes a `GraphQL::Batch` loader that fetches CardDB records in one remote batch request:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
class Types::DeckEntry < Types::BaseObject
|
|
204
|
+
field :carddb_record, Types::CarddbRecord, null: true
|
|
205
|
+
|
|
206
|
+
def carddb_record
|
|
207
|
+
CardDB::Rails::GraphQL::RecordLoader.for(
|
|
208
|
+
dataset_key: 'cards',
|
|
209
|
+
publisher_slug: 'pokemon-company',
|
|
210
|
+
game_key: 'pokemon-tcg'
|
|
211
|
+
).load(object.card_identifier)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
If your Rails app already uses `GraphQL::Batch`, this prevents one CardDB HTTP request per node.
|
|
217
|
+
|
|
218
|
+
Available loaders:
|
|
219
|
+
|
|
220
|
+
- `CardDB::Rails::GraphQL::DeckLoader`
|
|
221
|
+
- `CardDB::Rails::GraphQL::DeckByExternalRefLoader`
|
|
222
|
+
- `CardDB::Rails::GraphQL::RecordLoader`
|
|
223
|
+
- `CardDB::Rails::GraphQL::GameLoader`
|
|
224
|
+
- `CardDB::Rails::GraphQL::DatasetLoader`
|
|
225
|
+
- `CardDB::Rails::GraphQL::PublisherLoader`
|
|
226
|
+
|
|
227
|
+
If your schema uses `GraphQL::Dataloader` instead of `GraphQL::Batch`, parallel sources are also available:
|
|
228
|
+
|
|
229
|
+
- `CardDB::Rails::GraphQL::Dataloader::RecordSource`
|
|
230
|
+
- `CardDB::Rails::GraphQL::Dataloader::DeckSource`
|
|
231
|
+
- `CardDB::Rails::GraphQL::Dataloader::DeckByExternalRefSource`
|
|
232
|
+
- `CardDB::Rails::GraphQL::Dataloader::GameSource`
|
|
233
|
+
- `CardDB::Rails::GraphQL::Dataloader::DatasetSource`
|
|
234
|
+
- `CardDB::Rails::GraphQL::Dataloader::PublisherSource`
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
class Types::DeckEntry < Types::BaseObject
|
|
240
|
+
field :carddb_record, Types::CarddbRecord, null: true
|
|
241
|
+
|
|
242
|
+
def carddb_record
|
|
243
|
+
dataloader.with(
|
|
244
|
+
CardDB::Rails::GraphQL::Dataloader::RecordSource,
|
|
245
|
+
dataset_key: 'cards',
|
|
246
|
+
publisher_slug: 'pokemon-company',
|
|
247
|
+
game_key: object.game_key
|
|
248
|
+
).load(object.card_identifier)
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
You can also use dataloader-backed field macros directly:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
class Types::DeckEntry < Types::BaseObject
|
|
257
|
+
carddb_record_dataloader_field :carddb_record,
|
|
258
|
+
type: Types::CarddbRecord,
|
|
259
|
+
identifier: :card_identifier,
|
|
260
|
+
dataset_key: 'cards',
|
|
261
|
+
publisher_slug: 'pokemon-company',
|
|
262
|
+
game_key: :game_key
|
|
263
|
+
end
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Deck fields are also available in both styles:
|
|
267
|
+
|
|
268
|
+
```ruby
|
|
269
|
+
class Types::TournamentDeck < Types::BaseObject
|
|
270
|
+
carddb_deck_field :carddb_deck,
|
|
271
|
+
type: Types::CarddbDeck,
|
|
272
|
+
id: :carddb_deck_id
|
|
273
|
+
|
|
274
|
+
carddb_deck_dataloader_field :carddb_deck_async,
|
|
275
|
+
type: Types::CarddbDeck,
|
|
276
|
+
id: :carddb_deck_id
|
|
277
|
+
end
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
External-ref lookups use the same macros:
|
|
281
|
+
|
|
282
|
+
```ruby
|
|
283
|
+
class Types::TournamentDeck < Types::BaseObject
|
|
284
|
+
carddb_deck_field :carddb_deck,
|
|
285
|
+
type: Types::CarddbDeck,
|
|
286
|
+
external_ref: :external_ref
|
|
287
|
+
|
|
288
|
+
carddb_deck_dataloader_field :carddb_deck_async,
|
|
289
|
+
type: Types::CarddbDeck,
|
|
290
|
+
external_ref: :external_ref
|
|
291
|
+
end
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Deck Sync Helpers
|
|
295
|
+
|
|
296
|
+
For write flows, keep sync explicit with the provided service and job:
|
|
297
|
+
|
|
298
|
+
```ruby
|
|
299
|
+
CardDB::Rails::DeckSyncService.call(
|
|
300
|
+
external_ref: 'deck-ext-1',
|
|
301
|
+
input: {
|
|
302
|
+
title: 'Pikachu League Cup',
|
|
303
|
+
visibility: 'PRIVATE'
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
```ruby
|
|
309
|
+
CardDB::Rails::DeckSyncJob.perform_later(
|
|
310
|
+
external_ref: 'deck-ext-1',
|
|
311
|
+
input: {
|
|
312
|
+
title: 'Pikachu League Cup',
|
|
313
|
+
visibility: 'PRIVATE'
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
These helpers call `client.decks.upsert_by_external_ref(...)` and do not hide writes behind model callbacks.
|
|
319
|
+
|
|
320
|
+
## Deck Import / Export Helpers
|
|
321
|
+
|
|
322
|
+
For explicit service-style import and export flows:
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
CardDB::Rails::DeckImportService.call(
|
|
326
|
+
input: {
|
|
327
|
+
deckId: 'deck_123',
|
|
328
|
+
text: "4 Pikachu\n4 Rare Candy",
|
|
329
|
+
format: 'SIMPLE_TEXT'
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
```ruby
|
|
335
|
+
CardDB::Rails::DeckExportService.call(
|
|
336
|
+
deck_id: 'deck_123',
|
|
337
|
+
format: 'JSON'
|
|
338
|
+
)
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Deck Token Controller Helpers
|
|
342
|
+
|
|
343
|
+
Controllers automatically get token-aware helpers for deck preview/embed/access flows:
|
|
344
|
+
|
|
345
|
+
```ruby
|
|
346
|
+
class DeckPreviewsController < ApplicationController
|
|
347
|
+
def show
|
|
348
|
+
@deck = carddb_preview_deck(params[:token])
|
|
349
|
+
end
|
|
350
|
+
end
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Available helpers:
|
|
354
|
+
|
|
355
|
+
- `carddb_preview_deck(token)`
|
|
356
|
+
- `carddb_embed_deck(token)`
|
|
357
|
+
- `carddb_access_deck(token)`
|
|
358
|
+
- `carddb_exchange_deck_access_token(input)`
|
|
359
|
+
|
|
360
|
+
## GraphQL Field Helper
|
|
361
|
+
|
|
362
|
+
If you want a thin schema macro on top of the loader, extend `CardDB::Rails::GraphQL::Helpers` in your base object:
|
|
363
|
+
|
|
364
|
+
```ruby
|
|
365
|
+
class Types::BaseObject < GraphQL::Schema::Object
|
|
366
|
+
extend CardDB::Rails::GraphQL::Helpers
|
|
367
|
+
end
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Then define CardDB-backed fields declaratively:
|
|
371
|
+
|
|
372
|
+
```ruby
|
|
373
|
+
class Types::DeckEntry < Types::BaseObject
|
|
374
|
+
carddb_record_field :carddb_record,
|
|
375
|
+
type: Types::CarddbRecord,
|
|
376
|
+
identifier: :card_identifier,
|
|
377
|
+
dataset_key: 'cards',
|
|
378
|
+
publisher_slug: 'pokemon-company',
|
|
379
|
+
game_key: 'pokemon-tcg'
|
|
380
|
+
end
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
For a list relationship, use `carddb_datasets_field`:
|
|
384
|
+
|
|
385
|
+
```ruby
|
|
386
|
+
class Types::DeckTemplate < Types::BaseObject
|
|
387
|
+
carddb_datasets_field :carddb_datasets,
|
|
388
|
+
type: [Types::CarddbDataset],
|
|
389
|
+
publisher_slug: 'pokemon-company',
|
|
390
|
+
game_key: :game_key
|
|
391
|
+
end
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
If the underlying model includes `HasCardDBDatasets`, this field uses the model helper directly.
|
|
395
|
+
|
|
396
|
+
If you prefer dataloader-oriented naming consistency, `carddb_datasets_dataloader_field` is also available.
|
|
397
|
+
|
|
398
|
+
## Integration Coverage
|
|
399
|
+
|
|
400
|
+
The gem includes coverage for:
|
|
401
|
+
|
|
402
|
+
- Railtie configuration wiring via `CardDB::Rails.apply_rails_configuration!`
|
|
403
|
+
- install generator output for `config/initializers/carddb.rb`
|
|
404
|
+
|
|
405
|
+
You can also expose related CardDB resources directly:
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
class Types::DeckTemplate < Types::BaseObject
|
|
409
|
+
carddb_game_field :carddb_game,
|
|
410
|
+
type: Types::CarddbGame,
|
|
411
|
+
game_key: :game_key,
|
|
412
|
+
publisher_slug: 'pokemon-company'
|
|
413
|
+
|
|
414
|
+
carddb_dataset_field :carddb_dataset,
|
|
415
|
+
type: Types::CarddbDataset,
|
|
416
|
+
dataset_key: :dataset_key,
|
|
417
|
+
publisher_slug: 'pokemon-company',
|
|
418
|
+
game_key: :game_key
|
|
419
|
+
|
|
420
|
+
carddb_publisher_field :carddb_publisher,
|
|
421
|
+
type: Types::CarddbPublisher,
|
|
422
|
+
slug: :publisher_slug
|
|
423
|
+
end
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Rails + GraphQL Example
|
|
427
|
+
|
|
428
|
+
For a GraphQL app that already uses `GraphQL::Batch`, a good pattern is:
|
|
429
|
+
|
|
430
|
+
```ruby
|
|
431
|
+
class DeckEntry < ApplicationRecord
|
|
432
|
+
include CardDB::Rails::HasCardDBRecord
|
|
433
|
+
include CardDB::Rails::HasCardDBGame
|
|
434
|
+
|
|
435
|
+
has_carddb_record \
|
|
436
|
+
dataset_key: 'cards',
|
|
437
|
+
publisher_slug: 'pokemon-company',
|
|
438
|
+
game_key: :game_key,
|
|
439
|
+
identifier: :card_identifier
|
|
440
|
+
|
|
441
|
+
has_carddb_game \
|
|
442
|
+
game_key: :game_key,
|
|
443
|
+
publisher_slug: 'pokemon-company'
|
|
444
|
+
end
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
```ruby
|
|
448
|
+
class Types::BaseObject < GraphQL::Schema::Object
|
|
449
|
+
extend CardDB::Rails::GraphQL::Helpers
|
|
450
|
+
end
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
class Types::DeckEntry < Types::BaseObject
|
|
455
|
+
field :id, ID, null: false
|
|
456
|
+
|
|
457
|
+
carddb_record_field :carddb_record,
|
|
458
|
+
type: Types::CarddbRecord,
|
|
459
|
+
identifier: :card_identifier,
|
|
460
|
+
dataset_key: 'cards',
|
|
461
|
+
publisher_slug: 'pokemon-company',
|
|
462
|
+
game_key: :game_key
|
|
463
|
+
|
|
464
|
+
carddb_game_field :carddb_game,
|
|
465
|
+
type: Types::CarddbGame,
|
|
466
|
+
game_key: :game_key,
|
|
467
|
+
publisher_slug: 'pokemon-company'
|
|
468
|
+
end
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
That keeps the Active Record model explicit while letting GraphQL batch remote CardDB lookups across sibling nodes.
|
|
472
|
+
|
|
473
|
+
If you want to expose all datasets for a game in GraphQL, a good pattern is to call the model helper directly:
|
|
474
|
+
|
|
475
|
+
```ruby
|
|
476
|
+
class Types::DeckTemplate < Types::BaseObject
|
|
477
|
+
field :carddb_datasets, [Types::CarddbDataset], null: false
|
|
478
|
+
|
|
479
|
+
def carddb_datasets
|
|
480
|
+
object.carddb_datasets
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Boundaries
|
|
486
|
+
|
|
487
|
+
This gem intentionally does not:
|
|
488
|
+
|
|
489
|
+
- make CardDB objects behave like `ActiveRecord::Relation`
|
|
490
|
+
- define remote CardDB fields directly on your models
|
|
491
|
+
- perform implicit remote lookups in callbacks or validations
|
|
492
|
+
- sync CardDB data into local tables
|
|
493
|
+
|
|
494
|
+
The intent is explicit remote access with Rails-friendly ergonomics.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CardDB
|
|
4
|
+
module Rails
|
|
5
|
+
class Configuration
|
|
6
|
+
attr_accessor :client, :credentials_key, :use_rails_cache, :use_rails_logger
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@client = nil
|
|
10
|
+
@credentials_key = :carddb
|
|
11
|
+
@use_rails_cache = true
|
|
12
|
+
@use_rails_logger = true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CardDB
|
|
4
|
+
module Rails
|
|
5
|
+
module DeckControllerHelpers
|
|
6
|
+
def carddb_preview_deck(token, client: carddb_client)
|
|
7
|
+
client.decks.preview(token: token)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def carddb_embed_deck(token, client: carddb_client)
|
|
11
|
+
client.decks.embed(token: token)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def carddb_access_deck(token, client: carddb_client)
|
|
15
|
+
client.decks.access(token: token)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def carddb_exchange_deck_access_token(input, client: carddb_client)
|
|
19
|
+
client.decks.exchange_access_token(input: input)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def carddb_exchange_deck_session_token(input, client: carddb_client)
|
|
23
|
+
client.decks.exchange_session_token(input: input)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CardDB
|
|
4
|
+
module Rails
|
|
5
|
+
class DeckExportService
|
|
6
|
+
def self.call(deck_id:, format: 'SIMPLE_TEXT', client: nil)
|
|
7
|
+
new(deck_id: deck_id, format: format, client: client).call
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(deck_id:, format: 'SIMPLE_TEXT', client: nil)
|
|
11
|
+
@deck_id = deck_id
|
|
12
|
+
@format = format
|
|
13
|
+
@client = client || CardDB::Rails.client
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
@client.decks.export_deck(id: @deck_id, format: @format)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CardDB
|
|
4
|
+
module Rails
|
|
5
|
+
class DeckImportService
|
|
6
|
+
def self.call(input:, client: nil)
|
|
7
|
+
new(input: input, client: client).call
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(input:, client: nil)
|
|
11
|
+
@input = input
|
|
12
|
+
@client = client || CardDB::Rails.client
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
@client.decks.import_deck(input: @input)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'active_job'
|
|
4
|
+
|
|
5
|
+
module CardDB
|
|
6
|
+
module Rails
|
|
7
|
+
class DeckSyncJob < ActiveJob::Base
|
|
8
|
+
queue_as :default
|
|
9
|
+
|
|
10
|
+
def perform(external_ref:, input:)
|
|
11
|
+
DeckSyncService.call(external_ref: external_ref, input: input)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CardDB
|
|
4
|
+
module Rails
|
|
5
|
+
class DeckSyncService
|
|
6
|
+
def self.call(external_ref:, input:, client: nil)
|
|
7
|
+
new(external_ref: external_ref, input: input, client: client).call
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(external_ref:, input:, client: nil)
|
|
11
|
+
@external_ref = external_ref
|
|
12
|
+
@input = input
|
|
13
|
+
@client = client || CardDB::Rails.client
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call
|
|
17
|
+
@client.decks.upsert_by_external_ref(input: @input.merge(externalRef: @external_ref))
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|