model_driven_api 3.5.6 → 3.5.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 394acca44589bd5e704f0cbf966eab8eded237d1aafdcbc0f2a14318b78ceadc
4
- data.tar.gz: 302dfcc51da16d380148f5fc1953065a0cf537657bb713bc705dab5184a8a4d2
3
+ metadata.gz: df0f02193f91b162df1248961aabf8c4281440c49dc7f3147cb400db371f68d0
4
+ data.tar.gz: d0c42164dbde60bbb752110daa5e8b757ac8a7e0bfcc5e9b6a67898a02555ec8
5
5
  SHA512:
6
- metadata.gz: 7da1bbd9f1f565296c34479faefba15d9ba77036cea33904740e2c74c646b7aef9c90681e8fdcec8b254efea20818c0719188271f135d58dccac4064100e0c90
7
- data.tar.gz: 10702aeed1e9c0c925faa0214c01f0e2ed12187c09a23b559e5f9b1af89ce25cbe268883508bd7c9f75911aba49a2177190177eceb24c6e1947331420f0985dd
6
+ metadata.gz: e4e3c99b867169e19e966832ef085d5d8ffd67399a6a4047fa504a950c75a63b1a96719502d8c9d01dca6efd5d32ae7d9b07d69c8c7ad361a561cde24b957d65
7
+ data.tar.gz: 6bcff8f0e6c408ead804305dd386e827edf582b0e950774bcf9c79cdb9c24fe7577dab86d813e0c1a9cb8ff7e3355079f451d23eb1a8327df658824cd876b167
@@ -1135,6 +1135,174 @@ Content-Type: application/json
1135
1135
  "count": 156
1136
1136
  }
1137
1137
 
1138
+ ```
1139
+
1140
+ ## ActiveStorage Integration: React Frontend & Rails Backend
1141
+
1142
+ ### Overview
1143
+
1144
+ This guide explains how to handle file uploads (via Camera or Gallery) and attachment deletions using a **React** frontend and a **Ruby on Rails** backend.
1145
+
1146
+ The Rails model uses a virtual attribute strategy for deletion:
1147
+
1148
+ * **Upload:** handled via `has_many_attached :assets`
1149
+ * **Deletion:** handled via `attr_accessor :remove_assets`
1150
+
1151
+ ---
1152
+
1153
+ ### 1. Handling File Objects (No "Paths" needed)
1154
+
1155
+ In a web/mobile context (React Web or PWA), you do not need a file system path. When a user takes a photo or selects a file, the browser creates a native **`File`** object (a type of `Blob`).
1156
+
1157
+ You must send this binary object to the backend using **`FormData`**.
1158
+
1159
+ #### React Component Example
1160
+
1161
+ This component handles:
1162
+
1163
+ 1. **File Input:** Supports both gallery selection and direct camera capture on mobile.
1164
+ 2. **FormData:** Constructs the payload correctly for Rails.
1165
+ 3. **API Call:** Sends the data via `fetch`.
1166
+
1167
+ ```jsx
1168
+ import React, { useState } from 'react';
1169
+
1170
+ const ProductForm = () => {
1171
+ const [title, setTitle] = useState('');
1172
+ const [selectedFiles, setSelectedFiles] = useState([]);
1173
+
1174
+ // Handle file selection
1175
+ const handleFileChange = (event) => {
1176
+ // event.target.files is a FileList; convert to Array for convenience
1177
+ const filesArray = Array.from(event.target.files);
1178
+ setSelectedFiles(filesArray);
1179
+ };
1180
+
1181
+ // Handle form submission
1182
+ const handleSubmit = async (event) => {
1183
+ event.preventDefault();
1184
+
1185
+ // 1. Create the FormData object
1186
+ const formData = new FormData();
1187
+
1188
+ // 2. Append text fields
1189
+ formData.append('product[title]', title);
1190
+
1191
+ // 3. Append FILES
1192
+ // It is crucial to use 'product[assets][]' with brackets.
1193
+ // This tells Rails to treat it as an array of attachments.
1194
+ selectedFiles.forEach((file) => {
1195
+ formData.append('product[assets][]', file);
1196
+ });
1197
+
1198
+ try {
1199
+ const response = await fetch('http://localhost:3000/api/products', {
1200
+ method: 'POST',
1201
+ // IMPORTANT NOTE:
1202
+ // When using FormData, do NOT set 'Content-Type': 'application/json'
1203
+ // and do NOT manually set 'multipart/form-data'.
1204
+ // The browser will automatically set the header with the correct 'boundary'.
1205
+ body: formData,
1206
+ });
1207
+
1208
+ if (response.ok) {
1209
+ console.log("Upload successful!");
1210
+ // Reset form or redirect...
1211
+ } else {
1212
+ console.error("Upload error");
1213
+ }
1214
+ } catch (error) {
1215
+ console.error("Network error:", error);
1216
+ }
1217
+ };
1218
+
1219
+ return (
1220
+ <form onSubmit={handleSubmit} style={{ padding: '20px' }}>
1221
+ <div>
1222
+ <label>Product Title:</label>
1223
+ <input
1224
+ type="text"
1225
+ value={title}
1226
+ onChange={(e) => setTitle(e.target.value)}
1227
+ />
1228
+ </div>
1229
+
1230
+ <div style={{ marginTop: '20px' }}>
1231
+ <label>Photos (Camera or Gallery):</label>
1232
+ {/* accept="image/*": Accepts only images.
1233
+ capture="environment": On mobile, opens the rear camera directly.
1234
+ Remove 'capture' if you want the user to choose between Gallery and Camera.
1235
+ multiple: Allows selecting multiple photos.
1236
+ */}
1237
+ <input
1238
+ type="file"
1239
+ accept="image/*"
1240
+ multiple
1241
+ onChange={handleFileChange}
1242
+ />
1243
+ </div>
1244
+
1245
+ <button type="submit" style={{ marginTop: '20px' }}>
1246
+ Save Product
1247
+ </button>
1248
+ </form>
1249
+ );
1250
+ };
1251
+
1252
+ export default ProductForm;
1253
+
1254
+ ```
1255
+
1256
+ ---
1257
+
1258
+ ### 2. Key Implementation Details
1259
+
1260
+ #### A. The `capture` Attribute
1261
+
1262
+ * `<input type="file" capture="environment" />`: Opens the **rear camera** directly on iOS/Android.
1263
+ * `<input type="file" capture="user" />`: Opens the **front camera** (selfie mode).
1264
+ * **No `capture` attribute** (but with `accept="image/*"`): The device will prompt the user: *"Take Photo or Photo Library?"*. This is often the best UX.
1265
+
1266
+ #### B. The `forEach` Loop
1267
+
1268
+ You cannot pass an array directly into `FormData` (e.g., `formData.append('key', myArray)` will not work).
1269
+ Rails expects multiple values for the same key. You must append each file individually:
1270
+
1271
+ ```javascript
1272
+ // Correct
1273
+ files.forEach(file => formData.append('product[assets][]', file));
1274
+
1275
+ ```
1276
+
1277
+ #### C. The Content-Type Header
1278
+
1279
+ This is a common pitfall. When using `fetch` or `axios` with a `FormData` body, **do not set the Content-Type header manually**.
1280
+ The browser must generate it automatically to include the boundary:
1281
+ `Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...`
1282
+
1283
+ ---
1284
+
1285
+ ## 3. Handling Deletion (PATCH Request)
1286
+
1287
+ To remove specific attachments using the `remove_assets` virtual attribute defined in your Rails model, send the **Attachment IDs** (not the file objects).
1288
+
1289
+ ```javascript
1290
+ const handleUpdate = async () => {
1291
+ const formData = new FormData();
1292
+
1293
+ // 1. Add new files (if any)
1294
+ newFiles.forEach(file => formData.append('product[assets][]', file));
1295
+
1296
+ // 2. Add IDs to remove
1297
+ // (e.g., idsToRemove is an array like [12, 45])
1298
+ idsToRemove.forEach(id => formData.append('product[remove_assets][]', id));
1299
+
1300
+ await fetch(`http://localhost:3000/api/products/${productId}`, {
1301
+ method: 'PATCH',
1302
+ body: formData
1303
+ });
1304
+ };
1305
+
1138
1306
  ```
1139
1307
  MARKDOWN
1140
1308
  info
@@ -1,3 +1,3 @@
1
1
  module ModelDrivenApi
2
- VERSION = "3.5.6".freeze
2
+ VERSION = "3.5.7".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: model_driven_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.5.6
4
+ version: 3.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gabriele Tassoni