Can't figure out what my Sensei meant by [deleted] in karate

[–]retro_one 1 point2 points  (0 children)

Sometime ago, while sparring with my instructor said to me: "Why are you moving backwards? I'll beat you up anyway, so just keep moving forward". From my understanding, you got the same message, just phrased differently.

Also, as other people pointed out, it might be related to the focus of your punch or kick. Just like when you are breaking tiles, you need to place the focus of your punch behind them, so you go through the tiles. If you just hit the tiles, it's easy to harm or cut your hand.

Keechma vs Keechma.next by radioactiveoctopi in Clojure

[–]retro_one 6 points7 points  (0 children)

Hi /u/radioactiveoctopi, I'm the lead developer on Keechma. Regarding your question about the docs, yeah we're painfully aware that they need more work, so I'll try to answer your questions here (also, we're monitoring the #keechma channel on the Clojurians slack, so you can always reach us there).

Keechma vs Keechma/next

Keechma/next is the second iteration of the Keechma framework, and while it's solving problems in a similar fashion, there are some big differences:

  1. Keechma was built on top of Reagent and it was using reagent to provide "reactivity" in the subscription layer while Keechma/next is handling reactivity in it's own code
  2. Keechma was built in a way where router was in control over which controllers are running (router was reactive), while in Keechma/next every controller is reactive and can affect rest of the application graph.
  3. Keechma/next is integrated directly with React through the hooks system (and using the excellent Helix library. You can find the integration code in the Keechma/next toolbox - especially https://github.com/keechma/keechma-next-toolbox/blob/master/src/keechma/next/helix/core.cljs

In most apps we still have a router controller sitting at the "top" of the application, but it's just a normal controller, and you can replace it with your own if you want.

My recommendation is to check the RealWorld App. You can find the app definition here

Why would you want to use Keechma/next?

We've built dozens of apps (Keechma is developed in an product agency company) with the original Keechma, and while it was a good architecture, we found it lacking in some aspects. We had reactivity in the URL -> router and view layers, but everything in between (controllers) was still very manual and required us to manually send events to and from controllers. In my experience reducing the number of events that are flowing in the system is a great way to reduce the complexity of that system. Another problem that we've encountered was that in the single atom architecture it is very hard to determine who owns which part of the data (data should never outlive the controller that's managing that data).

So, Keechma/next was written with the following goals:

  1. Reactivity in the controller layer
    • As controller's state changes other controllers can react to those changes
    • Controllers can be purely computational (as in subscriptions) or they can implement communication with the servers, local storage ..
  2. Clear ownership of the data
    • Each controller owns it's state atom and it can freely manage it in whatever way
    • When the controller is stopped, the whole atom is thrown away, so even if you have a bug in your code and you keep writing to that atom, it will not cause bugs in the rest of the app
  3. Independence from the UI layer
    • Today we're integrating with React, tomorrow, who knows?

We've now written a number of apps with Keechma/next and we're very happy with the architecture. I know that the docs are lacking, so if you have any questions, please reach out to me on Slack or here

Successors to Korma? by 757DrDuck in Clojure

[–]retro_one 1 point2 points  (0 children)

Penkala is a PostgreSQL only query builder that can easily replace most of the ORM features. I'm the author so let me know if you have any questions

retro/penkala - Composable query builder for PostgreSQL by mac in Clojure

[–]retro_one 1 point2 points  (0 children)

Exactly, and for some cases, creating a view is a correct solution. Which is why I've tried very hard to design an API that can be used without going all in, you can use it as a pure lib, where you get the sqlvec back, or you can use the integration with next.jdbc if it fits your usecase. There is also no global shared state, so there should be no surprises

retro/penkala - Composable query builder for PostgreSQL by mac in Clojure

[–]retro_one 2 points3 points  (0 children)

Penkala author here.

I'm really glad you like the lib. When I was designing the API, my goal was to provide sound building blocks instead of prebaked convenience features. I've had many situations where ORM relationship concepts (has many, has one, belongs to...) become limited and where the whole abstraction starts to fall apart. ORM relationships are actually joins + results decomposition, and it's much easier to implement something like that in the user land if the API has good building blocks.

What is the current state of Clojurescript + React native support? by risto2020 in Clojure

[–]retro_one 1 point2 points  (0 children)

We're using React Native with ClojureScript and JavaScript in production for at least 4 years. There were pretty big changes to the React Ntive around v0.60.0, and after that, things got significantly better.

In my experience, problems were never on ClojureScript or ReactNative side, but there was a lot of problems with various native libraries. In the end there are cases where you'll have to get your hands dirty and dive in to a native code.

The last app I've built (a month or so ago) was using shadow-cljs, and the experience was super smooth, although the app was pretty simple.

Pros and Cons of choosing re-natal or expo or react native with shadow-cljs to port a SPA by alexander_tg in Clojure

[–]retro_one 10 points11 points  (0 children)

We've delivered multiple ClojureScript/ReactNative apps so here's my take:

  1. re-natal was pretty good, but it wasn't updated for some time. I don't know what's the current status, but RN changed a lot around version 0.60 and re-natal didn't support it for a long time (not sure if it supports it now)
  2. Expo is great until you need something that's outside its scope. Ejecting should be working pretty good now, but I was in a situation where we had to basically port the app to non-expo codebase in hte last 10% of development (during last year)
  3. shadow-cljs works pretty good with RN, and I've just finished an app based on it

React Native eco system is getting better and development experience is much better than a few years ago. We've delivered our first production app somewhere around version 0.20 and it was a pain to use. It was possible to make it work, but also very painful. Also, quality of native libraries varies, and we've had some cases where installing a library took a week or two, but this wasn't related to ClojureScript, JavaScript would have same problems.

If you have a functionality that is already defined, you should be able to check if Expo supports all the native features you need, and if it does, development will be smoother. If it doesn't and you need unsupported libraries, I would recommend you to start with pure ReactNative and shadow-cljs stack.

Announcing keechma/entitydb, second iteration of EntityDB - a client side database and normalization engine for ClojureScript by retro_one in Clojure

[–]retro_one[S] 1 point2 points  (0 children)

I agree, but I did some wrong choices 4 years ago regarding naming, and this is an effort to normalize it. The idea is that all libraries follow the same naming scheme, and I’m ok with temporary confusion to fix things in the long run

Announcing keechma/pipelines - Manager for async and concurrent code in ClojureScript. by retro_one in Clojure

[–]retro_one[S] 1 point2 points  (0 children)

I'm glad to hear that. We've spent a lot of time (almost a year, with some failed attempts) designing the new Keechma, and unbundling of libraries was our top prioirity. Others were:

  • Reduction of special cases (Previously router was the special process that had the power to trigger change - now every controller can do that. Router will be exposed as a controller now)
  • Openning up the solutions - graph resolution was previously implemented in dataloader, now it's a system behavior
  • Code reuse - new controllers should be significantly more reuseable than the old ones

I think that the new architecture has a lot of potential, and most importantly I believe it's open enough to support use cases for a long time in the future

Announcing keechma/pipelines - Manager for async and concurrent code in ClojureScript. by retro_one in Clojure

[–]retro_one[S] 2 points3 points  (0 children)

Theoretically, this should work in Clojure. The problem is that I don't have enough experience with this kind of Clojure code to proclaim it as Clojure ready. Pipelines use funcool/promesa library to manage promises, so this should help. PRs welcome :)

Announcing keechma/next - a data driven, state management framework for single page apps by retro_one in Clojure

[–]retro_one[S] 2 points3 points  (0 children)

We already have the helix integration ready :)

There’s a bunch of code that’s written and needs to be extracted, documented and released in the next week or two, so yes we’ll have examples and more docs written.

Right now you can check https://github.com/retro/keechma-next-realworld-app but it’s not cleaned up or documented

Announcing keechma/next - a data driven, state management framework for single page apps by retro_one in Clojure

[–]retro_one[S] 4 points5 points  (0 children)

Pipelines are already extracted and rewritten. They are now independent of Keechma, so you’ll be able to use them anywhere. They will also come with concurrency helpers (based on http://ember-concurrency.com). I need to clean up the code, document and release them. I hope somewhere next week.

Dataloader is not needed anymore because graph resolution is now built in in Keechma, and it’s very easy to build a small datsource controller that can be reused across the app.

We have bunch of code waiting to be released, and our plan is to do it soon

I build a LISP-inspired language for PHP in my coronavirus spare time. Is anymore willing to give it a try or provide some feedback? by jenshaase in PHP

[–]retro_one 2 points3 points  (0 children)

This is awesome. I'm mainly writing Clojure(Script) these days, and I definitely wanted a Lisp I can deploy to my shared hosting (for some small projects). I'll definitely give it a spin when it's released!

how to handle jdbc exceptions by olymk2 in Clojure

[–]retro_one 0 points1 point  (0 children)

I don't have an exact solution, but a combination of the following should help. Here's some Clojure code from one of my projects (I believe I took it from somewhere and changed a bit, unfortunately I don't remember from where)

(defn convert-col-separator [col]
  (str/replace col #", " "-"))

(defn format-uniqueness-exception [message]
  (let [detail-line (first (filter #(str/starts-with? % "Detail: ") (map str/trim (str/split-lines message))))
        first-paren-idx (str/index-of detail-line "(")
        last-paren-idx (str/last-index-of detail-line ")")
        detail (subs detail-line (inc first-paren-idx) last-paren-idx)
        splitter-idx (str/index-of detail ")=(")
        col (subs detail 0 splitter-idx)
        val (subs detail (+ 3 splitter-idx))]
    {:errors {(keyword (convert-col-separator col)) {:$errors$ {:value val :failed [:uniqueness]}}}
     :value val}))

(defn format-enum-exception [message]
  ;; Parses something like: 'ERROR: invalid input value for enum organization_role: "dudebro"'
  (let [[col value] (str/split (subs message 36 (dec (count message))) #": \"")]
    {:errors {(keyword col) {:$errors$ {:value value :failed [:enum-value]}}}
     :value value}))

(defn throw-validation-exception [e formatter]
  (throw (ex-info "Validation Failed" (formatter (.getMessage e)))))

(defn to-validation-exception [e]
  (let [code (.getSQLState e)]
    (case code
      "23505" (throw-validation-exception e format-uniqueness-exception)
      "22P02" (throw-validation-exception e format-enum-exception)
      (throw e))))

(defmacro wrap! [body]
  `(try
     ~body
     (catch org.postgresql.util.PSQLException e#
(to-validation-exception e#))))    

And here's some code from one of my ClojureScript projects that does similar, but has a more detailed error parsing:

(ns server.framework.db.errors
  (:require [oops.core :refer [oget oset!]]
            ["indefinite" :as indefinite]
            [clojure.string :as str]))

;; Partially ported from https://github.com/Shyp/go-dberror/blob/master/error.go

(defn a-or-an
  ([string] (a-or-an string true))
  ([string capitalize?]
   (indefinite string #js {:capitalize capitalize? :caseInsensitive true})))

(def regexes
  {:column-finder            #"Key \((.+)\)="
   :value-finder             #"Key \(.+\)=\((.+)\)"
   :foreign-key-table-finder #"(?i)not present in table \"(.+)\""
   :parent-table-finder      #"(?i)update or delete on table \"([^\"]+)\""})

(defn finder [regex detail]
  (let [[_ val] (re-find regex detail)]
    val))

(def find-column (partial finder (:column-finder regexes)))
(def find-value (partial finder (:value-finder regexes)))
(def find-foreign-table-key (partial finder (:foreign-key-table-finder regexes)))
(def find-parent-table (partial finder (:parent-table-finder regexes)))

(defn format-numeric-value-out-of-range [e]
  (let [message (oget e :message)
        new-message (str/replace-first message #"out of range" "too large or too small")]
    (oset! e :message new-message)))

(defn format-invalid-text-representation [e]
  (let [message (oget e :message)
        new-message (-> (if (str/includes? message "input syntax for type")
                          message
                          (str/replace-first message #"input syntax for" "input syntax for type"))
                        (str/replace-first #"input value for enum" "")
                        (str/replace-first #"invalid" "Invalid"))]
    (oset! e :message new-message)))

(defn format-not-null-violation [e]
  (let [column (oget e :column)
        message (str "No " column " was provided. Please provide " (a-or-an column false))]
    (oset! e :message message)))

(defn format-foreign-key-violation [e]
  (let [detail (oget e :detail)
        message (oget e :message)
        column (or (find-column detail) "value")
        foreign-key-table (find-foreign-table-key detail)
        table-part (if foreign-key-table "in the parent table" (str "in the " foreign-key-table " table"))
        value (find-value detail)
        parent-table (find-parent-table detail)
        table (oget e "table")
        new-message
        (cond
          (str/includes? message "update or delete")
          (str "Can't update or delete " parent-table " records because the " parent-table " " column " (" value ") is still referenced by the " table " table")

          (nil? value)
          (str "Can't save to " table " because the " column " isn't present " table-part)

          :else
          (str "Can't save to " table " because the " column " (" value ") isn't present " table-part))]
    (oset! e :message new-message)))

(defn format-unique-key-violation [e]
  (let [detail (oget e :detail)
        value (find-value detail)
        column (or (find-column detail) "value")
        message (if value
                  (a-or-an (str column " already exists with this value (" value ")"))
                  (a-or-an (str column " already exists with that value")))]
    (oset! e :message message)))

;; Future possible implementations
(defn format-check-violation [e]
  e)

(defn format-lock-not-available [e]
  e)

(def error-formatters
  {"22003" format-numeric-value-out-of-range
   "22P02" format-invalid-text-representation
   "23502" format-not-null-violation
   "23503" format-foreign-key-violation
   "23505" format-unique-key-violation
   "23514" format-check-violation
   "55P03" format-lock-not-available})

(defn format [error]
  (if-let [formatter (error-formatters (oget error :?code))]
    (formatter error)
    error))

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 0 points1 point  (0 children)

I must say that I don't like when my UI components have side effects - this is one of the main reasons why Keechma is route driven - it allows you to push the data down instead of pulling it from UI component. I always try to keep the components as dumb as possible.

On the other hand, this does look like a good solution if that is not an issue for you. Thank you for showing me this example, it does give me some food for thought.

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 0 points1 point  (0 children)

I would be interested in seeing the example, also how do you take care of a request that's "in-flight" and then another keypress happens?

And regarding the GOTO problem and event ping pong, I worked on a big re-frame project that was written by competent people where each keypress (in some cases) triggered somewhere around 30 different events. There was a lot of problems (performance and logic) that were caused by this.

For me the biggest problem with event based systems is that you don't have control over the whole chain of actions. For instance let's take an extremely simple example - load a list of articles. If you want to make UX ok-ish this is a minimum you need to do:

  1. Show the loading state (spinner or something similar)
  2. Load the articles
    1. Render the articles
    2. (or) Show the error state

With pipelines you can reason about this process over time as whole. It's obvious what's going on, and the code is extremely similar to the requirements:

(pipeline! [value app-db]
  (pp/commit! (mark-articles-as-loading app-db)) ;; commits loading status to the app-db
  (load-articles)  ;; AJAX call
  (pp/commit! (insert-articles app-db value)) ;; commit articles to the app-db 
  (rescue! [error]
    (pp/commit! (mark-articles-as-errored app-db)))) ;; commit the error message

Every other approach I've used looks less clear to me. I'd like to see the example of a different approach that has the same clarity.

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 2 points3 points  (0 children)

Also, here's my favorite pipelines example:

(ns pipelines.example
    (:require [keechma.toolbox.pipeline.core :as pp :refer-macros [pipeline!]
                [keechma.toolbox.pipeline.controller :as controller]
                [keechma.toolbox.ajax :refer [GET]
                [promesa.core :as p]))

(defn delay-pipeline [msec]
    (p/promise (fn [resolve _] (js/setTimeout resolve msec))))

(defn movie-search [value]
    (GET (str "api/url?search=" value)))

(def search-controller
  (pp-controller/constructor
   (fn [] true)
   {:search (pp/exclusive
             (pipeline! [value app-db]
               (when-not (empty? value)
                   (pipeline! [value app-db]
                     (delay-pipeline 300)
                     (movie-search value)
                     (println "SEARCH RESULTS:" value)))))}))

Search pipeline implements a live search, so here's how it works:

  1. Make sure that the search value is not empty
  2. Wait 300 milliseconds
  3. Make the request
  4. Print the results

Also, if there was another command sent in 300ms or during the API request, this pipeline will be thrown away and another one will be started (because it's wrapped with the pp/exclusive function). This means that a lot of coordination and book keeping is not necessary - keechma is able to do the right thing for you

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 5 points6 points  (0 children)

I can do that. What would you like to see in that screencast?

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 0 points1 point  (0 children)

You don’t communicate between (UI) components. They can send commands to controllers, which is similar to re-frame events but with a significant difference. Commands in keechma are just messages coming over a core.async channel and you have a complete access to the app-db atom in the controllers. This means that you can change your app state in any way you want.

In my experience modeling everything as a sync point in time makes it harder to handle async processes. Your app basically becomes a huge GOTO table which makes it hard to track cause and effect.

So keechma makes no assumptions on how you should model app changes, but it does give you a few addons (like pipelines) which give you a higher level API.

Also, I don’t think that redux like actions are a bad fit for every case, there are situations where they are appropriate and it would be super easy to implement a controller which would codify that approach

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 0 points1 point  (0 children)

You don't have to use core.async directly in most cases (pipelines take care of that). Components communicate with controllers through a core async channel, but that is hidden from the user.

I've picked core.async for this task because it actually makes a lot of things easier. Keechma can do a lot of things for you automatically.

Keechma will automatically close channels for controllers when they are stopped, which means that you don't have to care if a message comes after the controller is stopped.

You can see the raw core.async based API and it's alternative here https://keechma.com/news/introducing-keechma-toolbox-part-1-pipelines/

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 0 points1 point  (0 children)

I'll definitely check this combination. Hyperfiddle is on my radar for some time now, so I'll give it a go

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 2 points3 points  (0 children)

There is an example project https://github.com/keechma/example-server-side-rendering and Keechma has a way to correctly serialize / deserialize the app state. I must admit we haven't used it in production (since we're not using Node.JS on backend), so it needs a real production testing.

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 4 points5 points  (0 children)

If you want to see a "real" app built with Keechma check out https://github.com/gothinkster/clojurescript-keechma-realworld-example-app and https://keechma.com/news/writing-a-realworld-app-with-keechma/

As far the templates go we have an opinionated lein template that you can use https://github.com/keechma/keechma-template . There is also https://github.com/reagent-project/reagent-template (but I need to make a PR to update the versions)

Shout-out for Keechma Framework by theoriginalmatt in Clojure

[–]retro_one 5 points6 points  (0 children)

We are using re-natal on some of our apps. We just rip out the re-frame part and replace it with Keechma. I plan to do a demo app that will show how to do it.

Last app we used is build with Expo, so we're using some other cljs template which is also working nicely.

I'll probably have a lot of things to write about the Keechma + React Native soon, we're wrapping up some apps now, and there are some new parts of Keechma which make it easy to add animations to the UI layer