Skip to content

Commit ecf58ca

Browse files
committed
feat: add PG::JSONB converter
Closes #81
1 parent b778d26 commit ecf58ca

5 files changed

Lines changed: 36 additions & 4 deletions

File tree

db_spec/pg/migration.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ CREATE TABLE posts(
3434
editor_uuid UUID REFERENCES users (uuid),
3535
tag_ids INT[],
3636
content TEXT NOT NULL,
37+
meta JSONB NOT NULL DEFAULT '{}',
3738
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
3839
updated_at TIMESTAMPTZ
3940
);

db_spec/pg/models.cr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
require "../../src/onyx-sql/converters/pg"
22
require "../../src/onyx-sql/converters/pg/uuid"
33
require "../../src/onyx-sql/converters/pg/json"
4+
require "../../src/onyx-sql/converters/pg/jsonb"
45

56
alias Model = Onyx::SQL::Model
67
alias Field = Onyx::SQL::Field
@@ -60,10 +61,15 @@ end
6061
class Post
6162
include Model
6263

64+
record Meta, meta : Hash(String, String) do
65+
include JSON::Serializable
66+
end
67+
6368
schema posts do
6469
pkey id : Int32, converter: PG::Any(Int32)
6570

6671
type content : String, not_null: true
72+
type meta : Meta, converter: PG::JSONB(Meta)
6773
type created_at : Time, default: true, not_null: true
6874
type updated_at : Time
6975

db_spec/pg/repository/query_spec.cr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,13 @@ describe "Repository(Postgres)#query" do
101101
describe "insert" do
102102
context "with complex model" do
103103
post = repo.query(Post.query
104-
.insert(author: user, tags: [tag], content: "Blah-blah")
104+
.insert(author: user, tags: [tag], content: "Blah-blah", meta: Post::Meta.new({"foo" => "bar"}))
105105
.returning(Post)
106106
).first
107107

108108
it "returns model instance" do
109109
post.should be_a(Post)
110+
post.meta!.meta.should eq({"foo" => "bar"})
110111
end
111112

112113
it "preloads direct non-enumerable references" do

src/onyx-sql/converters/pg/json.cr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ require "../pg"
22
require "../../ext/pg/result_set"
33
require "json"
44

5-
# Converts between the PostgreSQL's native `JSON` and `JSONB` types and Crystal objects with
6-
# `#to_json` and `.from_json` methods (e.g. `JSON::Serializable`).
5+
# Converts between the PostgreSQL's native `"JSON"` column type and Crystal objects with
6+
# `#to_json` and `.from_json` methods (e.g. `JSON::Serializable` or `Hash`).
77
# See `Field` to read about of how to apply converters.
88
#
99
# ```sql
@@ -33,7 +33,7 @@ module Onyx::SQL::Converters::PG::JSON(T)
3333
value.to_json
3434
end
3535

36-
def self.from_rs(rs : DB::ResultSet)
36+
def self.from_rs(rs : DB::ResultSet) : T?
3737
bytes = rs.read_raw
3838
bytes.try do |bytes|
3939
T.from_json(String.new(bytes))
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
require "../pg"
2+
require "../../ext/pg/result_set"
3+
require "json"
4+
5+
# Converts between the PostgreSQL's `"JSONB"` type and Crystal objects.
6+
# It works the same way as the `JSON` converter does, but with `"JSONB"` type.
7+
#
8+
# OPTIMIZE: Refactor to extend the `JSON` module. Currently impossible due to https://github.com/crystal-lang/crystal/issues/7167.
9+
module Onyx::SQL::Converters::PG::JSONB(T)
10+
def self.to_db(value : T) : DB::Any
11+
value.to_json
12+
end
13+
14+
def self.from_rs(rs : DB::ResultSet) : T?
15+
bytes = rs.read_raw
16+
bytes.try do |bytes|
17+
T.from_json(String.new(bytes[1, bytes.bytesize - 1]))
18+
end
19+
end
20+
21+
def self.from_rs_array(rs) : T?
22+
from_rs(rs)
23+
end
24+
end

0 commit comments

Comments
 (0)