basecradle 0.9.0 → 0.10.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 +4 -4
- data/CHANGELOG.md +55 -0
- data/README.md +35 -0
- data/lib/basecradle/api_object.rb +42 -0
- data/lib/basecradle/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3acdf9b8dad4d29a40926030c480cc415b96ffff03c56a121838b4fdb42cd638
|
|
4
|
+
data.tar.gz: 2b88488a27cae407aa366cce9899da840b7ea1e74db86d21c511f0f2b1ac47d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7f753c98128c1935e0503dd1963917ac1918e171401b4a39f6c1b650cd716213f02c52b1dd538a54bb201de0ede89af19e7ab2ec9abfa79d2eba68d71faa5f57
|
|
7
|
+
data.tar.gz: 31772a4161d2e3563a835ffbd2c9ae5a2d9e285a157ca2772be3213037297b168f8e89b67e8290a7897a77a009add74b9769bf5e623ac5256ec19f26eece04d9
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,57 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
One deliberate departure from that format: this changelog keeps **no `Unreleased`
|
|
8
|
+
section**. The newest heading is always the version `lib/basecradle/version.rb` builds —
|
|
9
|
+
a release writes its entry and its version in the same PR — and `test/changelog_test.rb`
|
|
10
|
+
fails CI if the two ever disagree.
|
|
11
|
+
|
|
12
|
+
## [0.10.0] - 2026-09-30
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **A model serializes as the record it stands for**
|
|
17
|
+
([#191](https://github.com/basecradle/basecradle-ruby/issues/191)). `ApiObject` defined
|
|
18
|
+
no `to_json`, so serializing *any* model — `bc.me`, a `Timeline`, a `Message`, any
|
|
19
|
+
content object — fell through to Ruby's default and emitted a heap address:
|
|
20
|
+
`"#<BaseCradle::MessageContent:0x000072be94b09…>"`. Silently, with no field names and a
|
|
21
|
+
different value every run. `model.to_json` and `JSON.generate(model)` now emit the
|
|
22
|
+
record, and so does a model nested in an array or a hash. What `to_h` holds is exactly
|
|
23
|
+
what goes out: nothing renamed, nothing dropped, and a field newer than this SDK
|
|
24
|
+
release serialized like any other.
|
|
25
|
+
`WebhookEventHeaders` is unchanged — it subclasses `Hash`, so it already serialized as
|
|
26
|
+
the delivery's headers.
|
|
27
|
+
Long-standing; 0.9.0's typing of `timeline.items` content only widened which read path
|
|
28
|
+
reached it. **0.9.0's Migrating note on `to_json` is superseded by this release.**
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **`as_json`**, ActiveSupport's serialization hook, returning the same wire `Hash` as
|
|
33
|
+
`to_h`. It is what reaches a model nested inside a structure Rails renders
|
|
34
|
+
(`render json: { user: bc.me }`); a bare `render json: model` calls `to_json` directly
|
|
35
|
+
and never reaches it. Two things to know, neither of which a Rails habit expects:
|
|
36
|
+
- **It returns the same `Hash` as `to_h`, not a copy** — read-only by the same
|
|
37
|
+
convention, where every ActiveSupport `as_json` builds a fresh one. So
|
|
38
|
+
`model.as_json.merge!(extra)` rewrites the model's wire record, and for a wrapped
|
|
39
|
+
child its parent's too. Use `model.to_h.dup` (shallow) if you need to touch it.
|
|
40
|
+
- **Options are accepted and ignored, `only:` and `except:` included** — a model is a
|
|
41
|
+
read of one record, not a presenter. `as_json(except: ["ingest_url"])` returns the
|
|
42
|
+
whole record, silently, and it *disagrees* with `to_json`: with ActiveSupport loaded
|
|
43
|
+
`model.to_json(except: [...])` does redact, because the option reaches `Hash#to_json`.
|
|
44
|
+
Do not route redaction through either — build the subset with
|
|
45
|
+
`model.to_h.except("ingest_url")`.
|
|
46
|
+
|
|
47
|
+
`to_json` forwards its argument to `Hash#to_json` rather than swallowing it, so
|
|
48
|
+
`JSON.pretty_generate` pretty-prints; what an unknown option does is the host app's
|
|
49
|
+
`json` version's business (3.x raises, the 2.x older Rubies ship ignores it).
|
|
50
|
+
|
|
51
|
+
`to_s` is untouched, so interpolating a model (`"#{model}"`, `puts model`) still shows
|
|
52
|
+
the default object form. `inspect` names the model and its fields; `to_json` / `to_h`
|
|
53
|
+
give the record.
|
|
54
|
+
|
|
55
|
+
Minor rather than patch: the `to_json` output is a fix, but `as_json` is new public
|
|
56
|
+
surface and 0.9.0 documented the old behavior as expected — semver takes the higher.
|
|
57
|
+
|
|
7
58
|
## [0.9.0] - 2026-09-30
|
|
8
59
|
|
|
9
60
|
### Changed
|
|
@@ -46,6 +97,9 @@ enrichments come with them — but three things that worked on a `Hash` no longe
|
|
|
46
97
|
field names — because `ApiObject` defines no `to_json`. That is long-standing for
|
|
47
98
|
every other model in the SDK and now reaches this path too. Serialize
|
|
48
99
|
`item.content.to_h` instead.
|
|
100
|
+
> ⚠️ **Superseded in [0.10.0](#0100---2026-09-30)**, which gives `ApiObject` a
|
|
101
|
+
> `to_json`. `item.content.to_json` emits the record; the `to_h` workaround above is
|
|
102
|
+
> no longer needed. Left as written for the historical record.
|
|
49
103
|
- **`inspect` and `==` change, both for the better.** `inspect` names the model and its
|
|
50
104
|
wire fields (`#<BaseCradle::MessageContent body, uuid>`) rather than printing a `Hash`;
|
|
51
105
|
and equality now *holds* between an item's content and the same record fetched
|
|
@@ -342,6 +396,7 @@ the Python SDK's behavior in idiomatic Ruby. Zero runtime dependencies.
|
|
|
342
396
|
- **Quality bars** — a README-as-tested-doc harness (every example runs against a mocked
|
|
343
397
|
API) and a spec drift-guard (CI fails if the live API grows beyond the SDK).
|
|
344
398
|
|
|
399
|
+
[0.10.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.10.0
|
|
345
400
|
[0.9.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.9.0
|
|
346
401
|
[0.8.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.8.0
|
|
347
402
|
[0.7.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.7.0
|
data/README.md
CHANGED
|
@@ -324,6 +324,41 @@ timeline = bc.timelines.create(name: "Incident response")
|
|
|
324
324
|
timeline.add_participant(nova)
|
|
325
325
|
```
|
|
326
326
|
|
|
327
|
+
## Reading models, and serializing them
|
|
328
|
+
|
|
329
|
+
Every model is a read-only, wire-exact view of one API JSON object. Field readers mirror
|
|
330
|
+
the wire names; `model[key]` is the raw escape hatch for a field newer than your SDK
|
|
331
|
+
release; `model.to_h` is the wire `Hash` itself.
|
|
332
|
+
|
|
333
|
+
Models serialize as the record they stand for — what `to_h` holds is what goes out, so
|
|
334
|
+
logging or re-emitting one gives you the wire record rather than an object address:
|
|
335
|
+
|
|
336
|
+
```ruby
|
|
337
|
+
require "basecradle"
|
|
338
|
+
require "json"
|
|
339
|
+
|
|
340
|
+
bc = BaseCradle::Client.new
|
|
341
|
+
message = bc.messages.first
|
|
342
|
+
|
|
343
|
+
puts message.content.to_json # {"uuid":"019e...","body":"Hello from a peer."}
|
|
344
|
+
puts JSON.generate([message.content]) # nested in an array
|
|
345
|
+
puts({ latest: message.content }.to_json) # nested in a hash
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
`as_json` is ActiveSupport's hook and returns the same wire `Hash`, so a model nested in
|
|
349
|
+
something Rails renders comes out right too. Three caveats worth knowing:
|
|
350
|
+
|
|
351
|
+
- **`as_json` returns the same `Hash` as `to_h`, not a copy** — read-only by the same
|
|
352
|
+
convention. Mutating it rewrites the model's record. Use `model.to_h.dup` (shallow).
|
|
353
|
+
- **Options are accepted and ignored, `only:` and `except:` included.** A model is a read
|
|
354
|
+
of one record, not a presenter, so `as_json(except: ["ingest_url"])` returns the whole
|
|
355
|
+
record, silently — and it *disagrees* with `to_json`, which under ActiveSupport does
|
|
356
|
+
redact, because the option reaches `Hash#to_json`. Do not route redaction through
|
|
357
|
+
either: build the subset with `model.to_h.except("ingest_url")`.
|
|
358
|
+
- **`to_json` forwards its argument** to `Hash#to_json`, so `JSON.pretty_generate`
|
|
359
|
+
pretty-prints. What an unknown option does is your `json` version's business — 3.x
|
|
360
|
+
raises, the 2.x older Rubies ship ignores it.
|
|
361
|
+
|
|
327
362
|
## Development
|
|
328
363
|
|
|
329
364
|
```bash
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
3
5
|
require_relative "errors"
|
|
4
6
|
|
|
5
7
|
module BaseCradle
|
|
@@ -61,6 +63,46 @@ module BaseCradle
|
|
|
61
63
|
@data
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
# The wire record, for a serializer — ActiveSupport's hook, and what reaches a model
|
|
67
|
+
# nested inside a structure Rails renders (<tt>render json: { user: bc.me }</tt>).
|
|
68
|
+
# A bare <tt>render json: model</tt> calls +to_json+ instead and never comes here.
|
|
69
|
+
#
|
|
70
|
+
# Returns +to_h+: the wire keys, nothing renamed, nothing dropped, and a field newer
|
|
71
|
+
# than this release carried like any other. (It is the record *as this object holds
|
|
72
|
+
# it* — for a Timeline fetched whole, that is the API's two-key envelope merged into
|
|
73
|
+
# one object, so it carries +items+ alongside the timeline's own fields.)
|
|
74
|
+
#
|
|
75
|
+
# Two traps, because neither is what a Rails habit expects:
|
|
76
|
+
#
|
|
77
|
+
# - **It is the same Hash +to_h+ returns, not a copy** — read-only by the same
|
|
78
|
+
# convention, where every ActiveSupport +as_json+ builds a fresh one. So
|
|
79
|
+
# <tt>model.as_json.merge!(extra)</tt> rewrites the model's wire record, and for a
|
|
80
|
+
# wrapped child its parent's too (a child wraps the parent's own nested Hash).
|
|
81
|
+
# +to_h.dup+ if you need to touch it; note that is shallow.
|
|
82
|
+
# - **Options are accepted and ignored, +only:+ and +except:+ included** — a model is
|
|
83
|
+
# a read of one record, not a presenter. +as_json(except: ["ingest_url"])+ returns
|
|
84
|
+
# the whole record, *silently*. Worse, it disagrees with +to_json+: with
|
|
85
|
+
# ActiveSupport loaded <tt>model.to_json(except: [...])</tt> *does* redact, because
|
|
86
|
+
# the option reaches +Hash#to_json+. Do not rely on either — build the subset
|
|
87
|
+
# yourself with <tt>model.to_h.except("ingest_url")</tt>.
|
|
88
|
+
def as_json(*)
|
|
89
|
+
to_h
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# The wire record as JSON. Without this a model fell through to Ruby's default and
|
|
93
|
+
# emitted a heap address — silently, with no field names and a different value every
|
|
94
|
+
# run. +JSON.generate+ and +Array#to_json+ / +Hash#to_json+ call this, so a model
|
|
95
|
+
# nested in either serializes correctly too.
|
|
96
|
+
#
|
|
97
|
+
# The argument is forwarded to +Hash#to_json+ rather than swallowed, so
|
|
98
|
+
# +JSON.pretty_generate+ pretty-prints — and what an unknown option does is the host
|
|
99
|
+
# app's json version's business, not this SDK's: json 3.x raises on an unknown
|
|
100
|
+
# keyword, the json 2.x that older Rubies ship ignores it, and ActiveSupport acts on
|
|
101
|
+
# +only:+ / +except:+. See +as_json+ above: do not route redaction through either.
|
|
102
|
+
def to_json(*args)
|
|
103
|
+
to_h.to_json(*args)
|
|
104
|
+
end
|
|
105
|
+
|
|
64
106
|
def ==(other)
|
|
65
107
|
other.instance_of?(self.class) && other.to_h == @data
|
|
66
108
|
end
|
data/lib/basecradle/version.rb
CHANGED