I am learning common lisp and trying to use hunchentoot to develop web apps.
With the code below I cannot manage to see the page defined in the retro-games function definition on the browser. I expect it to be generated by this function.
I write address as:
http://localhost:8080/retro-games.htm.
What displayed on the browser is Resource /retro-games.htm not found, the message and the lisp logo at the default page, that I could display. I can display the hunchentoot's default page.
(ql:quickload "hunchentoot")
(ql:quickload "cl-who")
(defpackage :retro-games
(:use :cl :cl-who :hunchentoot))
(in-package :retro-games);i evaluate this from toplevel otherwise it does not change to this package.
(start (make-instance 'hunchentoot:acceptor :port 8080))
(defun retro-games ()
(with-html-output (*standard-output* nil :prologue t)
(:html (:body "Not much there"))
(values)))
(push (create-prefix-dispatcher "/retro-games.htm" 'retro-games) *dispatch-table*)
The two loadings at the beginning were successful.
What am I missing?
Hunchentoot's API has changed a bit since that was written. The behaviour of an acceptor assumed by that article is now found in easy-acceptor. Acceptor is a more general class now, which you can use for your own dispatch mechanism, if you are so inclined.
So, instead of (make-instance 'hunchentoot:acceptor #|...|#), use (make-instance 'hunchentoot:easy-acceptor #|...|#), and it should work.
Default implementation of the acceptor's request dispatch method, generates an HTTP Not Found error. So, you need to subclass acceptor class and redefine acceptor-dispatch-request method in subclass to make it actually dispatch requests. For example see documentation.
easy-acceptor works because it defines acceptor-dispatch-request to use *dispatch-table* for routing.
Related
I’ve got a lein plugin that manually runs my clojure.test code. It declares a dynamic variable baseuri that I wish to access from within my tests. I’ll strip out and change the code to get straight to the point. Here, inside of my plugin, I have a config file that creates the dynamic baseuri variable and sets it to a blank string.
;; myplugin
;; src/myplugin/config.clj
(ns leiningen.myplugin.config)
(def ^:dynamic baseuri "")
A task from within the plugin sets the dynamic baseuri variable and runs tests with clojure.test:
;; src/myplugin/runtests.clj
(ns leiningen.myplugin.runtests
(:require [leiningen.myplugin.config :as config]
[clojure.test]
[e2e.sometest]))
(defn run [project]
(binding [config/baseuri "https://google.com/"]
(println config/baseuri) ;; <-- prints google url
;; run clojure.test test cases from e2e.sometest namespace
;; This will call the `sampletest` test case
(clojure.test/run-tests e2e.sometest)
))
And inside of my clojure.test I try to use the baseuri variable but the binding doesn’t hold. It’s value is what I originally declared baseuri to be (an empty string)
;; tests/e2e/sometest.clj
(ns e2e.sometest
(:require [leiningen.myplugin.config :as config]))
(deftest sampletest
(println config/baseuri)) ;; <-- Prints an empty string instead of google url
I've edited the code to show in a basic manner how the clojure.test cases are run. I simply pass in the namespace I want to be run to the clojure.test/run-tests method.
I agree that clojure.test implementation is not optimal when it comes to parameterising your tests.
I am not sure why your binding form doesn't work - I have checked the code in clojure.test and I cannot see what could be wrong. I would check if:
the tests get executed in the same thread as the binding is established (maybe you could add logging the thread name/id in your plugin and in your tests)
different class loaders causing that your plugin namespace and its global dynamic variable is actually loaded and defined twice
I have one more idea (and I really don't want to criticise your solution, just trying to find alternative solutions :)): your problem is to pass a global configuration options to your code under test from external sources like test scripts configuration. Have you thought about passing them as environment variables? You could easily read them using (System/getenv "baseuri") or environ.
Maybe you have a dynamic var for very specific reasons, but, as you do not state so explicitly, I take a shot here.
Avoid dynamic rebinding of vars. At its best case, avoid global state at all, instead redefine your functions to take the baseuri as a parameter.
Or refactor your application to not have the need for static vars at all, like you have it right now.
EDIT My guess is that your functions:
(defn run [project]
(binding [config/baseuri "https://google.com/"]
(println config/baseuri) ;; <-- prints google url
;; runs clojure.test code here …
))
(deftest sampletest
(println config/baseuri))
are not connected in any way. At least I dont see how they should be. You are running a test and print some other var without rebinding it.
Maybe you could add a link to a repo to a minimal reproducable testcase to understand it better?
I play with Rook (https://portal.aviso.io/#/document/open-source/rook/Current) for Clojure based REST services.
While the general layout of Rook is quite clear (implementing Verbs for different interfaces) I am not so clear if I grokked Rook's approach for POST bodies. I.e. implementing a POST, I can access the request body, yet I wonder if this is the intended way:
(defn create
[ ^:request requ]
(let [request-body (slurp (:body requ))]
...
))
As long as you are using the standard middleware, the contents of a body request is available deserialized as the value of the :body-params key of the request map.
I would expect (doc Math/exp) to give me something but it doesn't.
Sure it's not a Clojure function, but still there should be some way to reach the documentation.
Closest thing that I can get is (javadoc Math).
I would prefer the actual doc from that page as a string:
static double exp(double a)
Returns Euler's number e raised to the power of a double value.
UPD: signature by reflection
Here's how I can get a signature:
(.toString (first (filter #(= (.getName %) "exp") (.getMethods Math))))
I'm hoping for a similar way to get the doc.
UPD: no doc by reflection
According question How to read Javadoc comments by reflection?,
it's not possible to get the doc by reflection.
I wonder if it's possible to re-use some of Eclipse code that does give access to docs.
Simply start typing the method name that you intend to use:
(java.lang.Math/exp)
and with the cursor on the "exp" , press M-.
See the CIDER docs for more related hotkeys https://cider.readthedocs.io/en/latest/interactive_programming/
If you also install "company-mode", then searching for the class you intend to use also gets easier via autocomplete.
I wanted to write something like ((IStringer)object).ToString() (in C#) in Erlang. After some studying I've learnt that Elixir has something called Protocols that pretty much resembles the same thing of C# (in an inside-out manner). Then I came up with this idea/code in Erlang - which is nice enough to me like:
?stringer(my_val):to_string().
And it either returns the expected value or not_implemented atom!
But 2 questions:
1 - Why nobody use this or promote things based on stateful modules in Erlang? (OTP aside and from talking to some Erlangers they did not know that actually OTP is built around this! So really there is a need to change how Erlang is being taught and promoted. It's possible that I am confused.).
2 - Why I get this warning? That call actually can never fails.
The warning:
stringer.erl:18: Warning: invalid module and/or function name; this call will always fail
stringer.erl:19: Warning: invalid module and/or function name; this call will always fail
stringer.erl:20: Warning: invalid module and/or function name; this call will always fail
The code:
-module(stringer).
-export([to_string/1,sample/0]).
-define(stringer(V), {stringer, V}).
to_string({stringer, V}) when is_list(V) ->
to_string(V, nop);
to_string({stringer, V}) when is_atom(V) ->
to_string(V, nop);
to_string({stringer, _V}) ->
not_implemented.
to_string(V, _Nop) ->
Buffer = io_lib:format("~p",[V]),
lists:flatten(Buffer).
sample() ->
io:format("~p~n", [?stringer([1,2]):to_string()]),
io:format("~p~n", [?stringer(cute_atom):to_string()]),
io:format("~p~n", [?stringer(13):to_string()]).
And the output is:
"[1,2]"
"cute_atom"
not_implemented
I am doing this on Erlang R16 B2 (V5.10.3) 32 bit on Windows 8 64 bit.
The warning you see is an Erlang bug. If Erlang sees you are invoking a function in a literal tuple, it shows the warning. I have seen this while working with Elixir, I have silenced it in Elixir's compiler but forgot to report it to the Erlang team as a bug. Sorry.
The stateful module thing is actually avoided in Erlang by the majority of developers. They were added to support a feature called "parameterized modules", which has then since been removed, but the underlying dispatching mechanism still exists. If you search the Erlang Questions mailing list you can find plenty of discussion on the topic. Note that protocols in Elixir are not implemented like that though.
In fact, it seems your implementation does not seem to add anything compared to a regular function. For example, you could have simply written:
to_string(V) when is_list(V); is_atom(V) ->
Buffer = io_lib:format("~p",[V]),
lists:flatten(Buffer);
to_string(V) ->
not_implemented.
and called the function directly. Your implementation is simply using the classic ad-hoc polymorphism provided by Erlang at the end of the day. The limitation of this approach is that, since dispatch is hardcoded to ?stringer, the only way to extend the to_string/1 behaviour to work with a new data type is to reimplement and replace the whole stringer module.
Here is an example of an issue that helps you ponder about this: if application A defines a "protocol" named stringer, how can applications B and C extend this protocol to their own data types and all be used by application D without loss of functionality?
In very simple words, the way protocols work in Elixir is by making the stringer module an intermediate dispatch module. So the stringer module actually works like this:
to_string(V) when is_list(V) ->
string_list:to_string(V);
to_string(A) when is_atom(A) ->
string_atom:to_string(A);
%% ...
to_string(A) when is_tuple(A) ->
string_tuple:to_string(A).
and imagine that code is wrapped around something that checks if the module exists and fails accordingly if not. Of course, all of that is defined automatically for you by simply defining the protocol. There is also a mechanism (called consolidation) to compile protocols down to a fast dispatch mechanism on releases.
In my clojure code, I have a few functions which are created with calls to custom macros. Typically, the macros would take a data structure of some sort and create a method from it.
This is a contrived example:
(create-function {:name "view-data" ...})
which would create a new function called view-data. (My database queries are data-driven, so I can create a function with an indicative name that calls a specific query)
My problem is that when I run the mvn clojure:swank target and connect to the slime session from emacs these functions aren't visible. I have to visit the file and compile it myself with C-c C-k for the functions to be created.
The maven output suggests that the files themselves compile fine, but the slime session doesn't know about the functions.
Any ideas why this might be happening?
I have a file in my project that requires all the namespaces which makes all the functions from every where available in the repl. perhaps there is a more slime-elegant way of doing this, but this hack has been very reliable for me.
Note that in clojure, compiling and loading are separate steps. You can generate all the class files you like, but if they're not loaded, it won't affect the running process.
I don't know enough about clojure:swank for maven, but it sounds to me that, like leiningen, the swank target will only set up the classpath for your project and load the swank code but not any of the code in your project. So you will still have load load your code in some way after that (for instance; from Emacs/SLIME, using some other target/plugin or from the REPL).