Racket - require an entire directory of files - scheme

Currently I have to do this at the top of every file:
(require "dynamore/dynamore.rkt")
(require "dynamore/db.rkt")
(require "dynamore/types.rkt")
I would like to be able to do something like this:
(require dynamore)
Preferably without having to develop my own collection.

If you prefer not to create a package (which means you need to require relatively):
If dynamore contains only those three files, you can use reprovide-lang's glob-in as follows: (require (glob-in "dynamore/*.rkt")).
You can also create main.rkt in dynamore that uses the main functionality of reprovide-lang to specifically reprovide only those three files:
#lang reprovide
"dynamore.rkt"
"db.rkt"
"types.rkt"
To use it, simply (require "dynamore/main.rkt").
A native solution that doesn't use reprovide-lang would be to use all-from-out manually:
#lang racket/base
(require "dynamore.rkt"
"db.rkt"
"types.rkt")
(provide (all-from-out "dynamore.rkt"
"db.rkt"
"types.rkt"))
If you prefer to create a package, then follow Solution 2 above (create main.rkt, etc.), create info.rkt in the dynamore directory as follows:
#lang info
(define collection "dynamore")
Then run raco pkg install. From now on, you will be able to (require dynamore) from anywhere.
Note: to install reprovide-lang, run raco pkg install reprovide-lang.

Related

How to separate development and production keys in Leiningen app on heroku?

I want have two different sets of API keys for development and production and I want to use them automatically depending on whether the app is run locally or on heroku.
For nodejs apps, I do the following:
if (process.env.NODE_ENV === 'production') {
// ./prod contains the production keys
module.exports = require('./prod');
} else {
// ./dev contains the development keys
module.exports = require('./dev');
}
How to do something similar in a Clojure Leiningen app? What would the process.env.NODE_ENV analogue be, and how to implement it?
Clojure runs on JVM, so you could use java System class. Assuming you have set APP_ENVIRONMENT variable in bash.
(if (= "production"
(or (System/getenv "APP_ENVIRONMENT")
"development"))
(start/production)
(start/development))
I would say the cleanest solution would be to use the environ library together with the lein-environ plugin.
With these you store development version of API keys in env vars in the leiningen dev profile and on production pass them as actual environment variables. E.g. you can have the following in your project.clj:
(defproject ...
...
:profiles {:dev {:env {:my-api-key "whatever-dev" } } }
...)
On the production environment you could simply set an environment variable MY_API_KEY to "whatever-prod".
In your code you will access the key like this:
(require '[environ.core :refer [env]])
(env :my-api-key) ;; would yield "whatever-dev" on dev and "whatever-prod" on prod environment
In particular, this allows you not to store the production keys under source control.
environ provides a couple more options - in particular with env files, which may also fit your needs. Do check its capabilities.
If you also need access to environment variables in your CLJS (on frontend), you can't simply access environment variables there indeed (because there's no such thing as environment anymore - the code runs on the client).
Still, if you just wish to drop the values into the resulting js during compilation, you can use environ + a macro for that. You will write a macro in a clj file that just returns the env var and then refer it in your cljs file. During compilation the appropriate value will be injected.
In a clj file:
(ns yourns.environment
(:require [environ.core :refer [env]]))
(defmacro my-api-key []
(env :my-api-key))
In cljs:
(ns yourns.core
(:require-macros [yourns.environment :refer [my-api-key]))
(my-api-key) ;; will return the appropriate value
Note, however, again that the variable value will be injected in compile time and can't be changed later.
Please see here for a complete code example.

How do I use a SRFI inside a module in Chicken Scheme?

The following file gives an error when it is compiled with csc.
(module
monoid *
(import chicken scheme)
(use srfi-9)
(define-record-type a0 (a0) a0?))
The error is:
Syntax error (import): cannot import from undefined module
srfi-9
Expansion history:
...
I followed an example in "The CHICKEN User's Manual/Supported language/Modules/Examples of using modules".
Some smaller SRFIs are part of the chicken module. You can just remove the (use srfi-9) line.
I know that this is somewhat confusing, but if you read it carefully, the manual doesn't state that there is a module for it (and indeed, there isn't). This too has been made a bit saner in the upcoming CHICKEN 5. There, srfi-9 is a clean, separate module, and your example program works as-is on CHICKEN 5.

Just can't import files to my Racket program

One in two files I try to import to my program doesn't work out.
I know the programs have to be in the same folder or at least the command require must state the directory.
I've got a folder where my program is saved and a folder se3-bib in the same folder that my program is in.
Then, there's another folder in the se3-bib folder called prolog which contains a file I'd like to import to my program.
When I try this:
#lang racket
(require "se3-bib/prolog/prologInScheme.rkt")
I get a fail saying:
se3-bib\prolog\prologInScheme.rkt:19:3: standard-module-name-resolver: collection not found
for module path: se3-bib/tools-module
collection: "se3-bib"
in collection directories:
C:\Users\Lidiya\AppData\Roaming\Racket\6.3\collects
C:\Users\Lidiya\Documents\Mathe WS1516\SE3\Racket\collects
... [161 additional linked and package directories] in: se3-bib/tools-module
no packages suggestions are available
You can also find a screenshot of that.

Why can't lein repl find function?

I'm using
[ics#steamboy util]$ lein version
Leiningen 2.5.1 on Java 1.7.0_91 OpenJDK Server VM
with Clojure 1.6
In lein repl I used to be able to call a function from within repl.
util.core=> (load-file "src/util/core.clj")
#'util.core/-main
util.core=> (bldg-sqft-test)
And execute a function from within the repl.
(defn ret-val-from-sos
"Takes a value, a map key, an s-o-s, and returns first match."
[in-val map-key-1 map-key-2 s-o-s]
(doseq [x s-o-s]
(println (str (first x)))))
(defn bldg-sqft-test
[& args]
(let [bldg-cols (fetch-csv-data "bldg_sqft_cols.csv")
bldg-data (fetch-csv-data "Buildingsqft.csv")
mapped-data (xform-sos-in bldg-data bldg-cols)
my-bldg-sqft (ret-val-from-sos (str 70782) (keyword "Bill#") (keyword "Fin. Area") mapped-data)]
my-bldg-sqft))
Debugging was easier, when I could examine variables in repl. As a workaround, I've converted a library to run with a main, but it's not as effective as repl debugging
What settings/configuration do I need to do call functions from within the repl?
When you run $ lein repl a JVM instance is started and it loads all the namespaces in your classpath, so (load-file "example.clj") isn't necessary.
The correct sequence for what you're trying to do is:
$ lein repl
user=>(require 'util.core)
nil
user=>(in-ns 'my-ns.core)
nil
my-ns.core=>(bldg-sqft-test)
or
$ lein repl
user=>(require '[util.core :refer :all])
nil
user=>(bldg-sqft-test)
If you somehow need to load a external .clj file then (load-file "external.clj") will add the file to the classpath and then you can require the namespace as above.

How to get the current hostname in Heroku

I'm playing around with deploying Clojure/Noir apps on Heroku and I've got my app mostly working. However, one final piece I need is to figure out the hostname of my app when deployed on Heroku. Ideally, I want to do this dynamically instead of hard-coding it.
So, if for example, my app's URL is 'http://freez-windy-1800.herokuapp.com', I want to be able to dynamically get this within my clojure code.
I know that I can look at the incoming request to figure this out, but ideally, I'd like to have some sort of 'setting' where I evaluate an expression once and save the value in a variable that I can then use (coming from the Python/Django world, I'm thinking of the settings.py equivalent in Clojure).
For reference, the code I'm deploying is available at https://github.com/rmanocha/cl-short.
You could set an environment variable in Heroku by
heroku config:add BASE_IRI=http://freez-windy-1800.herokuapp.com
and read it back in Clojure
(defn- base-iri []
(or (System/getenv "BASE_IRI") "http://localhost/"))
Heroku already sets the PORT you can use
(defn -main []
(let [port (Integer. (or (System/getenv "PORT") 8080))]
(run-jetty #'app {:port port})))
Works for me in different environments.
You would typically do this with InetAddress from the Java standard library.
(.getCanonicalHostName (java.net.InetAddress/getLocalHost))
This, however, does not do a DNS lookup.
3 ways to get the hostname. Use as you wish.
(ns service.get-host-name
(require [clojure.java.shell :as shell]
[clojure.string :as str])
(:import [java.net InetAddress]))
(defn- hostname []
(try
(-> (shell/sh "hostname") (:out) (str/trim))
(catch Exception _e
(try
(str/trim (slurp "/etc/hostname"))
(catch Exception _e
(try
(.getHostName (InetAddress/getLocalHost))
(catch Exception _e
nil)))))))

Resources