ruact 0.0.11 → 0.0.12
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 +110 -48
- data/CONTRIBUTING.md +233 -0
- data/README.md +14 -4
- data/RELEASING.md +125 -139
- data/lib/ruact/packaging.rb +68 -0
- data/lib/ruact/version.rb +1 -1
- data/vendor/javascript/vite-plugin-ruact/flight-client.test.mjs +321 -0
- data/vendor/javascript/vite-plugin-ruact/package-lock.json +11 -0
- data/vendor/javascript/vite-plugin-ruact/package.json +1 -0
- data/vendor/javascript/vite-plugin-ruact/runtime/flight-client.js +14 -3
- metadata +6 -107
- data/.codecov.yml +0 -31
- data/.github/workflows/ci.yml +0 -284
- data/.github/workflows/server-functions-bench.yml +0 -54
- data/.rubocop.yml +0 -107
- data/.rubocop_todo.yml +0 -63
- data/Rakefile +0 -10
- data/bench/server_functions_dispatch_bench.rb +0 -276
- data/bench/server_functions_dispatch_bench.results.md +0 -150
- data/docs/internal/README.md +0 -9
- data/docs/internal/decisions/server-functions-api.md +0 -2236
- data/spec/benchmarks/baseline.json +0 -12
- data/spec/benchmarks/render_pipeline_benchmark_spec.rb +0 -109
- data/spec/fixtures/flight/README.md +0 -136
- data/spec/fixtures/flight/array.txt +0 -1
- data/spec/fixtures/flight/as_json_object.txt +0 -2
- data/spec/fixtures/flight/bigint.txt +0 -1
- data/spec/fixtures/flight/boolean_false.txt +0 -1
- data/spec/fixtures/flight/boolean_true.txt +0 -1
- data/spec/fixtures/flight/client_component_with_props.txt +0 -2
- data/spec/fixtures/flight/client_reference.txt +0 -2
- data/spec/fixtures/flight/hash.txt +0 -1
- data/spec/fixtures/flight/infinity.txt +0 -1
- data/spec/fixtures/flight/nan.txt +0 -1
- data/spec/fixtures/flight/negative_infinity.txt +0 -1
- data/spec/fixtures/flight/nil.txt +0 -1
- data/spec/fixtures/flight/number_float.txt +0 -1
- data/spec/fixtures/flight/number_integer.txt +0 -1
- data/spec/fixtures/flight/react_element_no_props.txt +0 -1
- data/spec/fixtures/flight/redirect_row.txt +0 -1
- data/spec/fixtures/flight/serializable_object.txt +0 -2
- data/spec/fixtures/flight/string_basic.txt +0 -1
- data/spec/fixtures/flight/string_dollar_escape.txt +0 -1
- data/spec/fixtures/flight/undefined.txt +0 -1
- data/spec/fixtures/readme/children-error.html.erb +0 -3
- data/spec/fixtures/readme/children-error.txt +0 -1
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/demo/show.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/errors_demo/new.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/exploding_layout_demo/show.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/ghost_layout_demo/show.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/layout_demo/show.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/rootless_layout_demo/show.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/controller_request_spec_support/unwired_layout_demo/show.html.erb +0 -3
- data/spec/fixtures/story_7_9_views/layouts/bare_host.html.erb +0 -16
- data/spec/fixtures/story_7_9_views/layouts/exploding_host.html.erb +0 -24
- data/spec/fixtures/story_7_9_views/layouts/rootless_host.html.erb +0 -15
- data/spec/fixtures/story_7_9_views/layouts/ruact_host.html.erb +0 -17
- data/spec/readme_demo_message_spec.rb +0 -67
- data/spec/readme_spec.rb +0 -282
- data/spec/ruact/client_manifest_spec.rb +0 -270
- data/spec/ruact/component_contract_spec.rb +0 -119
- data/spec/ruact/configuration_spec.rb +0 -518
- data/spec/ruact/controller_request_spec.rb +0 -671
- data/spec/ruact/controller_spec.rb +0 -343
- data/spec/ruact/doctor_spec.rb +0 -769
- data/spec/ruact/erb_preprocessor_hook_spec.rb +0 -55
- data/spec/ruact/erb_preprocessor_spec.rb +0 -361
- data/spec/ruact/errors_spec.rb +0 -93
- data/spec/ruact/flight/renderer_spec.rb +0 -133
- data/spec/ruact/flight/serializer_spec.rb +0 -494
- data/spec/ruact/html_converter_spec.rb +0 -375
- data/spec/ruact/install_generator_spec.rb +0 -1549
- data/spec/ruact/layout_source_spec.rb +0 -108
- data/spec/ruact/manifest_resolver_spec.rb +0 -174
- data/spec/ruact/query_request_spec.rb +0 -706
- data/spec/ruact/query_spec.rb +0 -105
- data/spec/ruact/railtie_spec.rb +0 -155
- data/spec/ruact/render_context_spec.rb +0 -58
- data/spec/ruact/render_pipeline_concurrency_spec.rb +0 -78
- data/spec/ruact/render_pipeline_spec.rb +0 -928
- data/spec/ruact/scaffold_generator_spec.rb +0 -1849
- data/spec/ruact/serializable_spec.rb +0 -179
- data/spec/ruact/server_bucket_request_spec.rb +0 -785
- data/spec/ruact/server_function_name_spec.rb +0 -53
- data/spec/ruact/server_functions/backtrace_cleaner_spec.rb +0 -63
- data/spec/ruact/server_functions/bucket_two_payload_spec.rb +0 -200
- data/spec/ruact/server_functions/codegen_spec.rb +0 -397
- data/spec/ruact/server_functions/error_payload_spec.rb +0 -222
- data/spec/ruact/server_functions/error_suggestion_spec.rb +0 -79
- data/spec/ruact/server_functions/introspection_spec.rb +0 -135
- data/spec/ruact/server_functions/name_bridge_spec.rb +0 -212
- data/spec/ruact/server_functions/query_context_spec.rb +0 -72
- data/spec/ruact/server_functions/query_source_spec.rb +0 -193
- data/spec/ruact/server_functions/railtie_integration_spec.rb +0 -215
- data/spec/ruact/server_functions/rake_spec.rb +0 -86
- data/spec/ruact/server_functions/route_source_spec.rb +0 -202
- data/spec/ruact/server_functions/snapshot_spec.rb +0 -96
- data/spec/ruact/server_functions/snapshot_writer_spec.rb +0 -71
- data/spec/ruact/server_rescue_request_spec.rb +0 -416
- data/spec/ruact/server_spec.rb +0 -179
- data/spec/ruact/server_upload_request_spec.rb +0 -311
- data/spec/ruact/signed_references_spec.rb +0 -164
- data/spec/ruact/string_distance_spec.rb +0 -38
- data/spec/ruact/tasks_json_introspection_spec.rb +0 -141
- data/spec/ruact/testing/have_ruact_component_spec.rb +0 -170
- data/spec/ruact/testing/no_production_load_spec.rb +0 -41
- data/spec/ruact/validation_errors_spec.rb +0 -116
- data/spec/ruact/view_helper_spec.rb +0 -131
- data/spec/spec_helper.rb +0 -77
- data/spec/support/fixtures/pixel.png +0 -0
- data/spec/support/flight_wire_parser.rb +0 -21
- data/spec/support/flight_wire_parser_spec.rb +0 -93
- data/spec/support/matchers/flight_fixture_matcher.rb +0 -130
- data/spec/support/matchers/flight_fixture_matcher_spec.rb +0 -250
- data/spec/support/rails_stub.rb +0 -115
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
require "ruact/server"
|
|
5
|
-
|
|
6
|
-
# Story 9.3 AC4 — the `ruact_function_name` rename-override macro. Tested via
|
|
7
|
-
# the concern's ClassMethods in isolation (no ActionController boot needed) so
|
|
8
|
-
# the validation + storage contract is pinned independently of the request
|
|
9
|
-
# cycle. RouteSource consumes the resulting `__ruact_function_name_overrides`.
|
|
10
|
-
RSpec.describe "Ruact::Server.ruact_function_name", :story_9_3 do
|
|
11
|
-
subject(:host) { Class.new { extend Ruact::Server::ClassMethods } }
|
|
12
|
-
|
|
13
|
-
it "stores the override keyed by action name (string)" do
|
|
14
|
-
host.ruact_function_name(:publish_all, as: "publishEverything")
|
|
15
|
-
expect(host.__ruact_function_name_overrides).to eq("publish_all" => "publishEverything")
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it "accepts a symbol target and stringifies it" do
|
|
19
|
-
host.ruact_function_name(:publish_all, as: :publishEverything)
|
|
20
|
-
expect(host.__ruact_function_name_overrides.fetch("publish_all")).to eq("publishEverything")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it "starts empty and is per-class (not shared)" do
|
|
24
|
-
other = Class.new { extend Ruact::Server::ClassMethods }
|
|
25
|
-
host.ruact_function_name(:create, as: "makePost")
|
|
26
|
-
expect(host.__ruact_function_name_overrides).to eq("create" => "makePost")
|
|
27
|
-
expect(other.__ruact_function_name_overrides).to eq({})
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
it "rejects an invalid JS identifier at class-load time" do
|
|
31
|
-
expect do
|
|
32
|
-
host.ruact_function_name(:create, as: "2bad name")
|
|
33
|
-
end.to raise_error(Ruact::ConfigurationError, /not a valid JS identifier/)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it "rejects a reserved JS word" do
|
|
37
|
-
expect do
|
|
38
|
-
host.ruact_function_name(:create, as: "class")
|
|
39
|
-
end.to raise_error(Ruact::ConfigurationError, /reserved/)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
it "rejects a runtime-bound name (revalidate)" do
|
|
43
|
-
expect do
|
|
44
|
-
host.ruact_function_name(:create, as: "revalidate")
|
|
45
|
-
end.to raise_error(Ruact::ConfigurationError, /reserved|ruact runtime/)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "rejects the v2 runtime accessor name (_makeServerFunction)" do
|
|
49
|
-
expect do
|
|
50
|
-
host.ruact_function_name(:create, as: "_makeServerFunction")
|
|
51
|
-
end.to raise_error(Ruact::ConfigurationError, /reserved|ruact runtime/)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "spec_helper"
|
|
4
|
-
require "ruact/server_functions/backtrace_cleaner"
|
|
5
|
-
|
|
6
|
-
module Ruact
|
|
7
|
-
module ServerFunctions
|
|
8
|
-
RSpec.describe BacktraceCleaner, :story_8_4 do
|
|
9
|
-
describe "Story 8.4 — .split classifies frames by Ruact.gem_path prefix" do
|
|
10
|
-
let(:gem_root) { "/tmp/ruact-test-gem" }
|
|
11
|
-
|
|
12
|
-
before { allow(Ruact).to receive(:gem_path).and_return(gem_root) }
|
|
13
|
-
|
|
14
|
-
it "puts non-gem frames into :app, preserving order" do
|
|
15
|
-
frames = [
|
|
16
|
-
"/Users/dev/host/app/controllers/posts_controller.rb:12:in `create'",
|
|
17
|
-
"/Users/dev/host/app/models/post.rb:7:in `save!'"
|
|
18
|
-
]
|
|
19
|
-
expect(described_class.split(frames)).to eq(
|
|
20
|
-
app: frames,
|
|
21
|
-
gem: []
|
|
22
|
-
)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "puts frames under Ruact.gem_path into :gem, preserving order" do
|
|
26
|
-
frames = [
|
|
27
|
-
"#{gem_root}/lib/ruact/server_functions/endpoint_controller.rb:111:in `dispatch'",
|
|
28
|
-
"#{gem_root}/lib/ruact/server_functions/standalone_dispatcher.rb:42:in `apply'"
|
|
29
|
-
]
|
|
30
|
-
expect(described_class.split(frames)).to eq(
|
|
31
|
-
app: [],
|
|
32
|
-
gem: frames
|
|
33
|
-
)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
it "interleaves app+gem frames into separate buckets while preserving relative order in each bucket" do
|
|
37
|
-
frames = [
|
|
38
|
-
"/Users/dev/host/app/controllers/posts_controller.rb:12:in `create'",
|
|
39
|
-
"#{gem_root}/lib/ruact/server_functions/endpoint_controller.rb:111:in `dispatch'",
|
|
40
|
-
"/Users/dev/host/app/models/post.rb:7:in `save!'",
|
|
41
|
-
"#{gem_root}/lib/ruact/server_functions/standalone_dispatcher.rb:42:in `apply'"
|
|
42
|
-
]
|
|
43
|
-
result = described_class.split(frames)
|
|
44
|
-
expect(result[:app]).to eq([frames[0], frames[2]])
|
|
45
|
-
expect(result[:gem]).to eq([frames[1], frames[3]])
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
it "returns { app: [], gem: [] } for nil input" do
|
|
49
|
-
expect(described_class.split(nil)).to eq(app: [], gem: [])
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
it "returns { app: [], gem: [] } for an empty array" do
|
|
53
|
-
expect(described_class.split([])).to eq(app: [], gem: [])
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it "classifies a frame whose path is exactly Ruact.gem_path as GEM (edge case)" do
|
|
57
|
-
frames = ["#{gem_root}:0:in `<top>'"]
|
|
58
|
-
expect(described_class.split(frames)).to eq(app: [], gem: frames)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Story 9.2 — unit spec for the pure Bucket-2 response serializer. Pins the
|
|
4
|
-
# prop-exposure policy mirrored from Ruact::Flight::Serializer#serialize_unknown
|
|
5
|
-
# (Serializable → ruact_props only; strict → raise; vetted as_json fallback),
|
|
6
|
-
# producing a plain JSON-ready Hash (no Flight wire encoding). Pure function —
|
|
7
|
-
# no Rails / request / Ruact.config reads (NFR26 / AC8).
|
|
8
|
-
|
|
9
|
-
require "spec_helper"
|
|
10
|
-
require "ruact/server_functions/bucket_two_payload"
|
|
11
|
-
|
|
12
|
-
# Fixtures live OUTSIDE the example group (no leaky constants in the block).
|
|
13
|
-
module B2Fixtures
|
|
14
|
-
# A Serializable model exposing only some attributes.
|
|
15
|
-
class Post
|
|
16
|
-
include Ruact::Serializable
|
|
17
|
-
|
|
18
|
-
attr_reader :id, :title, :secret
|
|
19
|
-
|
|
20
|
-
def initialize(id:, title:, secret:)
|
|
21
|
-
@id = id
|
|
22
|
-
@title = title
|
|
23
|
-
@secret = secret
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
ruact_props :id, :title
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Serializable whose prop is itself a Serializable (nested).
|
|
30
|
-
class AuthoredPost
|
|
31
|
-
include Ruact::Serializable
|
|
32
|
-
|
|
33
|
-
attr_reader :title, :author
|
|
34
|
-
|
|
35
|
-
def initialize(title:, author:)
|
|
36
|
-
@title = title
|
|
37
|
-
@author = author
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
ruact_props :title, :author
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
class Author
|
|
44
|
-
include Ruact::Serializable
|
|
45
|
-
|
|
46
|
-
attr_reader :name, :password_digest
|
|
47
|
-
|
|
48
|
-
def initialize(name:, password_digest:)
|
|
49
|
-
@name = name
|
|
50
|
-
@password_digest = password_digest
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
ruact_props :name
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# A plain object with as_json (AR-like).
|
|
57
|
-
class PlainRecord
|
|
58
|
-
def as_json(_opts = nil)
|
|
59
|
-
{ "id" => 7, "leaked" => "everything" }
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
class SelfReturningAsJson
|
|
64
|
-
def as_json(_opts = nil)
|
|
65
|
-
self
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
class RaisingAsJson
|
|
70
|
-
def as_json(_opts = nil)
|
|
71
|
-
raise "boom in as_json"
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
RSpec.describe Ruact::ServerFunctions::BucketTwoPayload, :story_9_2 do
|
|
77
|
-
describe ".build (AC2 — keyed by ivar name, all exposed ivars)" do
|
|
78
|
-
it "keys the result by the assigns names and serializes each value" do
|
|
79
|
-
result = described_class.build(
|
|
80
|
-
{ "post" => B2Fixtures::Post.new(id: 1, title: "Hi", secret: "x"), "count" => 3 },
|
|
81
|
-
strict: true
|
|
82
|
-
)
|
|
83
|
-
expect(result).to eq("post" => { "id" => 1, "title" => "Hi" }, "count" => 3)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
it "does NOT unwrap a single ivar — it stays keyed (no magic unwrap)" do
|
|
87
|
-
result = described_class.build({ "post" => B2Fixtures::Post.new(id: 1, title: "Hi", secret: "x") }, strict: true)
|
|
88
|
-
expect(result).to eq("post" => { "id" => 1, "title" => "Hi" })
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
describe "Serializable policy" do
|
|
93
|
-
it "exposes ONLY ruact_props, never undeclared attributes (no secret leak)" do
|
|
94
|
-
result = described_class.build({ "post" => B2Fixtures::Post.new(id: 1, title: "Hi", secret: "nope") },
|
|
95
|
-
strict: true)
|
|
96
|
-
expect(result.fetch("post")).to eq("id" => 1, "title" => "Hi")
|
|
97
|
-
expect(result.fetch("post")).not_to have_key("secret")
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it "recurses into a Serializable-valued prop (nested), applying ruact_props at each level" do
|
|
101
|
-
author = B2Fixtures::Author.new(name: "Ada", password_digest: "HASH")
|
|
102
|
-
post = B2Fixtures::AuthoredPost.new(title: "T", author: author)
|
|
103
|
-
result = described_class.build({ "post" => post }, strict: true)
|
|
104
|
-
expect(result.fetch("post")).to eq("title" => "T", "author" => { "name" => "Ada" })
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it "serializes an Array of Serializables element-wise" do
|
|
108
|
-
posts = [B2Fixtures::Post.new(id: 1, title: "A", secret: "s"),
|
|
109
|
-
B2Fixtures::Post.new(id: 2, title: "B", secret: "s")]
|
|
110
|
-
result = described_class.build({ "posts" => posts }, strict: true)
|
|
111
|
-
expect(result.fetch("posts")).to eq([{ "id" => 1, "title" => "A" }, { "id" => 2, "title" => "B" }])
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
describe "primitive pass-through (NOT subject to strict policy)" do
|
|
116
|
-
it "passes scalars through untouched even under strict" do
|
|
117
|
-
result = described_class.build(
|
|
118
|
-
{ "i" => 5, "f" => 1.5, "s" => "x", "t" => true, "n" => nil, "sym" => :ok },
|
|
119
|
-
strict: true
|
|
120
|
-
)
|
|
121
|
-
expect(result).to eq("i" => 5, "f" => 1.5, "s" => "x", "t" => true, "n" => nil, "sym" => :ok)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it "passes Time through untouched (Rails render json: handles ISO formatting)" do
|
|
125
|
-
time = Time.utc(2026, 1, 2, 3, 4, 5)
|
|
126
|
-
result = described_class.build({ "at" => time }, strict: true)
|
|
127
|
-
expect(result.fetch("at")).to equal(time)
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
it "stringifies Hash keys and recurses values" do
|
|
131
|
-
assigns = { "meta" => { a: 1, b: B2Fixtures::Post.new(id: 9, title: "N", secret: "s") } }
|
|
132
|
-
result = described_class.build(assigns, strict: true)
|
|
133
|
-
expect(result.fetch("meta")).to eq("a" => 1, "b" => { "id" => 9, "title" => "N" })
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
describe "strict_serialization policy (AC5)" do
|
|
138
|
-
it "raises Ruact::SerializationError for a non-Serializable object under strict" do
|
|
139
|
-
expect { described_class.build({ "rec" => B2Fixtures::PlainRecord.new }, strict: true) }
|
|
140
|
-
.to raise_error(Ruact::SerializationError, /Cannot serialize B2Fixtures::PlainRecord/)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
it "falls back to as_json when strict is false" do
|
|
144
|
-
result = described_class.build({ "rec" => B2Fixtures::PlainRecord.new }, strict: false)
|
|
145
|
-
expect(result.fetch("rec")).to eq("id" => 7, "leaked" => "everything")
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "raises when as_json returns self (infinite-recursion guard), regardless of strict" do
|
|
149
|
-
expect { described_class.build({ "x" => B2Fixtures::SelfReturningAsJson.new }, strict: false) }
|
|
150
|
-
.to raise_error(Ruact::SerializationError, /as_json returned self/)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
it "wraps an exception raised inside as_json as Ruact::SerializationError" do
|
|
154
|
-
expect { described_class.build({ "x" => B2Fixtures::RaisingAsJson.new }, strict: false) }
|
|
155
|
-
.to raise_error(Ruact::SerializationError, /as_json raised RuntimeError: boom in as_json/)
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
describe "Story 9.4 — .serialize_value (single value, same policy as .build — D6)", :story_9_4 do
|
|
160
|
-
it "serializes a Serializable through ruact_props only" do
|
|
161
|
-
post = B2Fixtures::Post.new(id: 1, title: "Hi", secret: "nope")
|
|
162
|
-
expect(described_class.serialize_value(post, strict: true)).to eq("id" => 1, "title" => "Hi")
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
it "recurses into Arrays of Serializables" do
|
|
166
|
-
posts = [B2Fixtures::Post.new(id: 1, title: "A", secret: "x")]
|
|
167
|
-
expect(described_class.serialize_value(posts, strict: true)).to eq([{ "id" => 1, "title" => "A" }])
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
it "recurses into Hashes, stringifying keys" do
|
|
171
|
-
value = { total: 2, post: B2Fixtures::Post.new(id: 1, title: "A", secret: "x") }
|
|
172
|
-
expect(described_class.serialize_value(value, strict: true))
|
|
173
|
-
.to eq("total" => 2, "post" => { "id" => 1, "title" => "A" })
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it "passes primitives through untouched" do
|
|
177
|
-
expect(described_class.serialize_value(42, strict: true)).to eq(42)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
it "passes nil through (the dispatch controller renders it as JSON null — D6)" do
|
|
181
|
-
expect(described_class.serialize_value(nil, strict: true)).to be_nil
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
it "raises Ruact::SerializationError for a non-Serializable under strict" do
|
|
185
|
-
expect { described_class.serialize_value(B2Fixtures::PlainRecord.new, strict: true) }
|
|
186
|
-
.to raise_error(Ruact::SerializationError, /Cannot serialize B2Fixtures::PlainRecord/)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
it "falls back to as_json when strict is false" do
|
|
190
|
-
expect(described_class.serialize_value(B2Fixtures::PlainRecord.new, strict: false))
|
|
191
|
-
.to eq("id" => 7, "leaked" => "everything")
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
it "is the same policy .build applies per ivar (extraction, not a fork)" do
|
|
195
|
-
post = B2Fixtures::Post.new(id: 3, title: "Same", secret: "x")
|
|
196
|
-
expect(described_class.build({ "post" => post }, strict: true))
|
|
197
|
-
.to eq("post" => described_class.serialize_value(post, strict: true))
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|