I am creating a third-party library for Clojure. In the core of this library I need to run script from the operation system. In this case I cannot use absolute path to this file like this /Users/me/src/clojure-pymorphy/src/pymorphy/pymorphy.py.
But if I use relative path script/script.py - than sometimes location of the script could be determined as wrong. It depends on the directory from which I run/use this clojure library. Because this library will be distributed as the third-party I need the way to determine the path of script.py from Clojure source code.
Project folders looks like this:
clojure-pymorphy/
src/
pymorphy/
pymorphy.py
pymorphy.clj
Script pymorphy.py is invoked from pymorphy.clj. File pymorphy.clj begins from this lines:
(ns pymorphy
(:import (java.util ArrayList)
(jep Jep)))
I've tried to find out is there a way to determine directory to pymorphy namespace at runtime and just simply add "pymorphy/pymorphy.py" to it. But after many hours of googling I come to conclusion that it's not possible.
So is there any other ways to dynamically determine path to clojure namespace at runtime?
Many thanks.
While looking at the similar questions at stackoverflow I find the solution:
(.getFile (clojure.java.io/resource "pymorphy/pymorphy.py"))
If you to run this script, so this will be something like - this will copy script to temp file, and run it. You need to create file, because if you'll pack everything into jar, then interpreter won't be able to read file..
(let [tmp (File/createTempFile "tmp" "py")
script (clojure.java.io/resource "pymorphy/pymorphy.py")]
(try
(when (and script (.exists tmp))
(with-open [os (.openStream script)]
(clojure.java.io/copy os tmp))
(run-script....))
(finally
(when (.exists tmp)
(.delete tmp)))))
Related
I am following this tutorial: https://practicalli.github.io/blog/posts/web-scraping-with-clojure-hacking-hacker-news/ and I have had a hard time dealing with the :require part of the ns macro. This tutorial shows how to parse HTML and pull out information from it with a library called enlive, and to use it, I first had to put
...
:dependencies [[org.clojure/clojure "1.10.1"]
[enlive "1.1.6"]]
...
in my project.clj, and require the library in core.clj as the following:
(ns myproject.core
(:require [net.cgrand.enlive-html :as html])
(:gen-class))
I spent so much time finding out the name net.cgrand.enlive-html, since it was different from the package's name itself (which is just enlive), and I couldn't find it through any of the lein commands (I eventually found it out by googling).
How can I easily find out what name to require?
Practical approach
If your editor/IDE helps with auto-completion and
docs, that might be a first route.
Other than that, libraries usually have some read-me online, where they show off
what they do (what to require, how to use that).
Strict approach
If you really have nothing about a library, you will find the downloaded
library in you ~/.m2/repository directory. Note that deps without the naming
convention of "group/artifact" will just double on the artifact name, Next is
the version. So you can find your libraries JAR file here:
.m2/repository/enlive/enlive/1.1.6/enlive-1.1.6.jar.
JAR files are just ZIP-Files. Inside the JAR file you will usually find the
source files of the library. The directory structure reflects the package
structure. E.g. one file there is net/cgrand/enlive_html.clj (note the use
of the _ instead of -, this is due to name munging for the JVM). You then
can require the file in your REPL and explore with doc or dir etc. Or you
open this file, to see the docs and source code in one chunk.
Usually I get this from the documentation / tutorial for the library.
https://github.com/cgrand/enlive Check out the Quick Tutorial, which starts with the needed require.
A problem has traped me for quite a while when I'm exploring Clojure. I try to generate a class by invoking the compile function in REPL on a Clojure script as below:
(ns mylib.DirLister (:gen-class))
(defn -listDir [this path]
(->> path java.io.File. .listFiles (map #(.getName %))))
I saved this script to e:/temp/clj/src/mylib/DirLister.clj. The following session goes well when I specify the relative paths for the -cp option, i.e., classes are successfully generated in the classes path:
e:\temp\clj>java -cp .\src;.\classes;d:/tools/clojure-1.4.0/clojure-1.4.0.jar clojure.main
Clojure 1.4.0
user=> (compile 'mylib.DirLister)
mylib.DirLister
user=>
But when I use absolute paths, there prompt a "No such file or directory" error:
e:\>java -cp e:/temp/clj/src;e:/temp/clj/classes;d:/tools/clojure-1.4.0/clojure-1.4.0.jar clojure.main
Clojure 1.4.0
user=> (compile 'mylib.DirLister)
CompilerException java.io.IOException: No such file or directory, compiling:(mylib/DirLister.clj:1)
user=>
How come ? I mean why the absolute paths don't work but the relative paths do.
I know I could go with leiningen. but since I'm studying Clojure, I want to understand the stuff underneath before adopting this full-featured tool.
Looks to me like you have a mixture of forward-slash and backward-slashes on the command line. I'm not sure exactly what shell you are using and whether that could make a difference. But, a path with e: in it would normally require a back-slash '\'. Maybe this will help, or at least remove one potential source of problems?
Could you try to change the 2nd example to use
e:\temp\clj\src;e:\temp\clj\classes;d:\tools\clojure-1.4.0\clojure-1.4.0.jar
to see if that helps?
Finally I got the answer:
the compile function will always use the value of the built-in variable *compile-path* as the path of the output classes, which defaulted to "classes" under the current directory if you didn't set it otherwise. So the problem I came across is not really about relative or absolute path, it is about the current directory and the value of *compile-path* .
Refer to ClojureDoc .
Assume there is two files inside my clojure project, one clj and the other is txt.
Is there a way to know the path (as a string) of the txt file from the clj file?
There is:
(System/getProperty "user.dir")
or
(-> (java.io.File. ".") .getAbsolutePath)
But this gives where the current directory. The one that includes the clj file, the one the code is written in.
But how to know the path of the txt file?
The purpose is to write into this txt file from the clj file.
Thank you.
In Java and therefore Clojure you can find files on the CLASSPATH. For example, in Java it is common to put things like log4j.properties at the top of your CLASSPATH (e.g., in the classes directory) and then you can reference the file in your Clojure (or Java) code with:
(java.io.File. "log4j.properties")
Are you using and running your app with Leiningen? If so, you can create a directory at the top level and put files there. For example, if you have a config file you can have a "conf" dir with a properties files:
my-lein-proj$ ls
conf doc project.clj README.md src target test
Suppose you put a myproj.conf file in the conf directory and you want to read from it in your Clojure code. Then you can just do:
(slurp "conf/myproj.conf")
The Clojure library local-file allows you to get your current project's directory with local-file/project-dir. As long as you know where in your project the file you want to access is, you should be able to find it this way.
This gives where the current clj file, the one that this code is
written in.
No, it doesn't.
It gives the current directory.
Did you take into account that one can run clojure scripts that are not in the current directory?
if your file structure is something similar to this:
config | src | target | test
you have a config inside config directory, in our case let's assume its java.config and you are trying to read this file inside core.clj in src directory you can also use clojure.java.io/reader method
for example:
(clojure.java.io/reader "config/java.config")
you can run below commands in the repl to see the contents of the file
(slurp (clojure.java.io/reader "config/java.config")
if you are interested in reading the contents of the file line by line you combine above function with with-open method and read line by line:
(with-open [word (clojure.java.io/reader "config/java.config")]
(loop [c (.read word)]
(if (not= c -1)
(do
(print (char c))
(recur (.read word))))))
I'm trying out clojure on my second day and I don't understand almost anything yet. I am working with the Programming Clojure 2nd ed. and I am stuck with libraries.
I have Leiningen and have the REPL running. The book first tells the reader to run a simple
(require 'clojure.java.io)
which works just fine (I get a nil). Then it wants to load a file called introduction.clj by running another simple
(require 'examples.introduction)
where I get an error message
FileNotFoundException Could not locate clojure/java/introduction__init.class
or clojure/java/introduction.clj on classpath: clojure.lang.RT.load (RT.java:432)
I downloaded the introduction.clj file and looked where should I place it. The error and the book says the command will search in my classpath, but I have no idea where or what that is (after 1h of searching and reading I still don't get it, sorry). I ran a few commands and I had many classpaths listed (from which none contain a clojure/java/io.clj).
So I tried another approach - find the io.clj file on my disk and simply copy the file there and run it with a command
(require 'clojure.java.introduction)
This doesn't seem to work either. By the way, the io.clj file I found was in "C:\Program Files\clojure\src\clj\clojure\java". I tried running several other .clj files from the java folder as well from the clojure folder, like javadoc.clj or inspector.clj and all seem to work just fine with the above mentioned command. Only the new file doesn't seem to load this way.
Any help appreciated :)
Clojure runs on the Java Virtual Machine, so you will need to learn a bit about PATH and CLASSPATH concepts:
See: http://docs.oracle.com/javase/tutorial/essential/environment/paths.html
Regarding the error message, the Clojure runtime is expecting to find introduction.clj in the directory clojure\java\example\introduction.clj (not where it really should be - see below).
The convention for Clojure namespaces is that the last component is the file name, while any previous components are parent directories. So
clojure.java.introduction
would have to be in the directory (relative to your source "root" or classpath)
clojure\java\introduction.clj
(The lein REPL automatically adds your source root to the classpath).
Another concept you need to understand is where the "root" of your source code is located. For Leiningen (the build tool you are using) the default is either "src" or "src/main/clojure" - as documented in the Leiningen sample project file on GitHub).
Finally, if you get really stuck, it seems the complete project for the book is available on GitHub.
Looking at the project, I see that you should actually be placing the file under src\examples\introduction.clj
Are you reading the book "Programming Clojure"?
I have encountered the same problem. It ban be sovled as follows:
If you start clojure by java:
I work in windows, the clojure.jar is placed in D:\backup\clojure-1.5.1, and the source code of the book "Programming Clojure" is placed in D:\study\clojure\shcloj-code\code. You should first delete the user.clj file in folder D:\study\clojure\shcloj-code\code.
java -cp d:\backup\clojure-1.5.1\clojure-1.5.1.jar;d:\study\clojure\shcloj-code\code clojure.main -r
If you work in linux, replace the ";" with ":"
If you start clojure by lein
You should first cd to the D:\study\clojure\shcloj-code\code folder, and then
lein repl
You should also delete the user.clj file in folder D:\study\clojure\shcloj-code\code.
Say I made a change to a Clojure library (eg. added a parameter to the request-token in clj-oauth) and want to use that changed library in my project. What's the best way to do this, short of compiling the new library as a JAR and copying that to my project lib?
I want to be able to tweak the library and my project at the same time (preferably in the REPL). If I were doing this in Ruby, I would download and 'require' the gem, then reopen that class in my own project source and add or override the methods as needed.
You can hack directly at the REPL. Suppose you've got incanter on your classpath.
Start a REPL. The first thing we need to do is bring the incanter classes into it.
user> (require 'incanter.core)
nil
Now we can see the function incanter.core/matrix?
user> (incanter.core/matrix? 2)
false
We can look at the original source code:
user> (require 'clojure.repl)
nil
user> (clojure.repl/source incanter.core/matrix?)
(defn matrix?
" Test if obj is 'derived' incanter.Matrix."
([obj] (is-matrix obj)))
nil
Let's go and screw it up:
First change to the incanter.core namespace:
user> (in-ns 'incanter.core)
#<Namespace incanter.core>
Then we can redefine it, using the old source code as a crib:
incanter.core> (defn matrix? [obj] "hello")
#'incanter.core/matrix?
Unit test:
incanter.core> (matrix? 2)
"hello"
Switch back to the user namespace:
incanter.core> (in-ns 'user)
#<Namespace user>
Try it out:
user> (matrix? 2)
; Evaluation aborted.
There is no definition of user/matrix. We redefined it in the incanter.core namespace.
user> (incanter.core/matrix? 2)
"hello"
For experimenting at the repl, it's ok just to change source files and re-compile the single file (C-C C-k in emacs), or if you're in the right namespace, just re-evaluate the definition.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Now, if we want to make our valuable change permanent and available to other projects, it depends on how everything is set up.
I use maven for dependency management, so it would be a question of modifying the source file, and then re-running the build process for the library to compile the new version and install it into the local maven repository.
With a maven project, that should be as simple as
$ mvn install
A note about version numbers:
If you do make permanent modifications and use dependency management to coordinate the differences, then you should change the version number of your library, from maybe 1.2.0 to 1.2.0-johnshack-SNAPSHOT, or something that is unlikely to collide with the real thing when you want to use an unperverted version in another project. You wouldn't want a modified version finding its way into projects where it isn't welcome.
Then you modify your own project files to make sure that you use the hacked version where you want to, and the next time you start your repl, it should pull in the last hack that you installed.
You will need to reinstall again every time you want your changes to make their way into the repository, but that's actually probably a good thing.
Unfortunately, (and it was at this point that I started to wish that I had chosen a different example) Incanter turns out to be a leiningen project which is split into sub-modules in an ad-hoc scripty sort of way, so we need to figure out how it expects to be installed. The figuring out turned out to be quite hard, although the answer is easy. Leiningen sets my hair on fire.
You can get incanter's source here:
$ git clone http://github.com/liebke/incanter.git
and the relevant source file is:
~/incanter/modules/incanter-core/src/incanter/core.clj
Modify it to break the matrix? function, and then it turns out that what you have to do is:
Change the version numbers in both the top level project.clj, and also in the submodule project.clj.
Then you run lein install in the incanter-core directory, and then again in the top-level directory, and you have to do it in that order. I don't quite understand why.
At the moment all this seems needlessly complicated. I'm (fairly) sure that it will settle down as the tools mature.
If you're using (or wouldn't mind using) cake, check out the subproject dependencies section of the README. I think it might be exactly what you're looking for.
You upload it to clojars under a different name depend on that.