axhub-sdk 0.2.0 → 0.3.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.
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ergonomic data layer for the AX Hub Ruby SDK.
4
+ #
5
+ # Public surface (mirrors the node `resources/data` layer):
6
+ #
7
+ # client.tenant(tenant_slug).app(app_slug).data.table(name_or_schema)
8
+ # client.tenant(tenant_slug).app(app_slug).data.discover(table)
9
+ #
10
+ # returns a DataTableClient with list / list_all / count / get / insert /
11
+ # insert_many / update / delete, plus the predicate DSL (where(col).eq(v) /
12
+ # and_(...) / block form), define_schema(...), and offset-only pagination.
13
+ require_relative 'data/errors'
14
+ require_relative 'data/dsl/schema'
15
+ require_relative 'data/dsl/ops'
16
+ require_relative 'data/dsl/validation'
17
+ require_relative 'data/pagination'
18
+ require_relative 'data/projection'
19
+ require_relative 'data/where_serializer'
20
+ require_relative 'data/schema_cache'
21
+ require_relative 'data/discover'
22
+ require_relative 'data/client'
23
+
24
+ module AxHub
25
+ module Data
26
+ # Fluent scope wrappers so the public chain reads `client.tenant(t).app(a).data`
27
+ # (mirrors node/python, where `.data` on the app scope yields the ergonomic
28
+ # AppDataFactory). `client.data` itself stays the operation-id OperationClient.
29
+ class AppScope
30
+ attr_reader :data
31
+
32
+ def initialize(ergo_data, tenant_slug, app_slug)
33
+ # `ergo_data` is the single per-client DataClient (memoized on Client), so
34
+ # its schema cache persists across every tenant().app() chain — node parity.
35
+ @data = ergo_data.scoped(tenant_slug).app(app_slug)
36
+ end
37
+ end
38
+
39
+ class TenantScope
40
+ def initialize(ergo_data, tenant_slug)
41
+ @ergo_data = ergo_data
42
+ @tenant_slug = tenant_slug
43
+ end
44
+
45
+ def app(app_slug)
46
+ AppScope.new(@ergo_data, @tenant_slug, app_slug)
47
+ end
48
+ end
49
+ end
50
+
51
+ # --- Ergonomic data layer fluent surface (mirrors node client.tenant().app().data) ---
52
+ # `client.data` stays the operation-id route-table OperationClient (the
53
+ # conformance vectors + e2e tests depend on it). The ergonomic data layer is
54
+ # reached only through the tenant/app fluent chain, exactly as in node/python.
55
+ class Client
56
+ # The single per-client ergonomic DataClient, lazily memoized so the schema
57
+ # cache (TTL/negative-TTL/LRU) survives across tenant().app() chains (mirrors
58
+ # node, where `data` is one per-SDK DataClient).
59
+ def ergo_data
60
+ @ergo_data ||= AxHub::Data::DataClient.new(self, schema_cache: @schema_cache_opt)
61
+ end
62
+
63
+ def tenant(tenant_slug)
64
+ AxHub::Data::TenantScope.new(ergo_data, tenant_slug)
65
+ end
66
+ end
67
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AxHub
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/axhub_sdk.rb CHANGED
@@ -290,6 +290,30 @@ module AxHub
290
290
  req.body = JSON.generate(body)
291
291
  end
292
292
  end
293
+ _send(req, uri, camelize: true)
294
+ end
295
+
296
+ # Raw-path transport for endpoints with no generated operation-id facade
297
+ # (the ergonomic data ring: dynamic CRUD + runtime schema discover). `path`
298
+ # is already fully substituted; `query` keys may be array-valued so repeated
299
+ # filter params (e.g. `tag=eq.a&tag=eq.b`) serialize via URI.encode_www_form.
300
+ # Defaults to `camelize: false` to mirror the node data transport: row bodies
301
+ # and list envelopes (`has_more`/`per_page`) are returned verbatim.
302
+ def request_raw(method, path, query: {}, body: nil, camelize: false)
303
+ uri = URI(@base_url + path); uri.query = URI.encode_www_form(query) unless query.nil? || query.empty?
304
+ req_class = Net::HTTP.const_get(method.to_s.capitalize); req = req_class.new(uri); req['X-Request-ID'] = request_id
305
+ unless body.nil?
306
+ req['Content-Type'] = 'application/json'; req.body = JSON.generate(body)
307
+ end
308
+ _send(req, uri, camelize: camelize)
309
+ end
310
+
311
+ private
312
+
313
+ # Shared auth + send + redirect policy + response/error normalization tail.
314
+ # `camelize: true` mirrors the operation-id `request` path (snake->camel
315
+ # rewriting); `camelize: false` returns parsed JSON verbatim (data ring).
316
+ def _send(req, uri, camelize:)
293
317
  if @token
294
318
  case @token_type
295
319
  when :pat then req['X-Api-Key'] = @token
@@ -319,9 +343,9 @@ module AxHub
319
343
  retryable = err.key?('retryable') ? !!err['retryable'] : !!info&.retryable
320
344
  raise Error.new(category: err['category'] || info&.category || 'unknown', code: err['code'] || "http_#{res.code}", message: err['message'], status: res.code.to_i, retryable: retryable, request_id: err['request_id'] || err['requestId'])
321
345
  end
322
- AxHub.camelize(parsed)
346
+ camelize ? AxHub.camelize(parsed) : parsed
323
347
  end
324
- private
348
+
325
349
  def request_id
326
350
  (Time.now.to_i.to_s + SecureRandom.hex(16))[0, 26]
327
351
  end
@@ -352,3 +376,4 @@ module AxHub
352
376
  end
353
377
 
354
378
  require_relative 'axhub_sdk/operations'
379
+ require_relative 'axhub_sdk/data'
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: axhub-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jocoding AX Partners
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-08 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2026-06-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '13.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '13.0'
13
41
  description: Ruby SDK for AX Hub API route facades, error metadata, and conformance-tested
14
42
  client behavior.
15
43
  email:
@@ -22,6 +50,17 @@ files:
22
50
  - README.md
23
51
  - axhub-sdk.gemspec
24
52
  - lib/axhub_sdk.rb
53
+ - lib/axhub_sdk/data.rb
54
+ - lib/axhub_sdk/data/client.rb
55
+ - lib/axhub_sdk/data/discover.rb
56
+ - lib/axhub_sdk/data/dsl/ops.rb
57
+ - lib/axhub_sdk/data/dsl/schema.rb
58
+ - lib/axhub_sdk/data/dsl/validation.rb
59
+ - lib/axhub_sdk/data/errors.rb
60
+ - lib/axhub_sdk/data/pagination.rb
61
+ - lib/axhub_sdk/data/projection.rb
62
+ - lib/axhub_sdk/data/schema_cache.rb
63
+ - lib/axhub_sdk/data/where_serializer.rb
25
64
  - lib/axhub_sdk/operations.rb
26
65
  - lib/axhub_sdk/version.rb
27
66
  homepage: https://github.com/jocoding-ax-partners/axhub-sdk-ruby