Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ SCI is used in [babashka](https://github.com/babashka/babashka),
- Fix [#997](https://github.com/babashka/sci/issues/997): Var is mistaken for local when used under the same name in a `let` body
- Fix [#1001](https://github.com/babashka/sci/issues/1001): JS interop with reserved js keyword fails (regression of [#987](https://github.com/babashka/sci/issues/987))
- `sci.impl.Reflector` was rewritten into Clojure
- Fix [babashka/babashka#1886](https://github.com/babashka/babashka/issues/1886): Return a map when dissociating a
record basis field.

## 0.10.49 (2025-08-22)

Expand Down
46 changes: 30 additions & 16 deletions src/sci/impl/records.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
#?(:clj
(deftype SciRecord [rec-name
type
var ext-map
basis-fields
var
ext-map
^:unsynchronized-mutable my_hash
^:unsynchronized-mutable my_hasheq]
clojure.lang.IRecord ;; marker interface
Expand Down Expand Up @@ -65,7 +67,7 @@
(meta ext-map))
(withMeta [_ m]
(SciRecord.
rec-name type var (with-meta ext-map m) 0 0))
rec-name type var basis-fields (with-meta ext-map m) 0 0))

clojure.lang.ILookup
(valAt [_this k]
Expand Down Expand Up @@ -94,9 +96,11 @@
(iterator [_this]
(clojure.lang.RT/iter ext-map))
(assoc [_this k v]
(SciRecord. rec-name type var (assoc ext-map k v) 0 0))
(SciRecord. rec-name type basis-fields var (assoc ext-map k v) 0 0))
(without [_this k]
(SciRecord. rec-name type var (dissoc ext-map k) 0 0))
(if (contains? basis-fields k)
(dissoc ext-map k)
(SciRecord. rec-name type basis-fields var (dissoc ext-map k) 0 0)))

java.util.Map
java.io.Serializable
Expand Down Expand Up @@ -144,13 +148,14 @@
#?(:cljs
(deftype SciRecord [rec-name
type
basis-fields
var ext-map
^:mutable my_hash]
IRecord ;; marker interface

ICloneable
(-clone [_]
(new SciRecord rec-name type var ext-map my_hash))
(new SciRecord rec-name type basis-fields var ext-map my_hash))

IHash
(-hash [_]
Expand All @@ -177,7 +182,7 @@
IWithMeta
(-with-meta [_ m]
(new SciRecord
rec-name type var (with-meta ext-map m) my_hash))
rec-name type basis-fields var (with-meta ext-map m) my_hash))

ILookup
(-lookup [_ k]
Expand All @@ -201,11 +206,13 @@
(-contains-key? [_ k]
(-contains-key? ext-map k))
(-assoc [_ k v]
(new SciRecord rec-name type var (assoc ext-map k v) nil))
(new SciRecord rec-name type basis-fields var (assoc ext-map k v) nil))

IMap
(-dissoc [_ k]
(new SciRecord rec-name type var (dissoc ext-map k) nil))
(if (contains? basis-fields k)
(dissoc ext-map k)
(new SciRecord rec-name type basis-fields var (dissoc ext-map k) nil)))

ISeqable
(-seq [_]
Expand Down Expand Up @@ -242,10 +249,10 @@
(defmethod print-method SciRecord [v w]
(-sci-print-method v w)))

#?(:clj (defn ->record-impl [rec-name type var m]
(SciRecord. rec-name type var m 0 0))
:cljs (defn ->record-impl [rec-name type var m]
(SciRecord. rec-name type var m nil)))
#?(:clj (defn ->record-impl [rec-name type basis-fields var m]
(SciRecord. rec-name type basis-fields var m 0 0))
:cljs (defn ->record-impl [rec-name type basis-fields var m]
(SciRecord. rec-name type basis-fields var m nil)))

(defn defrecord [[_fname & _ :as form] _ record-name fields & raw-protocol-impls]
(let [ctx (store/get-ctx)]
Expand Down Expand Up @@ -332,15 +339,22 @@
([~@arg-syms]
(~constructor-fn-sym ~@arg-syms nil nil))
([~@arg-syms meta# ext#]
(sci.impl.records/->record-impl '~rec-type ~rec-type (var ~record-name)
(sci.impl.records/->record-impl '~rec-type
~rec-type
(set (map keyword ~keys))
(var ~record-name)
(cond-> (zipmap ~keys ~arg-syms)
ext# (merge ext#)
meta# (with-meta meta#)))))
ext# (merge ext#)
meta# (with-meta meta#)))))
(defn ~factory-fn-sym
([~@arg-syms]
(~constructor-fn-sym ~@arg-syms nil nil)))
(defn ~map-factory-sym [m#]
(sci.impl.records/->record-impl '~rec-type ~rec-type (var ~record-name) (merge '~nil-map m#)))
(sci.impl.records/->record-impl '~rec-type
~rec-type
(set (map keyword ~keys))
(var ~record-name)
(merge '~nil-map m#)))
~@protocol-impls
~record-name)))))

Expand Down
31 changes: 27 additions & 4 deletions test/sci/records_test.cljc
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
(ns sci.records-test
(:require
[clojure.string :as str]
[clojure.test :refer [deftest is testing]]
[sci.core :as sci]
[sci.test-utils :as tu]))
[clojure.string :as str]
[clojure.test :refer [are deftest is testing]]
[sci.core :as sci]
[sci.test-utils :as tu]))

(deftest protocol-test
(let [prog "
Expand Down Expand Up @@ -244,3 +244,26 @@
(deftest map-constructor-nil-test
(is (true? (sci/eval-string "(defrecord Foo [a]) (and (contains? (map->Foo {}) :a)
(nil? (:a (map->Foo {}))))"))))

(deftest dissoc-test
(testing "dissoc returns a map if any record basis field is removed"
(are [expected prog] (= expected (tu/eval* prog {}))
"{:a 1, :b 2}" "(defrecord Foo [a b c])
(def r (->Foo 1 2 3))
(print-str (dissoc r :c))"
"{:a 1, :b 2}" "(defrecord Foo [a b c])
(def r (->Foo 1 2 3))
(print-str (dissoc r :c :d))"
"{}" "(defrecord Foo [a b c])
(def r (->Foo 1 2 3))
(print-str (dissoc r :a :b :c))"))
(testing "dissoc keeps record type if no record basis field is removed"
(are [expected prog] (= expected (tu/eval* prog {}))
"#user.Foo{:a 1, :b 2, :c 3}" "(defrecord Foo [a b c])
(def r (->Foo 1 2 3))
(print-str (dissoc r :d))"
"#user.Foo{:a 1, :b 2, :c 3, :e 5}" "(defrecord Foo [a b c])
(def r (->Foo 1 2 3))
(-> r (assoc :d 4 :e 5)
(dissoc :d)
print-str)")))
Loading