Clojurescript Self Compile of def - compilation

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

Related

Is it possible to create a component-local atom being asynchronously reset without an infinite loop?

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.

wrap-params (cljs-ajax + compojure)

Consider the following compojure routing:
(defroutes main-routes
(POST "/something" r {:body (prn-str (:params r))}))
(def handler
(-> main-routes
(wrap-params)))
When testing this with curl I'm getting the desired result:
curl -d "a=b" localhost:3000/something
{"a" "b"}
The Post parameters are read by compojure and wrapped to the params-map.
However this does not work with an ajax request initiated by the cljs-ajax library:
(POST "/something" {:handler #(js/alert %)
:params {"a" "b"}})
It alerts "{}". When changing the code to use GET, it works however. I guess this is due to the fact, that the browser sends the body as an input stream and not as plain text. But I'm not sure and I don't know how to fix this.
It looks like cljs-ajax is sending a transit-formatted request and response by default. (See :format and :response-format defaults here). You might try specifying an explicit json response in the request map -
{:handler #(js/alert %)
:params {"a" "b"}
:response-format :json}

Change 'got' message for rspec's composable matchers

I've written a composable matcher called have_backtrace/error_with_backtrace, like so:
RSpec::Matchers.define :have_backtrace do |expected_backtrace|
match do |error|
error.backtrace == expected_backtrace
end
failure_message do |error|
"expected error to have backtrace #{expected_backtrace.inspect}, actually got #{error.backtrace.inspect}"
end
end
RSpec::Matchers.alias_matcher :error_with_backtrace, :have_backtrace
Then I can use it like this:
callback = double('reject_callback').as_null_object
# Do some stuff which could call callback.call
expect(callback).to have_received(:call).with(error_with_backtrace(caller))
This works fine, however the error message isn't exactly what I want
expected: (error with backtrace "backtrace_line_1", "backtrace_line_2", ... )
got: (#<RuntimeError: RuntimeError>)
I would like the 'got' part to also say (error with backtrace ...)
Is there any way of accomplishing this?

Understanding how parameters work in Ruby

Here is a sample code from a RSpec code:
describe Thing do
def create_thing(options)
thing = Thing.new
thing.set_status(options[:status])
thing
end
it "should do something when ok" do
thing = create_thing(:status => 'ok')
thing.do_fancy_stuff(1, true, :move => 'left', :obstacles => nil)
...
end
end
So my confusion is mostly on this line:
thing.set_status(options[:status])
So create_thing method has an "option" parameter then we are passing status part of that parameter? Can someone explain this syntax in some easier words?
options is just a variable. The part you need to understand is this part
thing = create_thing(:status => 'ok')
You are basically passing a Hash to create_thing and therefore options is a hash. Then you can access the value of the status key by doing options[:status].
If the above mentioned line looked like this
thing = create_thing("Foo")
options would be "Foo" and you could get an error trying to do something like options[:status]
create_thing takes an argument called options.
Options is expected to be a hash (most likely).
You're passing the hash value with the key (a symbol):option to the set_status method.
You've passed an implicit hash to create_thing:
create_thing({ status: 'ok' }) is the same as
create_thing(status: 'ok') is the same as
create_thing(:status => 'ok')
Any way you call it, you access that value via options[:status].

How can I pass function parameters to an erb view with sinatra?

I currently have this:
get '/myapp/get/:func' do
erb :server, :locals => {:func => params[:func]}
end
And then in my server.erb file I have this:
if (func == "myFunc1")
myFunc1
elsif (func == "myFunc2")
myFunc2
etc...
The ruby functions called in server.erb are defined.
Now I want to define a new function, and I want to pass a variable to it. So what I want in my server.erb is this:
def myNewFunc(param1)
# do stuff with param1
end
How do I pass param1 to sinatra?
Note: The parameter I want to pass in is just an integer between 0 and 6.
You don't have to pass params as locals, you can se them anywhere in your code – if that is what you mean.

Resources