This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-07-13
Channels
- # announcements (1)
- # babashka (29)
- # beginners (64)
- # calva (4)
- # cider (5)
- # cljs-dev (3)
- # cljsrn (2)
- # clojure (100)
- # clojure-australia (2)
- # clojure-conj (7)
- # clojure-dev (9)
- # clojure-europe (31)
- # clojure-germany (1)
- # clojure-nl (2)
- # clojure-uk (13)
- # clojured (2)
- # clojurescript (62)
- # community-development (2)
- # conjure (1)
- # cursive (21)
- # datomic (39)
- # events (2)
- # fulcro (7)
- # graalvm (24)
- # graalvm-mobile (11)
- # holy-lambda (3)
- # jobs (7)
- # lsp (15)
- # malli (26)
- # music (1)
- # nyc (2)
- # off-topic (18)
- # reagent (23)
- # reitit (5)
- # remote-jobs (1)
- # shadow-cljs (2)
- # tools-deps (26)
- # vim (6)
- # xtdb (17)
Translating this .js
const { google } = require('googleapis');
const sheets = google.sheets('v4')
to this:
(ns functions.index
(:require ;...
["googleapis" :as google]))
(def sheets (.sheets google "v4"))
or your variant, slightly longer but with :refer
not :as
(:require ["googleapis" :refer (google)])
this const { google }
in JS is destructuring, so basically const api = require("googleapis"); const sheets = api.google.sheets("v4")
from those few lines of code or from something you do later? what is the full trace?
i suspect its either something with the go macro or the sheets. Interestingly depending on how import them i don't get any warnings
Now like this i don't get any warnings:
(ns functions.index
(:require ["firebase-functions" :as fun]
[cljs.core.async :as async :refer (chan go)]
["googleapis" :refer (google)]))
(def sheets (.sheets google "v4"))
Okay, i took it from here: https://code.thheller.com/blog/shadow-cljs/2019/10/12/clojurescript-macros.html
Now i do this:
(ns functions.index
(:require ["firebase-functions" :as fun]
["googleapis" :refer (google)])
(:require-macros [cljs.core.async.macros :refer (go)]))
(def sheets (.sheets google "v4"))
that isn't what I meant. I meant you had ["googleapis" :as google]
and that would have been invalid. the core.async is valid either way unless you are on a really old core.async version
well if you ONLY use :require-macros
that is a problem. you'll need the :require
also
[cljs.core.async :as async :refer (chan go)]
is definitely the way to go nowadays
okay great. with the go macro settled i'm back at the previous error:
TypeError: (intermediate value) is not a function
> at switch__25716__auto__ (/Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/functions/index.cljs:12:5)
> at /Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/functions/index.cljs:12:5
> at Function.functions$index$book_consultation_$_state_machine__25717__auto____1 [as cljs$core$IFn$_invoke$arity$1] (/Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/functions.index.js:130:4)
> at Object.cljs$core$async$impl$ioc_helpers$run_state_machine [as run_state_machine] (/Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/cljs/core/async/impl/ioc_helpers.cljs:43:3)
> at Object.cljs$core$async$impl$ioc_helpers$run_state_machine_wrapped [as run_state_machine_wrapped] (/Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/cljs/core/async/impl/ioc_helpers.cljs:45:1)
> at /Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/functions/index.cljs:12:5
> at Immediate.cljs$core$async$impl$dispatch$process_messages [as _onImmediate] (/Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/cljs/core/async/impl/dispatch.cljs:26:7)
> at processImmediate (node:internal/timers:464:21)
(ns functions.index
(:require ["firebase-functions" :as fun]
["googleapis" :refer (google)]
[cljs.core.async :as async :refer (chan go)]))
(def sheets (.sheets google "v4"))
(defn book_consultation [req, res]
;/book_consultation
(let [query (.. req -query)
email (.-email query)]
(go (let [request
#js
{:spreadsheetId "XXX"
:range "Sheet1!A1:C1"
:valueInputOption "RAW"
:resource #js {:values [[41 42 43] [44 45 46]]}
:auth (new
(.JWT (.-auth google))
#js
{:keyFile "./key.json"
:scopes [""]})}]
(try
(let [response (.-data (.append (.. sheets -spreadsheets -values) request))]
(.log js/console (.stringify js/JSON response nil 2)))
(catch js/Object ^js err (.error js/console err)))))))
yeah the go unfortunately messes up some stack traces. why is this in a go
though? doesn't need to be? and thus shouldn't be š
this looks incorrect (new (.JWT (.-auth google))
. just remove the go
and you'll get a better more accurate stacktrace
Beginning execution of "book_consultation"
ā functions: TypeError: (intermediate value) is not a function
at functions$index$book_consultation (/Users/simonchristensen/Documents/Developer/movenation/calculator/.shadow-cljs/builds/firebase-functions/dev/out/cljs-runtime/functions/index.cljs:10:3)
10:3 points to the starting paranthesis of the let form
(ns functions.index
(:require ["firebase-functions" :as fun]
["googleapis" :refer (google)]
[cljs.core.async :as async :refer (chan go)]))
(def sheets (.sheets google "v4"))
(defn book_consultation [req, res]
;/book_consultation
(let [query (.. req -query)
; ^ 10:3 TypeError: (intermediate value) is not a function
email (.-email query)
request
#js
{:spreadsheetId "XXX"
:range "Sheet1!A1:C1"
:valueInputOption "RAW"
:resource #js {:values #js [#js [41 42 43] #js [44 45 46]]}
:auth (new
(.JWT (.-auth google))
#js
{:keyFile "./key.json"
:scopes #js [""]})}]
(try
(let [response (.-data (.append (.. sheets -spreadsheets -values) request))]
(.log js/console (.stringify js/JSON response nil 2)))
(catch js/Object ^js err (.error js/console err)))))
It is worth mentioning that this is the code i am trying to translate:
const functions = require('firebase-functions');
const { google } = require('googleapis');
const sheets = google.sheets('v4');
const auth = new google.auth.JWT({
keyFile:
'./key.json',
scopes: [
'',
],
});
exports.book_consultation =
functions.https.onRequest(async (req, res) => {
const request = {
// The ID of the spreadsheet to update.
spreadsheetId:
'XXX',
range: 'Sheet1!A1:C1',
valueInputOption: 'RAW',
resource: {
// TODO: Add desired properties to the request body.
values: [
[41, 42, 43],
[44, 45, 46],
],
},
auth,
};
try {
const response = (
await sheets.spreadsheets.values.append(
request,
)
).data;
// TODO: Change code below to process the `response` object:
console.log(
JSON.stringify(response, null, 2),
);
} catch (err) {
console.error(err);
}
});
I think for now i will just keep this serverless function written in Node.JS. I'm not sure it is worth it to translate it to CLJS that anyways will be compiled back to Node.JS
Thanks for taking the time to help me out anyways @U05224H0W and the fast response š
so I was correct. this is invalid (new (.JWT (.-auth google))
needs to be (new (.. google -auth -JWT))
Just for reference i managed to finally get it working! š š
(ns functions.index
(:require ["firebase-functions" :as fun]
["googleapis" :refer (google)]
[cljs.core.async :refer [go]]
[cljs.core.async.interop :refer-macros [<p!]]))
(defn book_consultation [req, res]
;/book_consultation
(let [body (js->clj (.. req -body) :keywordize-keys true)
{:keys [email tocity], {:keys [city_name latitude longitude]} :geolocation} body
timestamp (.now js/Date)
auth (new (.. google -auth -JWT)
(clj->js {:keyFile "./key.json"
:scopes [""]}))
request (clj->js
{:spreadsheetId "XXXabc"
:range "bookingdata!A1"
:valueInputOption "USER_ENTERED"
:resource {:values [[email tocity city_name latitude longitude timestamp]]}
:auth auth})]
(go (let [sheets (.sheets google "v4")
response (<p! (.append (.. sheets -spreadsheets -values) request))
data (.-data response)]
(try
(.log js/console (.stringify js/JSON data nil 2))
(.send (.status res 200) data)
(catch js/Error err (js/console.error (ex-cause err))))))))
(def main
#js{:book_consultation (.onRequest fun/https book_consultation)})
It is worth mentioning that this is the code i am trying to translate:
const functions = require('firebase-functions');
const { google } = require('googleapis');
const sheets = google.sheets('v4');
const auth = new google.auth.JWT({
keyFile:
'./key.json',
scopes: [
'',
],
});
exports.book_consultation =
functions.https.onRequest(async (req, res) => {
const request = {
// The ID of the spreadsheet to update.
spreadsheetId:
'XXX',
range: 'Sheet1!A1:C1',
valueInputOption: 'RAW',
resource: {
// TODO: Add desired properties to the request body.
values: [
[41, 42, 43],
[44, 45, 46],
],
},
auth,
};
try {
const response = (
await sheets.spreadsheets.values.append(
request,
)
).data;
// TODO: Change code below to process the `response` object:
console.log(
JSON.stringify(response, null, 2),
);
} catch (err) {
console.error(err);
}
});
Hey folks, I'm trying to get into ClojureScript to escape the hell which is modern webdev with JavaScript. My problem with all the attempts to leave JS kind of behind is the necessity to use large packages like OpenLayers. Sometimes it's already a pain to use these in JS frameworks. I noticed a cljsjs package exists for openlayers. And yes, I'm sure it's usable for starters but has someone some kind of insight how usable it is for larger projects where I have to use the nitty gritty features of a library like OpenLayers?
No idea about OpenLayers in particular, but it shouldn't be much harder to use any JS library from CLJS given that interop is pretty straightforward. One note though - don't use cljsjs. Use a regular NPM package instead - modern build tools allow it.
If you use a JS library heavily it might be worth it to wrap them generically. OpenLayers looks really useful. But at a glance it seems like it has a very imperative API and all of the configuration data is wrapped with classes. In Clojure you can either write some generic macros, or likely better, you can define your configurations and effects as data structures and interpret them to talk to the JS lib.
ājust use interopā is the generally recommended approach (KISS), but I think there is a lot of value in translation or at least in attempting it. Honeysql is a library that does that for SQL for example.
Here is another example that comes to mind: https://github.com/ertugrulcetin/racing-game-cljs which uses this: https://github.com/pmndrs/react-three-fiber to translate to this: https://github.com/mrdoob/three.js/
I recently (like two months ago) struggled with a similar problem. Had to use a js library with a mutable API (not from ClojureScript). It was claimed to be ādata-drivenā, however the objects I passed actually got mutated, so in order to see what I initially passed I had to deep copy them beforehand etc.
so what I did was wrapping it into an interface and deep copy everything I pass through it to keep my sanity.
I use OpenLayers 5.x in this project https://github.com/lipas-liikuntapaikat/lipas
Iām happy to share experiences if thereās something particular youād like to know.
Mh... ok. Thanks so far! Yes, OpenLayers relies heavily on classes, this is always some form of problem I stumble upon. Everything I need is in this package but I always hate to use this OO approach.
@U6N4HSMFW Oh, this looks promising! A 'fullstack' GIS app with ClojureScript for reference is exactly what I was looking for, thank you!
From developer experience point of view interop is on par with native JS (equally horrible). But what makes it tolerable is that you can pretty easily narrow down the imperative parts to certain files and keep the rest of your code clean.
I'll dig through your code but for now it looks like it's at least possible to build some heavy WebGIS application with ClojureScript so I can keep looking into it!
Is there a nice page that documents all those :require statement conventions coming to CLJS?
if you are using shadow-cljs you can refer (pun intended š ) to their guide: https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages
Just wanted to share this beautiful piece of code before i had to ruin it by changing the button text depending on the request-state
(defn- submit-button [email city geolocation]
(let [request-state (r/atom :ready)]
(fn [email city geolocation]
[:div.field
[:div.control
[:button.button.is-primary
(merge {:type "submit" :on-click #(submit % @email city geolocation)}
(case @request-state
:loading {:class "is-loading"}
:success {:class "is-success "}
:error {:class "is-danger"}
{}))
"Book Consultation"]]])))
Could be made even more beautiful by switching from a form-2 component to reagent.core/with-let
. ;)
Hey everybody, what's the most up-to-date way to create a reagent app with cider ? I haven't used it for a couple of years. It seems all the old methods (lein reagent template, etc) are broken/buggy.
The most up to date can only be guaranteed if you do it by hand. And it's not that hard - the smallest setup is just two files, at least in the case of shadow-cljs.
What's the problem with that? The bare minimum is just a few lines - no reason to create a script or even a template for that unless you churn out projects every week. :)
But, to answer it in a more correct way - of course, not everybody does that. Perhaps, someone can link some template that works for them.
I enjoy tonsky/rum for front-end stuff