conexa 0.0.9 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1dba13ee9b0dc841e758d5cb0b9c468a480ca564129666814c0e25892ea4cff
4
- data.tar.gz: 732eebd11f3ec3b5ee727f030ac51493fbce0e6212fb510dfcf5ba476d6424c4
3
+ metadata.gz: c71329999c6d13c4e935ef1e37a8eca6df5811c44d0326184cc1676d31fb199b
4
+ data.tar.gz: c6f6946375a05712ed7d952a4314738e86a2429bf3a249e269d2fd40b254c11f
5
5
  SHA512:
6
- metadata.gz: c45016bd74d1e830ae18915be67be0f5a5e5dfc87581a9fe6685775b83c0aa08a4b8a10389d1ae9c92bfbffbf1b1b076b244c736c74002688f6be84e4115f02e
7
- data.tar.gz: ff7831e660833da76d85325532889590d27f60451747980427ce7e2d8f7f5e48ec02960d5fdad5bfb5ea7abadab9fdec19e6e30c5bb2adbc0be2e9527680ec3b
6
+ metadata.gz: 13fc7859937cd156cd8ae68b78d6c303b62460d4f6e682f270f58927e87c8a99e29e7d43a4814318b53ed5c5e20ce5e0ea3892d46db8d4d08147e9aea79a690e
7
+ data.tar.gz: c1ae89758b0cea23df6c5f5cc16895c060baef04da9a31fefd58eb7ac9c7ba1b1483dfc44b81c00ddfb592cada2e34cace170237e5f298b3ea8b3217572bd1c9
data/CHANGELOG.md CHANGED
@@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.1] - 2026-03-31
11
+
12
+ ### Added
13
+ - Instance methods `Customer#persons`, `Customer#contracts`, `Customer#charges` for idiomatic Ruby usage
14
+ - `customer = Conexa::Customer.find(127); customer.persons` — fetches persons for the loaded customer
15
+ - Class methods `Customer.persons(id)`, `Customer.contracts(id)`, `Customer.charges(id)` remain available
16
+ - Useful to save a request when you don't need the customer data itself
17
+
18
+ ### Changed
19
+ - Customer sub-resource methods now available in both class and instance forms
20
+
21
+ ## [0.1.0] - 2026-03-31
22
+
23
+ ### Added
24
+ - `Result#next_page` — automatically fetches the next page preserving original filter params
25
+ - `Result#has_next?` — convenience method to check if more pages are available
26
+ - Pagination migration guide added to README (EN/PT-BR) and REFERENCE.md
27
+ - Validation for `limit` (must be positive integer) and `offset` (must be non-negative integer) parameters
28
+ - `frozen_string_literal: true` pragma added to all Ruby source files
29
+ - ActiveSupport compatibility guard for `blank?`/`present?` monkey-patches
30
+ - `respond_to_missing?` implemented in `ConexaObject` and `Result` (Ruby best practice)
31
+ - Integration tests for all new API v2 resources (24 specs)
32
+ - Unit tests for pagination validation and `class_name` camelCase preservation (6 specs)
33
+ - Total: 502 specs
34
+
35
+ ### Changed
36
+ - **Breaking**: Default pagination is now `limit: 100, offset: 0` (was `page: 1, size: 100`)
37
+ - Calling `.all` or `.find_by` without pagination params now uses the new model
38
+ - Legacy `page`/`size` is only used when explicitly passed (emits deprecation warning)
39
+ - `Model.all` simplified — delegates directly to `find_by` without double param extraction
40
+ - `Model.class_name` now returns proper lowerCamelCase (e.g. `recurringSale` instead of `recurringsale`)
41
+ - `OrderCommom` renamed to `OrderCommon` (backwards-compatible alias kept)
42
+ - `Bill#save` now raises `NoMethodError` with descriptive message
43
+ - README updated: all examples now use correct method names (`.all`, `.find`, `.destroy`)
44
+ - README error handling section rewritten with actual exception classes
45
+
46
+ ### Fixed
47
+ - **Concurrency bug**: `DEFAULT_HEADERS` constant was mutated on every request; now uses `.dup`
48
+ - **Boolean false bug**: `Result#method_missing` skipped `false` attribute values due to `||` operator
49
+ - **ValidationError crash**: undefined variable `error` in block scope — fixed to use `msg`
50
+ - **TokenManager typo**: `ShiConexa` → `Conexa` in credentials handling
51
+ - **Singularize**: `SINGULARS` patterns were stored as strings, never used as regex — rewritten with proper `Regexp` objects
52
+ - **Deep copy**: `Result#next_page` now uses `Marshal.load(Marshal.dump(...))` to prevent param mutation between pages
53
+ - Stray `-` character removed from `util.rb`
54
+ - Removed dead code: `Hash#except_nested` monkey-patch, commented-out `to_s` method
55
+
10
56
  ## [0.0.9] - 2026-03-30
11
57
 
12
58
  ### Added
@@ -87,7 +133,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
87
133
  - Charge with settle and PIX methods
88
134
  - Pagination support
89
135
 
90
- [Unreleased]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.9...HEAD
136
+ [Unreleased]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.1.1...HEAD
137
+ [0.1.1]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.1.0...v0.1.1
138
+ [0.1.0]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.9...v0.1.0
91
139
  [0.0.9]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.8...v0.0.9
92
140
  [0.0.8]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.7...v0.0.8
93
141
  [0.0.7]: https://github.com/guilhermegazzinelli/conexa-ruby/compare/v0.0.6...v0.0.7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- conexa (0.0.9)
4
+ conexa (0.1.1)
5
5
  jwt
6
6
  multi_json
7
7
  rest-client
data/README.md CHANGED
@@ -46,13 +46,13 @@ Conexa.configure do |config|
46
46
  end
47
47
 
48
48
  # List customers
49
- customers = Conexa::Customer.list
49
+ customers = Conexa::Customer.all
50
50
  customers.data.each do |customer|
51
51
  puts "#{customer.customer_id}: #{customer.name}"
52
52
  end
53
53
 
54
54
  # Get a specific customer
55
- customer = Conexa::Customer.retrieve(127)
55
+ customer = Conexa::Customer.find(127)
56
56
  puts customer.name
57
57
  puts customer.address.city
58
58
  ```
@@ -110,7 +110,7 @@ customer = Conexa::Customer.create(
110
110
  )
111
111
 
112
112
  # Retrieve customer
113
- customer = Conexa::Customer.retrieve(127)
113
+ customer = Conexa::Customer.find(127)
114
114
  customer.name # => "Empresa ABC Ltda"
115
115
  customer.company_id # => 3
116
116
  customer.is_active # => true
@@ -118,14 +118,28 @@ customer.address.city # => "Campinas"
118
118
  customer.legal_person['cnpj'] # => "99.557.155/0001-90"
119
119
 
120
120
  # Update customer
121
- Conexa::Customer.update(127, name: 'New Name', cell_number: '11888887777')
121
+ customer = Conexa::Customer.find(127)
122
+ customer.name = 'New Name'
123
+ customer.cell_number = '11888887777'
124
+ customer.save
122
125
 
123
126
  # List customers with filters (new pagination)
124
- customers = Conexa::Customer.list(
127
+ customers = Conexa::Customer.all(
125
128
  company_id: [3],
126
129
  is_active: true,
127
130
  limit: 20
128
131
  )
132
+
133
+ # Sub-resources (instance methods)
134
+ customer = Conexa::Customer.find(127)
135
+ customer.persons # => persons for this customer
136
+ customer.contracts # => contracts for this customer
137
+ customer.charges # => charges for this customer
138
+
139
+ # Sub-resources (class methods — saves a request)
140
+ Conexa::Customer.persons(127)
141
+ Conexa::Customer.contracts(127)
142
+ Conexa::Customer.charges(127)
129
143
  ```
130
144
 
131
145
  ### Contract
@@ -152,7 +166,7 @@ contract = Conexa::Contract.create_with_products(
152
166
  )
153
167
 
154
168
  # Retrieve contract
155
- contract = Conexa::Contract.retrieve(456)
169
+ contract = Conexa::Contract.find(456)
156
170
 
157
171
  # Cancel contract
158
172
  Conexa::Contract.cancel(456, cancel_date: '2024-12-31')
@@ -174,13 +188,13 @@ sale = Conexa::Sale.create(
174
188
  puts sale.id # => 188481
175
189
 
176
190
  # Retrieve sale
177
- sale = Conexa::Sale.retrieve(188510)
191
+ sale = Conexa::Sale.find(188510)
178
192
  sale.status # => "notBilled"
179
193
  sale.amount # => 80.99
180
194
  sale.discount_value # => 69.21
181
195
 
182
196
  # List sales
183
- sales = Conexa::Sale.list(
197
+ sales = Conexa::Sale.all(
184
198
  customer_id: [450, 216],
185
199
  status: 'notBilled',
186
200
  date_from: '2024-01-01',
@@ -190,10 +204,13 @@ sales = Conexa::Sale.list(
190
204
  )
191
205
 
192
206
  # Update sale
193
- Conexa::Sale.update(188510, quantity: 2, amount: 150.00)
207
+ sale = Conexa::Sale.find(188510)
208
+ sale.quantity = 2
209
+ sale.amount = 150.00
210
+ sale.save
194
211
 
195
212
  # Delete sale (only if not billed)
196
- Conexa::Sale.delete(188510)
213
+ Conexa::Sale.destroy(188510)
197
214
  ```
198
215
 
199
216
  ### Recurring Sale
@@ -208,20 +225,20 @@ recurring = Conexa::RecurringSale.create(
208
225
  )
209
226
 
210
227
  # List recurring sales for a contract
211
- Conexa::RecurringSale.list(contract_id: 456)
228
+ Conexa::RecurringSale.all(contract_id: 456)
212
229
  ```
213
230
 
214
231
  ### Charge
215
232
 
216
233
  ```ruby
217
234
  # Retrieve charge
218
- charge = Conexa::Charge.retrieve(789)
235
+ charge = Conexa::Charge.find(789)
219
236
  charge.status # => "paid"
220
237
  charge.amount # => 299.90
221
238
  charge.due_date # => "2024-02-10"
222
239
 
223
240
  # List charges
224
- charges = Conexa::Charge.list(
241
+ charges = Conexa::Charge.all(
225
242
  customer_id: [127],
226
243
  status: 'pending',
227
244
  due_date_from: '2024-01-01',
@@ -239,10 +256,10 @@ Conexa::Charge.send_email(789)
239
256
 
240
257
  ```ruby
241
258
  # Retrieve bill
242
- bill = Conexa::Bill.retrieve(101)
259
+ bill = Conexa::Bill.find(101)
243
260
 
244
261
  # List bills
245
- bills = Conexa::Bill.list(
262
+ bills = Conexa::Bill.all(
246
263
  customer_id: [127],
247
264
  page: 1,
248
265
  size: 50
@@ -253,10 +270,10 @@ bills = Conexa::Bill.list(
253
270
 
254
271
  ```ruby
255
272
  # List plans
256
- plans = Conexa::Plan.list(company_id: [3])
273
+ plans = Conexa::Plan.all(company_id: [3])
257
274
 
258
275
  # Retrieve plan
259
- plan = Conexa::Plan.retrieve(5)
276
+ plan = Conexa::Plan.find(5)
260
277
  plan.name # => "Plano Básico"
261
278
  plan.price # => 99.90
262
279
  ```
@@ -265,66 +282,66 @@ plan.price # => 99.90
265
282
 
266
283
  ```ruby
267
284
  # List products
268
- products = Conexa::Product.list(company_id: [3], limit: 50)
285
+ products = Conexa::Product.all(company_id: [3], limit: 50)
269
286
 
270
287
  # Retrieve product
271
- product = Conexa::Product.retrieve(101)
288
+ product = Conexa::Product.find(101)
272
289
 
273
290
  # Create product
274
291
  product = Conexa::Product.create(name: 'Novo Produto', company_id: 3)
275
292
 
276
293
  # Delete product
277
- Conexa::Product.delete(101)
294
+ Conexa::Product.destroy(101)
278
295
  ```
279
296
 
280
297
  ### Receiving Method
281
298
 
282
299
  ```ruby
283
- methods = Conexa::ReceivingMethod.list(limit: 50)
284
- method = Conexa::ReceivingMethod.retrieve(11)
300
+ methods = Conexa::ReceivingMethod.all(limit: 50)
301
+ method = Conexa::ReceivingMethod.find(11)
285
302
  method.name # => "Cartão de Crédito"
286
303
  ```
287
304
 
288
305
  ### Payment Method
289
306
 
290
307
  ```ruby
291
- methods = Conexa::PaymentMethod.list(limit: 50)
292
- method = Conexa::PaymentMethod.retrieve(2)
308
+ methods = Conexa::PaymentMethod.all(limit: 50)
309
+ method = Conexa::PaymentMethod.find(2)
293
310
  ```
294
311
 
295
312
  ### Bill Category / Subcategory
296
313
 
297
314
  ```ruby
298
- categories = Conexa::BillCategory.list(limit: 50)
299
- subcategories = Conexa::BillSubcategory.list(limit: 50)
315
+ categories = Conexa::BillCategory.all(limit: 50)
316
+ subcategories = Conexa::BillSubcategory.all(limit: 50)
300
317
  ```
301
318
 
302
319
  ### Cost Center
303
320
 
304
321
  ```ruby
305
- centers = Conexa::CostCenter.list(limit: 50)
306
- center = Conexa::CostCenter.retrieve(11)
322
+ centers = Conexa::CostCenter.all(limit: 50)
323
+ center = Conexa::CostCenter.find(11)
307
324
  ```
308
325
 
309
326
  ### Account
310
327
 
311
328
  ```ruby
312
- accounts = Conexa::Account.list(limit: 50)
313
- account = Conexa::Account.retrieve(23)
329
+ accounts = Conexa::Account.all(limit: 50)
330
+ account = Conexa::Account.find(23)
314
331
  ```
315
332
 
316
333
  ### Service Category
317
334
 
318
335
  ```ruby
319
- categories = Conexa::ServiceCategory.list(limit: 50)
320
- category = Conexa::ServiceCategory.retrieve(1)
336
+ categories = Conexa::ServiceCategory.all(limit: 50)
337
+ category = Conexa::ServiceCategory.find(1)
321
338
  ```
322
339
 
323
340
  ### Room Booking
324
341
 
325
342
  ```ruby
326
343
  # List bookings
327
- bookings = Conexa::RoomBooking.list(limit: 20)
344
+ bookings = Conexa::RoomBooking.all(limit: 20)
328
345
 
329
346
  # Create booking
330
347
  booking = Conexa::RoomBooking.create(room_id: 5, customer_id: 127)
@@ -350,20 +367,20 @@ card = Conexa::CreditCard.create(
350
367
  )
351
368
 
352
369
  # List customer's cards
353
- cards = Conexa::CreditCard.list(customer_id: 127)
370
+ cards = Conexa::CreditCard.all(customer_id: 127)
354
371
 
355
372
  # Delete card
356
- Conexa::CreditCard.delete(card_id)
373
+ Conexa::CreditCard.destroy(card_id)
357
374
  ```
358
375
 
359
376
  ### Company (Unit)
360
377
 
361
378
  ```ruby
362
379
  # List companies/units
363
- companies = Conexa::Company.list
380
+ companies = Conexa::Company.all
364
381
 
365
382
  # Retrieve company
366
- company = Conexa::Company.retrieve(3)
383
+ company = Conexa::Company.find(3)
367
384
  company.name # => "Matriz"
368
385
  company.document # => "12.345.678/0001-90"
369
386
  ```
@@ -373,19 +390,27 @@ company.document # => "12.345.678/0001-90"
373
390
  ### New Pagination (recommended) — `limit`/`offset`/`hasNext`
374
391
 
375
392
  ```ruby
376
- result = Conexa::Customer.list(limit: 50)
393
+ result = Conexa::Customer.all(limit: 50)
377
394
 
378
395
  result.data # Array of customers
379
396
  result.pagination.limit # => 50
380
397
  result.pagination.offset # => 0
381
- result.pagination.has_next # => true/false
398
+ result.has_next? # => true/false
382
399
 
383
- # Iterate through all pages
400
+ # Iterate through all pages using next_page
401
+ result = Conexa::Customer.all(limit: 50)
402
+ loop do
403
+ result.data.each { |customer| process(customer) }
404
+ break unless result.has_next?
405
+ result = result.next_page
406
+ end
407
+
408
+ # Or manually with offset
384
409
  offset = 0
385
410
  loop do
386
- result = Conexa::Customer.list(limit: 50, offset: offset)
411
+ result = Conexa::Customer.all(limit: 50, offset: offset)
387
412
  result.data.each { |customer| process(customer) }
388
- break unless result.pagination.has_next
413
+ break unless result.has_next?
389
414
  offset += 50
390
415
  end
391
416
  ```
@@ -394,7 +419,7 @@ end
394
419
 
395
420
  ```ruby
396
421
  # Still works but emits a deprecation warning
397
- result = Conexa::Customer.list(page: 1, size: 20)
422
+ result = Conexa::Customer.all(page: 1, size: 20)
398
423
  result.pagination.current_page # => 1
399
424
  result.pagination.total_pages # => 10
400
425
  ```
@@ -407,12 +432,12 @@ The legacy `page`/`size` pagination is deprecated and will be removed on **2026-
407
432
 
408
433
  ```ruby
409
434
  # BEFORE (legacy — deprecated)
410
- result = Conexa::Customer.list(page: 1, size: 50)
411
- result = Conexa::Customer.list(page: 2, size: 50)
435
+ result = Conexa::Customer.all(page: 1, size: 50)
436
+ result = Conexa::Customer.all(page: 2, size: 50)
412
437
 
413
438
  # AFTER (new)
414
- result = Conexa::Customer.list(limit: 50) # offset defaults to 0
415
- result = Conexa::Customer.list(limit: 50, offset: 50) # second page
439
+ result = Conexa::Customer.all(limit: 50) # offset defaults to 0
440
+ result = Conexa::Customer.all(limit: 50, offset: 50) # second page
416
441
  ```
417
442
 
418
443
  **Conversion formula:** `offset = (page - 1) * size`
@@ -436,19 +461,18 @@ result.pagination.has_next # => true/false
436
461
  # BEFORE (legacy)
437
462
  page = 1
438
463
  loop do
439
- result = Conexa::Customer.list(page: page, size: 100)
464
+ result = Conexa::Customer.all(page: page, size: 100)
440
465
  break if result.empty?
441
466
  result.data.each { |c| process(c) }
442
467
  page += 1
443
468
  end
444
469
 
445
- # AFTER (new)
446
- offset = 0
470
+ # AFTER (new — using next_page)
471
+ result = Conexa::Customer.all(limit: 100)
447
472
  loop do
448
- result = Conexa::Customer.list(limit: 100, offset: offset)
449
473
  result.data.each { |c| process(c) }
450
- break unless result.pagination.has_next
451
- offset += 100
474
+ break unless result.has_next?
475
+ result = result.next_page
452
476
  end
453
477
  ```
454
478
 
@@ -481,29 +505,22 @@ begin
481
505
  customer = Conexa::Customer.create(name: '')
482
506
  rescue Conexa::ValidationError => e
483
507
  # Field validation errors (400)
484
- e.errors.each do |error|
485
- puts "#{error['field']}: #{error['messages'].join(', ')}"
486
- end
487
- rescue Conexa::AuthenticationError => e
488
- # Authentication required (401)
489
- puts e.message
490
- rescue Conexa::AuthorizationError => e
491
- # Not authorized (403)
492
508
  puts e.message
493
- rescue Conexa::NotFoundError => e
509
+ rescue Conexa::NotFound => e
494
510
  # Resource not found (404)
495
511
  puts e.message
496
- rescue Conexa::UnprocessableError => e
497
- # Business logic error (422)
498
- e.errors.each do |error|
499
- puts "#{error['code']}: #{error['message']}"
500
- end
501
- rescue Conexa::RateLimitError => e
502
- # Too many requests (429)
503
- puts "Rate limit exceeded. Retry after #{e.retry_after} seconds"
504
- rescue Conexa::ApiError => e
505
- # Generic API error
506
- puts "Error #{e.status}: #{e.message}"
512
+ rescue Conexa::ResponseError => e
513
+ # API response error (4xx/5xx)
514
+ puts e.message
515
+ rescue Conexa::RequestError => e
516
+ # Request-level error (invalid params, etc.)
517
+ puts e.message
518
+ rescue Conexa::ConnectionError => e
519
+ # Network/connection error
520
+ puts e.message
521
+ rescue Conexa::ConexaError => e
522
+ # Generic Conexa error (catch-all)
523
+ puts e.message
507
524
  end
508
525
  ```
509
526
 
data/README_pt-BR.md CHANGED
@@ -73,25 +73,27 @@ Cada recurso segue um conjunto consistente de padrões de métodos:
73
73
 
74
74
  ### Paginação
75
75
 
76
- A API utiliza os parâmetros `page` e `size` para paginação com os seguintes valores padrão:
77
-
78
- - **page**: Número da página atual (padrão: `1`)
79
- - **size**: Quantidade de itens por página (padrão: `100`)
76
+ A gem utiliza por padrão `limit: 100, offset: 0` (nova paginação). O modelo legado `page`/`size` ainda funciona mas emite um aviso de depreciação.
80
77
 
81
78
  #### Chamando com Paginação
82
79
 
83
80
  ```ruby
84
- # Paginação padrão (página 1, 100 itens)
81
+ # Paginação padrão (limit: 100, offset: 0)
85
82
  Conexa::Customer.all
86
83
 
87
- # Argumentos posicionais (página, tamanho)
88
- Conexa::Customer.all(2, 50) # página 2, 50 itens por página
84
+ # Com limite customizado
85
+ Conexa::Customer.all(limit: 50)
89
86
 
90
- # Parâmetros nomeados
91
- Conexa::Customer.all(page: 3, size: 25)
87
+ # Com offset
88
+ Conexa::Customer.all(limit: 50, offset: 100)
92
89
 
93
- # Usando o alias `where`
94
- Conexa::Customer.where(page: 2, size: 10)
90
+ # Iteração automática com next_page
91
+ resultado = Conexa::Customer.all(limit: 50)
92
+ loop do
93
+ resultado.data.each { |cliente| processa(cliente) }
94
+ break unless resultado.has_next?
95
+ resultado = resultado.next_page
96
+ end
95
97
  ```
96
98
 
97
99
  ### Objeto Result
@@ -309,14 +311,22 @@ resultado = Conexa::Customer.all(limit: 50)
309
311
  resultado.data # Array de clientes
310
312
  resultado.pagination.limit # => 50
311
313
  resultado.pagination.offset # => 0
312
- resultado.pagination.has_next # => true/false
314
+ resultado.has_next? # => true/false
313
315
 
314
- # Iterar por todas as páginas
316
+ # Iterar por todas as páginas usando next_page
317
+ resultado = Conexa::Customer.all(limit: 50)
318
+ loop do
319
+ resultado.data.each { |cliente| processa(cliente) }
320
+ break unless resultado.has_next?
321
+ resultado = resultado.next_page
322
+ end
323
+
324
+ # Ou manualmente com offset
315
325
  offset = 0
316
326
  loop do
317
327
  resultado = Conexa::Customer.all(limit: 50, offset: offset)
318
328
  resultado.data.each { |cliente| processa(cliente) }
319
- break unless resultado.pagination.has_next
329
+ break unless resultado.has_next?
320
330
  offset += 50
321
331
  end
322
332
  ```
@@ -364,13 +374,12 @@ loop do
364
374
  pagina += 1
365
375
  end
366
376
 
367
- # DEPOIS (novo)
368
- offset = 0
377
+ # DEPOIS (novo — usando next_page)
378
+ resultado = Conexa::Customer.all(limit: 100)
369
379
  loop do
370
- resultado = Conexa::Customer.all(limit: 100, offset: offset)
371
380
  resultado.data.each { |c| processa(c) }
372
- break unless resultado.pagination.has_next
373
- offset += 100
381
+ break unless resultado.has_next?
382
+ resultado = resultado.next_page
374
383
  end
375
384
  ```
376
385
 
@@ -443,6 +452,21 @@ cliente.destroy
443
452
  clientes = Conexa::Customer.find({ name: 'João Silva' }, 1, 20)
444
453
  ```
445
454
 
455
+ ##### Sub-recursos do Cliente
456
+
457
+ ```ruby
458
+ # Via instância (quando já possui o customer carregado)
459
+ cliente = Conexa::Customer.find(127)
460
+ cliente.persons # => Lista de persons deste cliente
461
+ cliente.contracts # => Lista de contratos deste cliente
462
+ cliente.charges # => Lista de cobranças deste cliente
463
+
464
+ # Via classe (economiza um request, não precisa buscar o customer)
465
+ Conexa::Customer.persons(127)
466
+ Conexa::Customer.contracts(127)
467
+ Conexa::Customer.charges(127)
468
+ ```
469
+
446
470
  #### Faturas (Bills)
447
471
 
448
472
  ##### Criando uma Fatura
data/REFERENCE.md CHANGED
@@ -32,7 +32,7 @@ Conexa is a Brazilian SaaS platform for **recurring billing**, **subscription ma
32
32
 
33
33
  ```ruby
34
34
  # Gemfile
35
- gem 'conexa', '~> 0.0.9'
35
+ gem 'conexa', '~> 0.1.1'
36
36
 
37
37
  # Or install directly
38
38
  gem install conexa
@@ -211,7 +211,13 @@ customer.destroy
211
211
  # or
212
212
  Conexa::Customer.destroy(127)
213
213
 
214
- # Sub-resources
214
+ # Sub-resources (instance methods — requires fetching customer first)
215
+ customer = Conexa::Customer.find(127)
216
+ customer.persons # List persons for this customer
217
+ customer.contracts # List contracts for this customer
218
+ customer.charges # List charges for this customer
219
+
220
+ # Sub-resources (class methods — saves a request, no need to fetch customer)
215
221
  Conexa::Customer.persons(127) # List persons for customer
216
222
  Conexa::Customer.contracts(127) # List contracts for customer
217
223
  Conexa::Customer.charges(127) # List charges for customer
@@ -1034,7 +1040,7 @@ person.destroy
1034
1040
 
1035
1041
  #### New Pagination (recommended) — `limit`/`offset`/`hasNext`
1036
1042
 
1037
- All `#all` and `#find_by` methods support the new pagination when `limit:` is passed:
1043
+ All `#all` and `#find_by` methods use `limit`/`offset` by default. When no pagination params are passed, the gem defaults to `limit: 100, offset: 0`.
1038
1044
 
1039
1045
  ```ruby
1040
1046
  # Page 1, 50 items
@@ -1043,24 +1049,35 @@ result = Conexa::Customer.all(limit: 50)
1043
1049
  result.data # => Array of customers
1044
1050
  result.pagination.limit # => 50
1045
1051
  result.pagination.offset # => 0
1046
- result.pagination.has_next # => true/false
1052
+ result.has_next? # => true/false
1047
1053
  result.empty? # => false
1048
1054
 
1049
- # Iterate all pages
1055
+ # Iterate all pages using next_page
1056
+ result = Conexa::Customer.all(limit: 100)
1057
+ loop do
1058
+ result.each { |customer| process(customer) }
1059
+ break unless result.has_next?
1060
+ result = result.next_page
1061
+ end
1062
+
1063
+ # Or manually with offset
1050
1064
  offset = 0
1051
1065
  loop do
1052
1066
  result = Conexa::Customer.all(limit: 100, offset: offset)
1053
1067
  break if result.empty?
1054
-
1055
1068
  result.each { |customer| process(customer) }
1056
- break unless result.pagination.has_next
1069
+ break unless result.has_next?
1057
1070
  offset += 100
1058
1071
  end
1059
1072
  ```
1060
1073
 
1074
+ **`next_page`** — Fetches the next page automatically, preserving all original filter params. Raises `StopIteration` when there are no more pages.
1075
+
1076
+ **`has_next?`** — Returns `true` if more pages are available.
1077
+
1061
1078
  #### Legacy Pagination (deprecated — removed 2026-08-01)
1062
1079
 
1063
- If `limit:` is NOT passed, the gem falls back to the old `page`/`size` model
1080
+ If `page:` or `size:` is explicitly passed, the gem uses the old pagination model
1064
1081
  (emitting a deprecation warning):
1065
1082
 
1066
1083
  ```ruby
@@ -1115,13 +1132,12 @@ loop do
1115
1132
  page += 1
1116
1133
  end
1117
1134
 
1118
- # AFTER
1119
- offset = 0
1135
+ # AFTER (using next_page)
1136
+ result = Conexa::Customer.all(limit: 100)
1120
1137
  loop do
1121
- result = Conexa::Customer.all(limit: 100, offset: offset)
1122
1138
  result.each { |c| process(c) }
1123
- break unless result.pagination.has_next
1124
- offset += 100
1139
+ break unless result.has_next?
1140
+ result = result.next_page
1125
1141
  end
1126
1142
  ```
1127
1143
 
@@ -1255,6 +1271,10 @@ The API enforces a limit of 100 requests per minute (changing to **60 requests p
1255
1271
 
1256
1272
  ## Version History
1257
1273
 
1274
+ - **v0.1.1** - Add instance methods for Customer sub-resources (persons, contracts, charges)
1275
+ - **v0.1.0** - Default new pagination, Result#next_page, code review fixes, 502 specs
1276
+ - **v0.0.9** - New pagination model (limit/offset), new API v2 resources
1277
+ - **v0.0.8** - Fix Auth.login, integration tests, REFERENCE.md rewrite
1258
1278
  - **v0.0.7** - Add Auth resource, fix Auth.login, add test suite with VCR
1259
1279
  - **v0.0.6** - Fix nil guard in camelize_hash, fix Result#empty? delegation
1260
1280
  - **v0.0.5** - Initial public release
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'jwt'
2
4
 
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Conexa
2
4
  class Configuration
3
5
  attr_accessor :api_token, :api_host