I am making a simple Clojure web-app to be deployed on Heroku consisting of one html and one css file. I created the file using the "lein new heroku MYAPP" command and am trying to modify it from a simple "hello world" to have it render an html file in another folder on startup. I have managed to get the html to load on a local host in my browser, but it is not being modified by the css when I do it. What do I need to change to get the css to modify the html to have it render properly in the browser and then deploy to heroku?
html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<img id="sun" src="http://goo.gl/dEEssP">
<div id='earth-orbit'>
<img id="earth" src="http://goo.gl/o3YWu9">
</div>
</body>
</html>
project.clj
(defproject solar_system "1.0.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://solar_system.herokuapp.com"
:license {:name "FIXME: choose"
:url "http://example.com/FIXME"}
:dependencies [[org.clojure/clojure "1.5.1"]
[compojure "1.1.1"]
[ring/ring-jetty-adapter "1.1.0"]
[ring/ring-devel "1.1.0"]
[ring-basic-authentication "1.0.1"]
[environ "0.2.1"]
[com.cemerick/drawbridge "0.0.6"]]
:uberjar-name "solar_system-standalone.jar"
:min-lein-version "2.0.0"
:plugins [[environ/environ.lein "0.2.1"]]
:hooks [environ.leiningen.hooks]
:profiles {:production {:env {:production true}}})
web.clj:
(ns solar_system.web
(:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
[compojure.handler :refer [site]]
[compojure.route :as route]
[clojure.java.io :as io]
[ring.middleware.stacktrace :as trace]
[ring.middleware.session :as session]
[ring.middleware.session.cookie :as cookie]
[ring.adapter.jetty :as jetty]
[ring.middleware.basic-authentication :as basic]
[cemerick.drawbridge :as drawbridge]
[environ.core :refer [env]]))
(defn- authenticated? [user pass]
;; TODO: heroku config:add REPL_USER=[...] REPL_PASSWORD=[...]
(= [user pass] [(env :repl-user false) (env :repl-password false)]))
(def ^:private drawbridge
(-> (drawbridge/ring-handler)
(session/wrap-session)
(basic/wrap-basic-authentication authenticated?)))
(defroutes app
(ANY "/repl" {:as req}
(drawbridge req))
(GET "/" []
{:status 200
:headers {"Content-Type" "text/html"}
:body (slurp (io/resource "index.html"))})
(ANY "*" []
(route/not-found (slurp (io/resource "404.html")))))
(defn wrap-error-page [handler]
(fn [req]
(try (handler req)
(catch Exception e
{:status 500
:headers {"Content-Type" "text/html"}
:body (slurp (io/resource "500.html"))}))))
(defn -main [& [port]]
(let [port (Integer. (or port (env :port) 5000))
;; TODO: heroku config:add SESSION_SECRET=$RANDOM_16_CHARS
store (cookie/cookie-store {:key (env :session-secret)})]
(jetty/run-jetty (-> #'app
((if (env :production)
wrap-error-page
trace/wrap-stacktrace))
(site {:session {:store store}}))
{:port port :join? false})))
;; For interactive development:
;; (.stop server)
;; (def server (-main))
here is the directory tree. The html and css files are in resources (with the error.html files), web.clj is in src/solar_system and project.clj is in the root folder:
solar_system
├── resources
├── src
│ └── solar_system
├── target
│ ├── classes
│ └── stale
└── test
└── solar_system
I don't know why the lein new heroku _ template doesn't do this for you.
Use compojure.route/resources to tell the handler where to look for static files.
(defroutes app
(ANY "/repl" ...)
(GET "/" [] ...)
(route/resources "/")
(ANY "*" [] ...))
Now, if you visit http://example.com/style.css, it will expect resources/public/style.css.
Aside: It's better to serve static assets from resources/public/ rather than resources/ because you may want to have resources/secrets.txt without anybody being able to access it.
You have a css file, but no route to serve it! Your browser is begging for a CSS file, but the server says "404, man, never heard of that file." You can either add another route like the one for index.html, or you can use compojure's resources or files route to serve all files in a directory.
As the others have said, you're missing a route to your css file.
(defroutes app
(ANY "/repl" {:as req}
(drawbridge req))
(GET "/" []
{:status 200
:headers {"Content-Type" "text/html"}
:body (slurp (io/resource "index.html"))})
(route/resources "/") ; special route for serving static files like css
; default root directory is resources/public/
(ANY "*" []
(route/not-found (slurp (io/resource "404.html")))))
http://my-website.com/style.css should display your css file. If it doesn't, there's something wrong with your routes or your file isn't there. It expects resources/public/style.css. Make sure you are restarting your app and doing a cacheless refresh in your browser (shift F5 in most instances) to make sure there's nothing weird giving you weird results.
Related
I want to create a reagent component that has content that is changed by the handler of an ajax request. Using a local atom is yielding an infinite loop GETting the resource and updating the atom, thereby triggering the re-rendering of the component.
Example:
(ns example
(:require
[reagent.core :as r]
[ajax.core :refer [GET POST]]))
(defn get-data []
(let [ret (r/atom "")]
(GET "/ajax/" {:handler (partial reset! ret)})
ret))
(defn page []
(let [data #(get-data)]
[:div data]))
(defn init! []
(r/render [#'page] (.-body js/document)))
A "global" atom will work, as it is not recreated on each rerender:
(ns example
(:require
[reagent.core :as r]
[ajax.core :refer [GET POST]]))
(def global-atom (r/atom ""))
(defn get-data []
(let [ret global-atom]
(GET "/ajax/" {:handler (partial reset! ret)})
ret))
(defn page []
(let [data #(get-data)]
[:div data]))
(defn init! []
(r/render [#'page] (.-body js/document)))
Is a global def the only possible solution, or is there a way to solve this problem with a local atom?
I believe that if you use the second form of reagent view, it should not trigger itself:
(defn page []
(let [data (get-data)]
(fn []
[:div #data])))
If a view function returns a function, that function becomes the render function of the React component. Otherwise the view function itself becomes render.
Well, basically I'm facing a problem just on Windows, when I run "boot run dev" on macOS or Linux, it works perfectly, but on Windows, the dream is over, the boot starts but get an error soon as it starts, this is my build.boot:
(set-env!
:source-paths #{"src"}
:resource-paths #{"src" "resources"}
:dependencies '[[org.clojure/clojure "1.9.0" :scope "provided"]
[adzerk/boot-cljs "2.1.4" :scope "test"]
[adzerk/boot-reload "0.5.2" :scope "test"]
[pandeiro/boot-http "0.8.3" :scope "test"]
[nightlight "RELEASE"]
[org.clojure/clojurescript "1.9.946"]
[rum "0.10.8"]
[org.roman01la/cljss "1.5.13"]
[org.clojure/core.async "0.4.474"]
[io.replikativ/konserve "0.4.11"]
[ring/ring-core "1.6.3"]
[bidi "2.1.2"]
[congomongo "0.5.0"]
[cljs-http "0.1.44"]
[http-kit "2.2.0"]
[com.hypirion/clj-xchart "0.2.0"]])
(require
'[adzerk.boot-cljs :refer [cljs]]
'[adzerk.boot-reload :refer [reload]]
'[pandeiro.boot-http :refer [serve]]
'[nightlight.boot :refer [nightlight sandbox]])
(task-options!
aot {:namespace #{'brad.server}}
pom {:project 'brad
:version "0.1.0"
:description "FIXME: write description"
:url "http://example/FIXME"
:scm {:url "https://github.com/yourname/brad"}
:license {"Eclipse Public License"
"http://www.eclipse.org/legal/epl-v10.html"}}
jar {:main 'brad.server
:file "brad.jar"}
cljs {:ids #{"brad/admins" "brad/fisicos"}})
(deftask dev []
(comp
(watch)
(reload :asset-path "brad"
:cljs-asset-path ".")
(sandbox :file "java.policy")
(cljs :source-map true
:optimizations :none)
(target)))
(deftask run []
(comp
(serve :dir "target/brad" :port 3000)
(dev)
(nightlight :port 4000 :url "http://localhost:3000")))
(deftask build []
(comp
(cljs :optimizations :advanced
:compiler-options {:fn-invoke-direct true})
(aot)
(pom)
(uber)
(jar)
(target)))
Sorry about the extensive code, I think that maybe the problem is with some permission that I didn't give, I was trying to run boot on git bash, then I tried on windows powershell, the same error was ocurred, please, help me, and I'm sorry about my english.
There are known issues on Windows, please try on Windows 10.
I have an endpoint where I can upload a text file with curl like this:
curl -X POST -H "Content-Type: multipart/form-data" -F "file=#/resources/speciesDiffusion.tree" http://localhost:4000/continuous/tree
now I need to send a similar request from a browser, but
(ajax/ajax-request
{:uri (str "http://localhost:4000" "/continuous/tree")
:method :post
:params {:treefile file}
:handler #(println %1)
:format (ajax/text-request-format)
:response-format (ajax/json-response-format {:keywords? true})})
gives me a (nicely json converted, so I got that part going, which is nice) error response:
[false {:status 500, :status-text , :failure :error, :response {:timestamp 1494279686227, :status 500, :error Internal Server Error, :exception org.springframework.web.multipart.MultipartException, :message Current request is not a multipart request, :path /continuous/tree}}]
Also, in the browser I can see that the content-type headers is not correctly set, but I couldn't get it to work with any other combination of :format and :params.
There are some examples in the README of the cljs-ajax project. For example:
(let [form-data (doto
(js/FormData.)
(.append "id" "10")
(.append "file" js-file-value "filename.txt"))]
(POST "/send-file" {:body form-data
:response-format (raw-response-format)
:timeout 100}))
https://github.com/JulianBirch/cljs-ajax
As per my comment the problem was not in the request, but rather in the f-tion that dispatched it, i.e. I was reading the file content instead of sending the raw object like here:
(defn handle-file-upload [evt]
(let [target (.-currentTarget evt)
js-file (-> target .-files (aget 0))]
(do
(re-frame/dispatch [:post-file {:file js-file
:name (.-name js-file)}])
(set! (.-value target) ""))))
(defn upload-button []
(fn []
[:input {:class "none"
:id "file-upload"
:type "file"
:on-change #(handle-file-upload %)}]))
where
:post-file
is an event which invokes the handler which does the POST request.
Clojurescript 1.7 now supports self compilation (see here). I can compile code as follows
(ns self-compile.core
(:require cljs.js))
(enable-console-print!)
(set! cljs.js/*eval-fn* cljs.js/js-eval)
(def state (cljs.js/empty-state))
(cljs.js/eval-str state "(+ 1 2)"
(fn [response] ...))
This works fine for most code, except (def a 3) which gives the error #error {:message "ERROR", :data {:tag :cljs/analysis-error}, :cause #object[TypeError TypeError: Cannot set property 'a' of undefined]}.
How can I fix the setup?
I had the same issues, here's a few things I had to do go get it working:
Pass options to eval-str, you may have to pass options like:
:context :expr
:def-emits-var true
:ns 'cljs.user
Try evaling (ns cljs.user) first before evaling the def.
That should fix it.
See https://github.com/cljsinfo/cljs-api-docs/blob/catalog/refs/cljs.js_eval-str.md
I can deploy the sample heroku clojure webapp as described here. However a custom webapp, running fine locally, crashes on access.
heroku logs:
2013-11-28T02:01:57.142302+00:00 heroku[web.1]: State changed from crashed to starting
2013-11-28T02:01:57.124843+00:00 heroku[web.1]: Process exited with status 1
2013-11-28T02:02:02.579325+00:00 heroku[web.1]: Starting process with command `lein with-profile production trampoline run`
2013-11-28T02:02:03.366402+00:00 app[web.1]: Picked up JAVA_TOOL_OPTIONS: -Djava.rmi.server.useCodebaseOnly=true
2013-11-28T02:02:05.136478+00:00 app[web.1]: That's not a task. Use "lein help" to list all tasks.
2013-11-28T02:02:06.366976+00:00 heroku[web.1]: Process exited with status 1
2013-11-28T02:02:06.377083+00:00 heroku[web.1]: State changed from starting to crashed
I can't see heroku config listing JAVA_TOOL_OPTIONS either. What am i missing?
project.clj:
(defproject xxx "0.1.0"
:warn-on-reflection false
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/tools.nrepl "0.2.3"]
[ring "1.2.1"]
[ring/ring-jetty-adapter "1.1.6"]
[compojure "1.1.6"]
[enlive "1.1.4"]
[ring/ring-devel "1.1.0"]
[ring-basic-authentication "1.0.1"]
[com.cemerick/drawbridge "0.0.6" :exclusions [org.clojure/tools.nrepl]]
[environ "0.4.0"]]
:plugins [[lein-ring "0.8.8"]
[lein-environ "0.4.0"]]
:main xxx.web)
web.clj:
(ns xxx.web
(:require [compojure.core :refer [defroutes GET PUT POST DELETE ANY]]
[compojure.handler :refer [site]]
[compojure.route :as route]
[clojure.java.io :as io]
[ring.middleware.stacktrace :as trace]
[ring.middleware.session :as session]
[ring.middleware.session.cookie :as cookie]
[ring.adapter.jetty :as jetty]
[ring.middleware.basic-authentication :as basic]
[cemerick.drawbridge :as drawbridge]
[ring.middleware.params :as params]
[ring.middleware.keyword-params :as keyword-params]
[ring.middleware.nested-params :as nested-params]
[ring.middleware.session :as session]
[ring.middleware.basic-authentication :as basic]
[environ.core :refer [env]]
[xxx.templates :as templates]))
(defn- authenticated? [user pass]
;; TODO: heroku config:add REPL_USER=[...] REPL_PASSWORD=[...]
(= [user pass] [(env :repl-user false) (env :repl-password false)]))
(def ^:private drawbridge
(-> (drawbridge/ring-handler)
(session/wrap-session)
(basic/wrap-basic-authentication authenticated?)))
(defroutes app
(ANY "/repl" {:as req}
(drawbridge req))
(GET "/" []
{:status 200
:headers {"Content-Type" "text/html"}
:body (templates/index "Hello.") })
(ANY "*" []
(route/not-found (slurp (io/resource "404.html")))))
(defn wrap-error-page [handler]
(fn [req]
(try (handler req)
(catch Exception e
{:status 500
:headers {"Content-Type" "text/html"}
:body (slurp (io/resource "500.html"))}))))
(def drawbridge-handler
(-> (cemerick.drawbridge/ring-handler)
(keyword-params/wrap-keyword-params)
(nested-params/wrap-nested-params)
(params/wrap-params)
(session/wrap-session)))
(defn wrap-drawbridge [handler]
(fn [req]
(let [handler (if (= "/repl" (:uri req))
(basic/wrap-basic-authentication
drawbridge-handler authenticated?)
handler)]
(handler req))))
(defn -main [port]
(let [port (Integer. (or port (System/getenv "PORT")))]
;(jetty/run-jetty #'app {:port port :join? false})))
(jetty/run-jetty (wrap-drawbridge app) {:port port :join? false})))
You're probably using a different version of Leiningen locally than # Heroku.
From Heroku doc:
Leiningen 1.7.1 will be used by default, but if you have :min-lein-version "2.0.0" in project.clj (highly recommended) then the latest Leiningen 2.x release will be used instead.