Clojure Portal tool: Exception viewer and other custom viewers are missing - clojure

I can start portal and inspect values as expected. But my portal tool doesn't include viewers such as Exception viewer or class viewer as different from the demo video.
I checked the source code of the portal and the jar distribution file. The source code contains a namespace called portal.ui.viewer. This namespace is missing from the portal's jar distribution. Are those viewers included into some other jar file? How can I employ those viewers?
My deps.edn has the following alias
:inspect/portal-cli
{:extra-deps {djblue/portal {:mvn/version "0.15.1"}
cheshire/cheshire {:mvn/version "5.10.0"} ; json
clj-commons/clj-yaml {:mvn/version "0.7.0"} ; yaml
,}}
I start portal using:
clojure -M:inspect/portal-cli
My user.clj includes:
(ns user
(:require [portal.api :as p]))
(def portal (p/open))
(p/tap)
After repl starts, I run the following commands:
(tap> {::exception (try (/ 1 0) (catch Exception e e))})
Here are the results:
According to the demo video, I am supposed to see portal.viewer/ex as a viewer option. Then the stack trace would be listed in the proper format as here:

The viewer is available, but the exception needs to be data-fied first. Portal use to automatically datafy values but this behavior was problematic when I wanted access to the original object. You can datafy any selected value within the portal ui via the command palette (cmd + shift + p or ctrl + j).

Related

Why does this re-frame command only work when the browser is on a specific local host address? Shouldn't it be just an interface to the the database?

I have been using Clojure, ClojureScript, lein, shadow-cljs, Emacs, and CIDER to work on a Clojure/ClojureScript dynamic web app project.
Usually, I build the project executing command cider-jack-in-cljs in Emacs, choosing shadow-cljs, then shadow for REPL type, and, finally, app for building option.
It works fine. I can watch changes on the UI on localhost:3005.
There is something weird though that I do not understand (take into consideration that I am new to the Clojure stack).
On the REPL I can do:
cljs.user> #(re-frame.core/subscribe [:the-selections])
{:firm "cb08795f-378b-4eb0-9404-ad83b83a8474",
:active-client "Random-client",
:active-atb "c6193c35-bf91-4711-8523-d905bd7f0a03"}
The keywords inserted by me and retrieved by the REPL are related to the project.
My doubt is about the fact that this only works if the browser is in a specific page (address). Authentication here is not relevant.
On http://localhost:3005/link/random-numbers-asidadbsadkfbajhksdbf9283492374, it works:
cljs.user> #(re-frame.core/subscribe [:the-selections])
{:firm "cb08795f-378b-4eb0-9404-ad83b83a8474",
:active-client "Random-client",
:active-atb "c6193c35-bf91-4711-8523-d905bd7f0a03"}
But, if change the address bar on the Chrome browser to another valid path being properly rendered by the browser: http://localhost:3005/another-path.
And if try the same command, surprisingly the REPL retrieves nil:
cljs.user> #(re-frame.core/subscribe [:the-selections])
nil
Even after authentication and even if the browser address is inside the home page, the command above does not work. It only works after the address is on a specific page.
In addition, based on #ThomasHeller's comment, it seems to be relevant to post how :the-selections is defined. I am not sure this is the root of the definition, but it is my best bet:
(rf/reg-sub
:the-selections
;; Only returns something if all :firm, :active-client, and
;; :active-atb are present. :raw-selections defined in
;; selections.cljs omits validation if (rarely) needed. selections
;; are only stored locally in re-frame's app-db, not in firebase,
;; like :the-client and :the-atb. Minor selections components which
;; are not part of the validation are :active-account and :active-je
(fn [_q _d]
(rf/subscribe [:raw-selections]))
(fn [selections _q]
(tc-selections/valid-selections selections)))
This behavior intrigues me. Isn't a re-frame.core/subscribe an interface to the database?
According to re-frame's documentation:
When a View Function uses a subscription, like this (subscribe [:something :needed]), the sub-graph of nodes needed to service that subscription is created. The necessary sub-graph will "grow backwards" from the View Function all the way to app-db.
If so, why should the address bar on the browser matter after proper build and authentication?
This is impossible to answer without knowing how :the-selections is defined.
However, a guess would be that the value is provided by the "router". It basically takes the URL and puts some data related to it into the app-db. In case of URIs starting with /link it may be creating the necessary data, while others don't.

timbre `set-config!` has changed arity thus don't know how to use it to output std err/out to a file

I am trying to use https://github.com/ptaoussanis/timbre to log to a file rather than the console. Here is some documentation I've found on how to do that:
; The default setup is simple console logging. We with to turn off console logging and
; turn on file logging to our chosen filename.
(timbre/set-config! [:appenders :standard-out :enabled?] false)
(timbre/set-config! [:appenders :spit :enabled?] true)
(timbre/set-config! [:shared-appender-config :spit-filename] log-file-name)
(timbre/set-config! [:shared-appender-config :spit-filename] log-file-name)
This works for a previous version of sente, but not for version [com.taoensso/timbre "4.3.1"]. (For unrelated reasons I need to use the latest). The issue with the above code is that set-config! now takes one argument - a hash-map. And I can't find any documentation that would help me with translating the above 'two params' code to the new 'one param' code.
I know there's a very similar question out there. This question has actual code in it so is more specific. I raised an issue as well. The code above basically comes straight from here.
To log to a file rather than the console in timbre v4.0.0 (2015 June 10) you can do the following:
(ns app.core
(:require [taoensso.timbre :as timbre]
[taoensso.timbre.appenders.core :as appenders]))
;; Disable logging to the console in timbre v4.0.0:
(timbre/merge-config! {:appenders {:println {:enabled? false}}})
;; Create a "spit to file" appender in timbre v4.0.0:
(timbre/merge-config! {:appenders {:spit (appenders/spit-appender {:fname "log.txt"})}})
Note that in timbre v4.3.1 the following does not remove nor disable the println (console) appender:
(timbre/merge-config! {:appenders {:println nil}})
The related issue can be found here https://github.com/ptaoussanis/timbre/issues/163
Addition:
As Timbre allows to modify it's config map timbre/*config* simply by using
the standard clojure API, you may also use
(timbre/swap-config! assoc-in [:appenders :spit :enabled?] false)
(timbre/swap-config! assoc-in [:appenders :spit] (appenders/spit-appender {:fname "log.txt"}))
Based on this you can additionally define a simple helper:
(def set-log-config-param! (partial timbre/swap-config! assoc-in))
and then be consistent with the timbre v3.4.0 set-config! syntax you
have quoted in your question:
(set-log-config-param! [:appenders :println :enabled?] false)
(set-log-config-param! [:appenders :spit] (appenders/spit-appender {:fname "log.txt"}))
Imho this eases the config transition between timbre v3 and v4 and makes the path to
the params more readable then the equivalent
(timbre/merge-config! {:appenders {:println {:enabled? false}}})
(timbre/merge-config! {:appenders {:spit (appenders/spit-appender {:fname "log.txt"})}})
I got a quick response from the maintainer:
"Usage of the spit (file) appender is documented in the README at https://github.com/ptaoussanis/timbre#file-appender"
And here's the code to answer the question:
;; (:require [taoensso.timbre.appenders.core :as appenders]) ; Add to ns
(timbre/merge-config!
{:appenders {:println nil ; Remove println appender
:spit (appenders/spit-appender {:fname log-file-name})}})
Unfortunately even with the :println nil mapentry the same output will go to two places. So this answer is incorrect.

How to set S3 path style in Clojure using Amazonica library?

I'm a Ruby dev moving over to Clojure and I'm having trouble understanding how to translate the following Java call into Clojure based on the conventions used in the Clojure library Amazonica.
AmazonS3 client = new AmazonS3Client(credentials);
client.setS3ClientOptions(new S3ClientOptions().withPathStyleAccess(true));
The code I have at the moment is:
(ns spurious-aws-sdk-helper.core
(:use [amazonica.aws.s3]])
(:require [amazonica.core :refer [ex->map]]))
(def credentials {:access-key "development_access"
:secret-key "development_secret"
:endpoint "s3.spurious.localhost:49154"
:client-config {:protocol "http"}})
(try
(amazonica.aws.s3/set-s3client-options {:path-style-access true})
(create-bucket credentials "testing")
(catch Exception e
(clojure.pprint/write (ex->map e))))
But I'm getting the following error:
com.amazonaws.http.AmazonHttpClient executeHelper
INFO: Unable to execute HTTP request: testing.s3.spurious.localhost
java.net.UnknownHostException: testing.s3.spurious.localhost
This doesn't look to be correct as it's prefixing the bucket name (testing) onto the hostname. Where as I need the SDK to talk to our local (fake) S3 service (s3.spurious.localhost:49154) using path style.
e.g. like http://s3.spurious.localhost:49154/testing
I think it's because I'm not translating the Java code correctly...
(amazonica.aws.s3/set-s3client-options {:path-style-access true})
...this is passing a map to set-s3client-options rather than what it should be, which is passing the result of calling withPathStyleAccess(true) on a new instance of S3ClientOptions. But I don't know how to do that here?
Any help would be greatly appreciated.
UPDATE
Here is the latest version of the code (which still doesn't work)...
(ns spurious-aws-sdk-helper.core
(:use [amazonica.aws.s3])
(:require [amazonica.core :refer [ex->map]]))
(def credentials {:access-key "development_access"
:secret-key "development_secret"
:endpoint "s3.spurious.localhost:49154"
:client-config {:protocol "http"}})
(try
(amazonica.aws.s3/set-s3client-options
(. (com.amazonaws.services.s3.S3ClientOptions.) setPathStyleAccess true))
(create-bucket credentials "testing")
(catch Exception e
(clojure.pprint/write (ex->map e))))
Your first call to set up the S3ClientOptions is the correct one.
I think you're confusing what that setting does.
It changes the pre-signed URL generation from using the Virtual Hosted Style https://my_bucket_name.s3.amazonaws.com/my_key to the Path Style https://s3.amazonaws.com/my_bucket_name/my_key
This is necessary if your bucket name contains dots, as the virtual hosted style breaks Amazon's SSL certificate identification (i.e. it only supports the wildcard *.s3.amazonaws.com)
So if you're generating pre-signed URLs on a bucket name containing dots in Clojure e.g.
(s3/generate-presigned-url bucket key (-> 6 hours from-now)))
You will first need to set the path style using
(amazonica.aws.s3/set-s3client-options {:path-style-access true})
Thanks to #stukennedy for responding to the open question. I should have come back long ago and updated this space with the actual solution which I managed to figure out and implement ~8months ago (Feb 2nd 2015):
(ns spurious-aws-sdk-helper.s3
(:use [amazonica.aws.s3])
(:require [spurious-aws-sdk-helper.utils :refer [endpoint cred]]))
(defn resource [type]
(endpoint type :spurious-s3))
(defn setup
([type]
(set-s3client-options (cred (resource type)) :path-style-access true))
([type name]
(try
(let [credentials (cred (resource type))]
(set-s3client-options credentials :path-style-access true)
(create-bucket credentials name))
(catch Exception e
(prn "S3 Error: chances are you're creating a bucket that already exists")))))
You can find the full details here: https://github.com/Integralist/spurious-clojure-aws-sdk-helper if you need more context
If I remember correctly, I needed to pass credentials through to set-s3client-options (without them it wouldn't work)

Create file in Clojure

I'm trying to create rdf model with Apache Jena TDB, so here is the code:
(def model (com.hp.hpl.jena.tdb.TDBFactory/createModel "/rdfrepo"))
Which gives me following error:
Exception in thread "main" com.hp.hpl.jena.tdb.base.file.FileException: Failed to open: /rdfrepo/node2id.idn (mode=rw)
I have tried adding
:resource-paths ["shared" "resources"]
to projects.clj but didn't make any change.
I'm not very familiar with file system management in Clojure so I really need help with this one.
You can get resource file with clojure.java.io/resource
(clojure.java.io/resource "css/default.css")
=> #<URL file:/Users/yyy/xx/resources/css/default.css>
You don't need to add the / before the resource path.
You can then open the file with io/file:
(require '[clojure.java.io :as io])
(-> "file.png" io/resource io/file)
In your case you can try passing (io/resource "rdfrepo") to createModel
A path that begins with / is always at the root of the file system, which is a place you should not be trying to write. Will the code work without the leading / on the path?

FTP with Clojure

Are there any libraries to perform FTP transfer with clojure, idiomatic to clojure, or is it necessary to use a java library such as apache commons?
Thanks
It is not necessary to use java library and you can roll complete FTP implementation in Clojure but that would be like re-inventing the wheel and not a feasible thing to do. What you can do is probably write a more functional wrapper over the Java library and then use that wrapper in your clojure code so that everything seems seamless and that't how many of the existing Java libraries are being used in Clojure.
You can use https://github.com/miner/clj-ftp either by invoking few convenience functions or by opening a client and invoking multiple commands with it.
The full API is documented in GitHub at https://github.com/miner/clj-ftp/blob/master/src/miner/ftp.clj .
Contents of project.clj
(defproject my-sweet-project "0.5.0"
:dependencies [[com.velisco/clj-ftp "0.3.0"]
; Other deps
]
; ...
)
Invoking a single FTP command
This will open new FTP connection for each command so it should be used for invoking a single command only. See the full API for complete list of these convenience functions.
(ns my-sweet-name.space
(:require [miner.ftp :as ftp]))
(defn list-files-from-ftp-server []
"Here we list contents of a directory with a convenience function"
(let [ftp-url "ftp://username:password#my.ftp.server.host:port/path/to/stuff"]
(ftp/list-files ftp-url)))
Invoking multiple commands with same connection
This will open FTP connection and invoke arbitrary amount of commands with it. This should be used when multiple commands should be invoked. The FTP connection will be automatically closed. Again check the full API for complete list of functions.
(ns my-sweet-name.space
(:require [miner.ftp :as ftp]))
(defn list-and-download-files []
"Here we list and download contents of a directory"
(let [ftp-url "ftp://username:password#my.ftp.server.host:port/path/to/stuff"]
(ftp/with-ftp [ftp-client ftp-url]
; client-file-names is used to list contents of the ftp-url
; client-get is used to download a file
(doseq [file-name (ftp/client-file-names ftp-client)]
(let [local-file-name (str "/download-path/" file-name)]
(ftp/client-get ftp-client file-name local-file-name))))))
https://github.com/miner/clj-ftp is a wrapper over Apache Commons Net.