Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New middleware: out.clj #257

Merged
merged 1 commit into from
Sep 23, 2015
Merged

New middleware: out.clj #257

merged 1 commit into from
Sep 23, 2015

Conversation

Malabarba
Copy link
Member

Automatically changes the root binding of out to print to any active
connections. An active connection is a session that has sent at least
one "eval" op and hasn't been closed.
We use an eval message, instead of the clone op, because there's no
guarantee that the message that sent the clone op will properly handle
output replies.

clojure-emacs/cider#1328

(defn eval-messages-watch [_ _ _ new-state]
(let [o (fork-out (vals new-state))]
;; FIXME: This won't apply to Java loggers unless we also setOut.
;; (System/setOut (PrintStream. o))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for postponing this part?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you can't directly convert a PrintWriter to a PrintStream.

@bbatsov
Copy link
Member

bbatsov commented Sep 18, 2015

The code looks good to me. You'll have to mention this middleware in the README I guess, even though it doesn't provide any new ops. And when squashing the commits you should update the text of the first commit.

@bbatsov
Copy link
Member

bbatsov commented Sep 18, 2015

One more thing - some tests for this would be nice to have. Maybe something printing output from a future or something. Not sure what's the best way to test it.

(defmacro do-with-out-messages
"Run body with v bound to the output stream of each msg in msg-seq.
Also run body with v bound to `original-out`."
[[v msg-seq] & body]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, what does v stand for?

@bbatsov
Copy link
Member

bbatsov commented Sep 21, 2015

Where are we with this?

@Malabarba
Copy link
Member Author

Readme and tests missing. Haven't started on them yet.

[messages]
(PrintWriter. (proxy [Writer] []
(close [] (.flush ^Writer this))
(write [& [x ^Integer off ^Integer len]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This & + destructuring seems extraneous and I think is responsible for a stacktrace. Well, removing & and the destructuring makes things work :-) :

ERROR [2015-09-18 22:03:06,794] com.yammer.dropwizard.jersey.LoggingExceptionMapper: Error handling a request: 8cf72e1a7bae9e6a
! java.lang.IllegalArgumentException: No matching method found: write for class java.io.OutputStreamWriter
! at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:80)
! at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:28)
! at cider.nrepl.middleware.out$fork_out$fn__20648.doInvoke(out.clj:45) ~[na:na]
! at clojure.lang.RestFn.invoke(RestFn.java:423)
! at cider.nrepl.middleware.out.proxy$java.io.Writer$ff19274a.write(Unknown Source) ~[na:na]
! at java.io.PrintWriter.write(PrintWriter.java:405) ~[na:1.8.0_45-internal]
! at java.io.PrintWriter.append(PrintWriter.java:1063) ~[na:1.8.0_45-internal]
! at java.io.PrintWriter.append(PrintWriter.java:56) ~[na:1.8.0_45-internal]
! at clojure.core$pr.doInvoke(core.clj:3563)
! at clojure.lang.RestFn.applyTo(RestFn.java:139)
! at clojure.core$apply.invoke(core.clj:630)
! at clojure.core$prn.doInvoke(core.clj:3593)
! at clojure.lang.RestFn.applyTo(RestFn.java:137)
! at clojure.core$apply.invoke(core.clj:630)

I'm using this impl of fork-out:

(defn fork-out
  "Returns a PrintWriter suitable for binding as *out* or *err*. All
  operations are forwarded to all output bindings in the sessions of
  messages in addition to the server's usual PrintWriter (saved in
  `original-out`)."
  [messages]
  (PrintWriter. (proxy [Writer] []
                  (close [] (.flush ^Writer this))
                  (write
                    ([c]
                     (do-with-out-messages [out messages] (.write out c)))
                    ([s ^Integer off ^Integer len]
                     (do-with-out-messages [out messages] (.write out s off len))))
                  (flush []
                    (do-with-out-messages [out messages] (.flush out))))
                true))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it also explains all these build failures: https://travis-ci.org/clojure-emacs/cider-nrepl/builds/81060333

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm... I guess it's not safe to call (.write out s off len) on the system's standard out. I'll fix it, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reflector can't find the right java method when off/len are null in the case that the 1-ary version of .write was called. I think that's because they're primitive arguments, even if it could find it, it would fail to cast a null to a primitive. It might be jvm-version-dependent as well.

@Malabarba
Copy link
Member Author

Thanks @gtrak, I believe the exception is fixed now.

I've added a readme entry and a test as well. The test is terribly superficial, but my knowledge of this whole Writer/Print/Stream family of Java inheritance is very limited, so I can't do much better.
I'd be very thankful if someone could write some proper tests for the fork-out function.

@bbatsov
Copy link
Member

bbatsov commented Sep 22, 2015

Guess this will do for now. Squash the commits and we'll refine the code down the road.

([x]
(with-out-binding [out messages]
(.write out x)))
([x ^Integer off & [^Integer len]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't get why you need this argument as optional, there's only arity 1 and 3 .write methods in http://docs.oracle.com/javase/7/docs/api/java/io/Writer.html, so len will never be null if this arity gets called (since it's also a primitive int).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't get why you need this argument as optional, there's only arity 1 and 3 .write methods in Writer

Because I didn't know that. :)

Automatically changes the root binding of *out* to print to any active
connections. An active connection is a session that has sent at least
one "eval" op and hasn't been closed.
We use an eval message, instead of the clone op, because there's no
guarantee that the message that sent the clone op will properly handle
output replies.
@Malabarba
Copy link
Member Author

Ok. I believe the failing tests are the same ones that fail on master

bbatsov added a commit that referenced this pull request Sep 23, 2015
@bbatsov bbatsov merged commit e68672d into master Sep 23, 2015
@bbatsov bbatsov deleted the out branch September 23, 2015 13:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants