tag:blogger.com,1999:blog-56675298257076485842024-02-20T17:48:55.496-08:00CodeaboutLike Walkabout, except without Jenny Agutter. Or Australia...Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.comBlogger27125tag:blogger.com,1999:blog-5667529825707648584.post-50676057976850447122018-11-10T17:44:00.000-08:002018-11-23T23:51:10.197-08:00Unix Signals with Clojure (1.10-beta5), JDK11, and Kubernetes (1.12)Kubernetes has a beta feature where you can share the process namespaces for a pod, allowing you to send, say, SIGHUP to another container in the pod, assuming you set the relevant permissions in the pod manifest.<br />
<br />
Clojure and JDK11 allow you to catch signals, through sun.misc.Signal, (despite years of threatening to take it away).<br />
<br />
So you can catch SIGHUP with the following:<br />
<br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;">(defn set-signal-handler!</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> "Catch HUP, INT, or USR1, etc. and run a one argument handler that</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> accepts a sun.misc.Signal"</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> [sig f]</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> (sun.misc.Signal/handle</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> (sun.misc.Signal. sig)</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> (proxy [sun.misc.SignalHandler] []</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> (handle [received-signal]</span><br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;"> (f received-signal)))))</span><br />
<div>
<br /></div>
<span style="font-family: inherit;">Just for reference, you can get your pid via</span><br />
<br />
<span style="font-family: "courier new" , "courier" , monospace;"> <span style="background-color: #fce5cd;">(.pid (java.lang.ProcessHandle/current))</span></span><br />
<br />
If you need to send a signal from Clojure, I'd suggest shelling out, as to the best of my knowledge, the JVM only lets you send SIGTERM and SIGKILL:<br />
<br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;">(clojure.java.shell/sh "/bin/kill" "-s" "SIGINT" "<some-pid>")</span><br />
<br />
or<br />
<br />
<span style="background-color: #fce5cd; font-family: "courier new" , "courier" , monospace;">(clojure.java.shell/sh "/usr/bin/pkill" "-HUP" "some-process-name")</span><br />
<br />
Here is the <a href="https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/" target="_blank">relevant Kubernetes information</a> with the pod configuration, which basically amounts to setting <span style="font-family: "courier new" , "courier" , monospace;">shareProcessNamespace: true</span> at the top of the pod spec, and adding the following to the container that will be <b>sending</b> the signal:<br />
<br />
<pre style="background: none rgb(248, 248, 248); border: 0px; box-sizing: border-box; font-family: Roboto, sans-serif; font-size: 11.9px; overflow-x: auto; padding: 15px; position: relative; tab-size: 4;"><code class="language-yaml" data-lang="yaml" style="background: none rgb(247, 247, 247); border: 0px; box-sizing: border-box; color: #303030; display: inline-block; font-family: "Roboto Mono", monospace; font-size: 14px; font-weight: bold; margin: 0px; padding: 0px; vertical-align: baseline;">securityContext:<span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;">
</span><span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;"> </span>capabilities:<span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;">
</span><span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;"> </span>add:<span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;">
</span><span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;"> </span>-<span style="background: none; border: 0px; box-sizing: border-box; color: #bbbbbb; margin: 0px;"> </span>SYS_PTRACE</code></pre>
<br />
Also, if you've come this far, you should take a look at <b><a href="https://github.com/pyr/signal">https://github.com/pyr/signal</a></b><br />
<b><br /></b>
Note that java has a flag that seems to be related. I found it after I wrote the original post.<br />
<br />
<pre style="background: none rgb(248, 248, 248); border: 0px; box-sizing: border-box; font-family: Roboto, sans-serif; font-size: 11.9px; overflow-x: auto; padding: 15px; position: relative; tab-size: 4;">
<dt class="dlterm" style="background-color: white; box-sizing: border-box; color: #333333; font-family: "Helvetica Neue", "Segoe UI", Roboto, sans-serif-regular, sans-serif; font-size: 14px; font-weight: bold; line-height: 1.42857; white-space: normal;"><code class="codeph" style="background: inherit; border-radius: 4px; box-sizing: border-box; color: inherit; font-family: "Courier New", Courier, monospace; font-size: 15.4px; padding: 0px;">-XX:+AllowUserSignalHandlers</code></dt>
<dd style="background-color: white; box-sizing: border-box; color: #333333; font-family: "Helvetica Neue", "Segoe UI", Roboto, sans-serif-regular, sans-serif; font-size: 14px; line-height: 1.42857; margin-left: 0px; white-space: normal;"><div style="box-sizing: border-box; margin-bottom: 14px; margin-top: 14px;">
Enables installation of signal handlers by the application. By default, this option is disabled and the application isn’t allowed to install signal handlers.</div>
</dd></pre>
<br />
Referenced Javadocs:<br />
<a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ProcessHandle.html" target="_blank">java.lang.ProcessHandle</a><br />
<br />Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com1tag:blogger.com,1999:blog-5667529825707648584.post-25190092568521240692015-03-25T13:34:00.000-07:002015-03-25T13:34:37.936-07:00Friend, oauth2, JWT, and google.A quick placeholder for showing how to put them together, without added extra dependencies.<br />
<br />
<a href="https://gist.github.com/jwhitlark/5dc423485548c31d2c72" target="_blank">View gist</a><br />
<br />
I'll discuss it shortly.Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com2tag:blogger.com,1999:blog-5667529825707648584.post-66235736393340962922015-02-08T13:32:00.000-08:002015-03-11T10:54:50.313-07:00Towards a better dir function in Clojure. (pt. 1)Coming from Python, for the most part, I felt right at home in the Clojure REPL. However, one of my trustiest old tricks in the Python interpreter didn't work nearly as well in Clojure.<br />
<br />
I'm referring, of course, to the "what the hell was that called again?" fixer: dir.<br />
<br />
In Python, dir will get you the fns/classes in a namespace, but the first major obstacle in clojure is the lack of support for namespace aliases.<br />
<br />
Let's fix that, shall we?<br />
<br />
Looking at the source for the dir macro, we find:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">user> (source dir)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(defmacro dir</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> "Prints a sorted directory of public vars in a namespace"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> [nsname]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> `(doseq [v# (dir-fn '~nsname)]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (println v#)))</span><br />
<br />
Ok, this is really no more than a bit of sugar, to save us having to quote the namespace and to print the vars on their own line. We'll have to dig deeper for some meat.<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">user> (source dir-fn)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(defn dir-fn</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> "Returns a sorted seq of symbols naming public vars in</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> a namespace"</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> [ns]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (sort (map first (ns-publics (the-ns ns)))))</span><br />
<br />
One thing I really love about Lisps: many times when I look under the hood, I'm expecting a monstrous Rube Goldberg machine, but what I find is some beautifully simple thing, that will still be pretty much the same thing in 50 years. (Go back and read some of the early algorithmic lisp code, it comes very easy.)<br />
<br />
The first part of dir that bugs me is you must specify the full namespace, even if you've aliased it to something much simpler.<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(ns yournamespace</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (:require [clojure.string as str]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> [clojure.repl :refer :all]))</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(dir str)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">=> Exception No namespace: str found clojure.core/the-ns (core.clj:3830)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">user> (dir clojure.string)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">blank?</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">capitalize</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">escape</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<br />
You can get aliases via ns-aliases, so...<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(defn alias-dir-fn [nsname]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (-> (ns-aliases *ns*)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (get nsname) ; look up the alias or nil trying</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (or nsname)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> dir-fn))</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">(defmacro alias-dir</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> "Prints a sorted directory of public vars in a namespace or an alias</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> namespace."</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> [nsname]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> `(doseq [v# (alias-dir-fn '~nsname)]</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> (println v#)))</span><br />
<br />
Now we can...<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">user> (alias-dir str)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">blank?</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">capitalize</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">escape</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">...</span><br />
<br />
But I'm not done with dir yet. Stay tuned for part 2.<br />
<br />
Edit: I've filed a ticket for this functionality to be added to <a href="http://dev.clojure.org/jira/browse/CLJ-1673" target="_blank">Clojure</a>Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com1tag:blogger.com,1999:blog-5667529825707648584.post-26481701367595422332015-02-08T13:16:00.000-08:002015-02-09T12:10:07.645-08:00Using memoization to change a creation function into get-or-create.N.B. This should work in any language with first class functions, memoization, and immutable values.<br />
<br />
tl;dr: In the past I'd always thought of memoization as a way to save the computer work. It hadn't occurred to me that it could also save me work.<br />
<br />
I was re-reading "The Joy of Clojure" and came across a gem I'd missed the first time. Listing 14.12 is described as "A function to create or retrieve a unique Agent for a given player name".<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">(def agent-for-player</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (memoize</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (fn [player-name]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (-> (agent [])</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (set-error-handler! #(println "ERROR: " %1 %2))</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (set-error-mode! :fail)))))</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">;; The above doesn't quite work for me, set-error-handler!</span><br />
<span style="font-family: Courier New, Courier, monospace;">;; doesn't </span><span style="font-family: 'Courier New', Courier, monospace;">seem to return the agent. Doesn't make the pattern</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">;; less compelling, though.</span><br />
<br />
The authors comment that this allows you to maintain player-name as an index into a table of agents without having to explicitly manager and lookup agents.<br />
<br />
There are two caveats to this approach: 1) player-name must be immutable, and 2) You really need to understand the memoization mechanism. clojure.core/memoize, for instance, will keep a internal map of args/response until the end of time. You could use http://clojure.github.io/core.memoize/ to modify the strategy if you so choose.<br />
<br />
The place where I'd try this first is in what I call "micro-logs". Frequently as I'm working, I want to log some data to a side channel, and this pattern saves having to manage this manually and cluttering up my code.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">(def get-or-create-micro-log</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (memoize</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (fn [file]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (io/make-parents file)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (.createNewFile file)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (-> (io/writer file :append true)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (agent</span><br />
<span style="font-family: Courier New, Courier, monospace;"> :error-mode :fail</span><br />
<span style="font-family: Courier New, Courier, monospace;"> :error-handler #(println "ERROR: " %1 %2)))</span><span style="font-family: 'Courier New', Courier, monospace;">)))</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">(defn microlog</span><br />
<span style="font-family: Courier New, Courier, monospace;"> "Useful micro-pattern to send off a write to various</span><br />
<span style="font-family: Courier New, Courier, monospace;"> files via </span><span style="font-family: 'Courier New', Courier, monospace;">agents without having to maintain a lookup</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> table. Symlinks can get </span><span style="font-family: 'Courier New', Courier, monospace;">you into trouble; at a </span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> minimum they will duplicate the agent."</span><br />
<span style="font-family: Courier New, Courier, monospace;"> [lg line]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (let [a (-> </span><span style="font-family: 'Courier New', Courier, monospace;">(io/file lg)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> (.getAbsoluteFile)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;"> (get-or-create-micro-log))</span><br />
<span style="font-family: Courier New, Courier, monospace;"> output (str (str/trim-newline line) "\n")]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (send-off a</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (fn [writer]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (doto writer</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (.write output)</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (.flush))))))</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">(defn microlog-all</span><br />
<span style="font-family: Courier New, Courier, monospace;"> [lg all-data]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (doseq [d all-data]</span><br />
<span style="font-family: Courier New, Courier, monospace;"> (microlog lg d)))</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<br />
<div>
Edits: Used io/make-parents instead of File calls. Changed send to send-off, since this is I/O. Cleaned up creation of agent.</div>
Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-46450950094941251562014-06-20T13:56:00.000-07:002014-06-24T11:35:01.215-07:00Om examples: Scrubbing Calculator componentI'm starting to get the hang of Om. While I've used cljs & jayq for a while, understanding the right way to use om + core.async has taken a bit of doing. They're both great libraries, and have fairly small APIs, but I've been hungry for fairly simple examples. In that vein, here are a couple, the second inspired by Bret Victor's <a href="http://worrydream.com/ScrubbingCalculator/">Scrubbing Calculator</a>.<br />
<br />
<h2>
Update: git repo with examples</h2>
<div>
<a href="https://github.com/jwhitlark/scrubber-example">https://github.com/jwhitlark/scrubber-example</a></div>
<h2>
Prelude: simple clock</h2>
<div>
<script src="https://gist.github.com/jwhitlark/5831b282ecce8e15d1e6.js"></script>
<span style="font-family: Times, 'Times New Roman', serif;">Simple enough, derived from the animation example in </span><a href="https://github.com/swannodette/om/tree/master/examples" style="font-family: Times, 'Times New Roman', serif;">om examples</a><span style="font-family: Times, 'Times New Roman', serif;">.</span></div>
<div>
<span style="font-family: Times, Times New Roman, serif;"><br /></span></div>
<h2>
<span style="font-family: Times, Times New Roman, serif;">Scrubbing Int, Approach one: all local state</span></h2>
<div>
<span style="font-family: Times, Times New Roman, serif;">Two utility functions:</span></div>
<script src="https://gist.github.com/jwhitlark/9b99e6448067e35f79b9.js"></script>
<br />
<div>
<span style="font-family: Times, Times New Roman, serif;">My first attempt entirely used internal state. Not terribly useful, but it set the stage.</span></div>
<script src="https://gist.github.com/jwhitlark/0081c0b479591ce1b3c7.js"></script>
<br />
<div>
It works entirely in the span element. Click and drag changes the value. If you drag out of the element before you release the mouse button, the state becomes inconsistent. The snippet below will let you tune the sensitivity if you use it in the :onMouseMove handler.<br />
<br />
<script src="https://gist.github.com/jwhitlark/df071d0f065d9692031e.js"></script>
Now</div>
<h2>
Approach two: Moving out the state</h2>
<div>
So here I've switched things to use an external atom for the value, and moved most of the logic into event listeners under IWillMount. This works correctly even if you drag off the span element. It's pretty much everything I set out to do with this.</div>
<script src="https://gist.github.com/jwhitlark/707f9fb5b379b39c51a5.js"></script>
<br />
<div>
The only drawback is the event listeners should be connected in the handler for onMouseDown, and removed when "mouseup" is received. I'll update it when I've figured out how to remove existing listeners.<br />
<br />
<h2>
Afterward: Stuff you should be looking at</h2>
</div>
<div>
<ul>
<li><a href="https://github.com/bhauman/devcards#what-do-i-do-for-deployment">devcards</a> are awesome, a great way to prototype this kind of stuff</li>
<li><a href="https://github.com/Prismatic/om-tools">Prismatic/om-tools</a></li>
<li><a href="http://blog.getprismatic.com/om-sweet-om-high-functional-frontend-engineering-with-clojurescript-and-react/">om-sweet-om-high-functional-frontend-engineering-with-clojurescript-and-react/</a></li>
<li><a href="http://domkm.com/posts/2014-06-15-isomorphic-clojure-1/">Isomorphic Clojurescipt</a>, and his example <a href="https://github.com/DomKM/omelette">omelette</a></li>
</ul>
</div>
Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com4tag:blogger.com,1999:blog-5667529825707648584.post-61567609065517735472014-05-02T14:31:00.000-07:002014-05-03T10:23:10.832-07:00DataScript might be a great idea for DatomicI was looking at <a href="https://github.com/tonsky/datascript">DataScript</a>, and the simple implementation gave me (I think) a lot of insight into Datomic. If <a href="http://tonsky.me/blog/datomic-as-protocol/">Tonsky's post</a> is correct, and there is a core to <a href="http://www.datomic.com/">Datomic</a> that could be used as an in-memory Datomic-like thing, which could be used as easily as, say, core.logic, and that I could look under the hood to see how a simple case works, I would be in a much better place. A big part of the problem is the catch-22 for learning datalog, easy to toy with, don't know where to go from there.<br />
<br />
I'm not against spending money. A few months back I went to an all-day Datomic class, and thought it well worth the $300 or so I spent on it. I wouldn't be against talking people into paying for Datomic, but I'm not familiar with it to know when I should. The main problem is that I haven't spent enough time playing with it to get a good feel for where it fits best. Spinning up a separate server process just doesn't really work for that kind of playing.<br />
<br />
Sort of like how I understood garbage collection, but once I implemented a toy lisp, I *got* it, and understood it's tradeoffs in a way I never had before.<br />
<br />
I don't know what Datomic's sales numbers look like, but this seems like it would be a good strategy to increase interest/familiarity with the protocols and ideas involved, which would presumably increase the sales pipeline.<br />
<br />Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com4tag:blogger.com,1999:blog-5667529825707648584.post-57012488437943884912012-06-29T16:06:00.002-07:002012-07-02T22:24:46.650-07:00.emacs style Clojure rc files<div>
<span style="font-family: inherit;">The following will let you run clojure code in the current namespace, from a dotfile in your home directory.</span></div>
<span style="font-family: 'Courier New', Courier, monospace;"></span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><span style="font-family: 'Courier New', Courier, monospace;"><br /></span></span></div>
<span style="font-family: 'Courier New', Courier, monospace;">
(require '[clojure.java.io :as io])</span><br />
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">(load-string (slurp (io/file (System/getenv "HOME") *rc-file*)))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>Jason Whitlarkhttp://www.blogger.com/profile/14704491999084470927noreply@blogger.com2tag:blogger.com,1999:blog-5667529825707648584.post-28352033645679172772011-12-09T18:39:00.001-08:002011-12-09T18:40:08.877-08:00The old, new jobOh, yea. FYI: I work for bittorrent these days. I started around June.Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-11659887839776252532011-09-19T19:52:00.001-07:002011-09-19T20:02:46.232-07:00Running Rake as a scriptRake seemed nice, but I needed some of it for a server side trick. Not difficult, but if you're new to it, it's tough to find just what you need.<br /><br /><div><meta equiv="content-type" content="text/html; charset=utf-8"><a href="https://gist.github.com/1228206/eca65818062f0a18a5c7923c94350f56153b32a6">https://gist.github.com/1228206/eca65818062f0a18a5c7923c94350f56153b32a6<br /></a></div><div><br /></div><div>Is all the boilerplate you need to run rake tasks as a script, from anywhere in the filesystem.<br /></div>Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-36859094201096900122011-03-29T14:27:00.000-07:002011-03-29T14:37:50.422-07:00Controlling android via http(s) over usbI've been playing with couchdb, and was playing with the paw web server for android (http://paw-android.fun2code.de/), and discovered something interesting. <br /><br />* If you turn on usb teathering, then connect your phone to a linux box, it'll automatically add usb0 as a connection.<br />* If you grab the default route for usb0, you'll have the ip of your android phone.<br />* If you run paw, you'll be able to do things like send sms, control your phone, etc.<br />* If you run, (and tweak the config), you'll be able to connect to the couchdb on your phone<br />* You don't have to access these services via a web browser, (yay! SMS from emacs!)<br />* paw server also has stuff like "play music from phone", and "use phone as webcam", etc. etc. etc.<br /><br />I suppose I knew all this was possible, but I didn't think it would be "done in 5 minutes" possible...Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com1tag:blogger.com,1999:blog-5667529825707648584.post-72639483381415829882011-03-14T15:15:00.000-07:002011-03-14T15:18:53.291-07:00Python lockfilesThe python-daemon library has a nice implementation of pidfiles, much better than the standard lib lockfile. i.e.<br /><br />import lockfile.pidlockfile as pidlock<br />lf = pidlock.PIDLockFile("/tmp/mqtask.pid")<br /><br /># module methods<br />pidlock.read_pid_from_pidfile<br />pidlock.remove_existing_pidfile<br />pidlock.write_pid_to_pidfileJason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-15573886195494942272010-06-21T10:29:00.000-07:002010-06-21T10:49:54.701-07:00Airbnb sounds cool, but I really want Walk-in-closet-to-goI don't like packing. <br /><br />When I look around my house, I see the progress I've made towards living a less-stuff lifestyle, such as advocated by Paul Graham, among many others, (see <a href="http://paulgraham.com/stuff.html">Stuff</a>.) Still, I don't think I'll reach the point where I can comfortably live out of a suitcase or a backpack without significantly reducing the quality of my life.<br /><br />So what about a service more like <a href="http://www.airbnb.com/">AirBnB</a> + <a href="http://www.pods.com">PODS</a> + <a href="http://www.closetfactory.com/">A Closet organizer something like this</a> + <a href="http://www.kivasystems.com/">Robotic movers similar to these</a> = Easy, comfortable, continuous travel.<br /><br />Just playing with numbers on the back of an envelope, I think this would be both possible and cheap, in the near future.<br /><br />Hmmmm....Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com1tag:blogger.com,1999:blog-5667529825707648584.post-76465304903094241432010-06-07T17:52:00.000-07:002010-06-08T15:37:05.495-07:00Using Apache Camel from ClojureI've been looking at <a href="http://camel.apache.org/">Apache Camel</a> for a project at work, and had a bit of trouble getting it working from Clojure, so I wrote this up in case anyone has the same issues.<br /><br />The first example in <a href="http://www.manning.com/ibsen/">Camel in Action</a> watches a directory for new files, and copies them to an output directory. Here are the steps to get that running in Clojure, using <a href="http://github.com/technomancy/leiningen">leiningen</a>.<br /><br /><ul><li>Create your input and output directories</li><li>Create a new leiningen project</li><li>Add the following to the project.clj</li></ul><br /><br /><script src="http://gist.github.com/430741.js?file=project.clj"></script><br /><br /><ul><li>run lein deps<br /></li><li>Add the following to your src/org/whitlark/fc.clj (or whatever your last name is ;-)</li></ul><br /><br /><script src="http://gist.github.com/430741.js?file=FileCopyWithCamel.clj"></script><br /><br />Part of what screwed me up was the fact that you need to include spring in the project in order to get camel to work properly.<br /><br />You can also use a macro to make the code even cleaner, like this:<br /><br /><script src="http://gist.github.com/430741.js?file=FileCopyWithCamel2.clj"></script><br /><br />Which I think compares nicely to the original Java:<br /><br /><script src="http://gist.github.com/430741.js?file=FileCopyWithCamel.java"></script><br /><br />I'm sure there are more improvements possible, but this got me started.Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com1tag:blogger.com,1999:blog-5667529825707648584.post-83222816592833988752010-04-23T11:25:00.000-07:002010-04-23T11:28:44.548-07:00Extending the range of a PS3 controller.Just plug a short usb cable (I use 12"), and leave the other end unplugged. The cable seems to act like an antenna, giving you a better connection. I've not done any tests to see exactly how much you can get, but I get at least another 10'.<br /><br />I'm thinking about opening up the controller and soldering an antenna to the usb enclosure. If I do that, I'll post instructions/video.Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-31031924642823072302009-06-22T10:16:00.000-07:002009-06-22T10:18:00.847-07:00Quick post on working with large changes across your codebaseStuff I wrote up for work. Ignore the p4 part, assuming you use a real version control system, *cough* git.<br /><br /><p> Strategies for making changes that affect a large number of files </p><p> emacs: </p><ul><li> recursive editing (C-r, C-M-c) </li><li> find-grep-dired </li><li> ibuffer (Used to save large number of buffers at once) </li><li> dired-do-query-replace-regexp (find-replace across marked files in dired buffer) </li></ul> <p> shell: </p><ul><li> grep -rIl 'pattern' * > files_to_op_on (that's a capital i and a lowercase L) <ul><li> grep option -r: recurse </li><li> grep option -I: (capital i) exclude binary files </li><li> grep option -l: (lowercase L) Show only filenames with matching content, no other output. </li><li> grep option -h: don't include filename in the match line. </li></ul> </li><li> cat files_to_op_on | xargs sed -i backup_extension 's/old/new/g' #Note that bsd sed required a backup extension with the -i argument. </li><li> cat files_to_op_on | xargs p4 edit </li></ul> <p>Watch out for symlinks! If you get them in your changelist, p4 won't accept submit, in which case you'll need to delete them via p4 change. </p><pre>p4 submit > /dev/null<br /></pre> works well to just get the errors. <p> The general pattern of: </p><pre>grep -rI 'something' * | sort -u<br /></pre>can show you the variations you'll need to fix. If you want to get rid of leading whitespace in your results, try piping through: <pre> sed -E -e 's/^[[:space:]]+//'<br /></pre>Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-59474755946905750152009-03-07T23:27:00.000-08:002009-10-09T11:55:46.814-07:00Clojure, Frozen Bubble, and how I learned to start worrying about my math educationSo, I've been playing around with <a href="http://clojure.org/">Clojure</a>, reading the rough cut of <a href="http://www.pragprog.com/titles/shcloj/programming-clojure">Programming Clojure</a>, (good book, so far), and was looking for a project to sink my teeth into in order to really get a feel for the language. <a href="http://www.frozen-bubble.org/">Frozen Bubble</a>, aka Bust A Move, has long been a favorite game of my wife, and while I tend to lose to my coworkers, should be interesting to implement.<br /><br />Using a sample implementation of snake from the book as a template, I start hacking on the code, striping out all the snakey stuff, and just getting a minimal program that just displays a window. So, Frozen Bubble has only two real game objects: your target pointer, which ranges from -90 to +90 degrees, (a little less actually, no sense firing horizontally), and bubbles, which are in one of three states: not moving while in the initial position at the center of the bottom of the screen, stuck to the top of the game area (directly or via a chain of bubbles), and moving from the bottom of the screen to the top.<br /><br />Well, the first state is easy, the third state is probably the same, but the second state has some unexpected complexity. First I think I'll give the bubble a location and direction, and handle the speed via the game tics. Seems reasonable, right? Then I run smack into something that makes me wish I had paid more attention in math class. The bubble has a direction, expressed in degrees, which I need to use to manipulate the location, which is expressed as [x,y].<br /><br />Being a proper little reductionist, and stubborn enough to try to figure it out for myself instead of spending five minutes with google, I decide to think about the second simplest case, a 2x2 grid of pixels. (The simplest would be a single pixel, and I don't see how that would help at all. Perhaps I'm just not being clever enough, though.)<br /><br />Anyway, this setup gives us the following:<br /><br />Starting point: [0,0], 0 degrees (straight up or North) -> [0,1], 90 degrees (right or East) -> [1,0], and 45 degrees (NE) -> [1,1]<br /><br />This says to me that if I want to work in single pixels, I have to do it in 45 degree increments. Not satisfactory. I can approximate closer and closer angles by using a larger grid, (why do I feel like this leads to calculus?) But that means that I'm not working with single pixels anymore.<br /><br />I went and read some things at <a href="http://betterexplained.com/">Better Explained</a>, which while fascinating in their own right, (I love that site), didn't seem to help matters. I think the gap is that the actual position in real space would be floating point (well, kinda), but the game space in measured in integers (pixels).<br /><br />Grabbing my trusty graph paper, I start diagramming. A 3x3 grid only buys me two new angles: 22.5 and 67.5. 4x4 gives me 0, 15, 30, 45, 60, 75, & 90.<br /><br />I now have:<br />2x2 = 4 squares = 3 angles<br />3x3 = 9 squares = 5 angles<br />4x4 = 16 squares = 7 angles<br />5x5 = 25 squares = 9 angles, etc.<br />which generalizes to: <span class="blsp-spelling-error" id="SPELLING_ERROR_0">AxA</span> = A**2 squares, and 2A-1 angles<br />So to represent all 90 degrees, (which is actually kind of arbitrary anyway), I solve for 90 = 2A-1 = 45.5 squares to a side. My screen resolution on my laptop is 1024/768, so using the height as a guide, if I want to update to bubble position by individual degrees, I can only do it ~16 times from the bottom to the top of the screen. Clearly less than ideal, some sort of approximation is called for.<br /><br />There are two ways that I can think of to do this off the top of my head for a 2x2 grid update: splitting the difference evenly, (i.e. a 20 degree angle will be bumped up to 45, or it can be rounded to the closer number, in this case 0. The rounding case seems more rational.) Obviously I want to stagger the updates to get the closes approximation to the actual angle I can, but a constraint I'm operating under is to have no other state saved between updates than location and direction. <span class="blsp-spelling-error" id="SPELLING_ERROR_1">Ummmm</span>.....<br /><br />The only option I can see is to manipulate the direction each time so that after ~45 pixels, it arrives in the correct place. However, my laptop battery is about to die, and it's late, so I think I'll sleep on it and continue this later.Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-14719776588782624562008-11-19T11:57:00.000-08:002008-11-19T12:03:44.459-08:00Nice trick with functools and os.path.joinSometimes os.path.join('something', 'something_else', 'more' ... ) gets old. With functools.partial, you can preload os.path.join to clean up your code. For example:<br /><br />import functools<br /><span style="font-family:courier new;"><span style="font-family:courier new;">my_path_plus = functools.partial( os.path.join, 'a', 'b', 'c')</span><br /><span style="font-family:courier new;">m = my_path_plus('x', 'y', 'z')</span><br /><span style="font-family:courier new;"># m is 'a/b/c/x/y/z'</span><br /><span style="font-family:courier new;">n = my_path_plus()</span><br /><span style="font-family:courier new;"># n is 'a/b/c', for when you don't want to add anything.</span><br /></span>Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-23380802197039658192008-05-29T11:01:00.000-07:002009-10-09T11:53:42.136-07:00What cities whisper to usAnother great <a href="http://www.paulgraham.com/cities.html">post</a> by Paul Graham. I think I'll stop linking to his stuff unless I have something to add. Just go an read his essays. <br /><br />I wonder what message Istanbul sends to residents. (edit) Having been to Istanbul again recently, I still don't know what it says to you, but can say it's a very sweet song, (if you've got money).Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-4350347652700652082008-05-23T17:31:00.000-07:002009-03-08T00:18:40.603-08:00Distraction...Paul Graham <a href="http://www.paulgraham.com/distraction.html">posted</a> again in his usual good form. I canceled my cable years ago, and have never regretted it. Amusingly, I follow his formula for email at work, but only because Ironport uses exchange, and I haven't gotten around to setting up a windows vm on my Linux desktop. Hmm.. Thinking about it, I should try using Evolution instead. I'll give that a shot and report back...<br /><br />Update: Evolution might get the job done, but I don't like it. Back to thunderbird, and ignoring meetings...Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-44338920836949078422008-05-23T17:22:00.000-07:002008-05-23T17:30:29.369-07:00Unit tests force decouplingI just read Vidar Hokstad's <a href="http://www.hokstad.com/reducing-coupling-through-unit-tests.html">post </a>on reducing coupling via unit tests, and I can't agree more. I'm currently trying to untangle some code that was written without unit tests, and is <span style="font-weight: bold;">very </span>tightly coupled. It's easier to fix in Python than it would be in, say, Java, but that still doesn't make it fun. A recent <a href="http://binstock.blogspot.com/2008/05/is-popularity-of-unit-tests-waning.html">post</a> on <a href="http://reddit.com/r/programming/">proggit</a> makes the argument that unit testing is on the wane. I really hope he's wrong. I've been doing TDD for several years now, (since reading Kent Beck's book on the subject), and I find it much better than any other methodology.<br /><br />I was also sad to hear that Agitar went under. I was never in their target market, but I always thought they had some cool tools.<br /><span id="Header"><span class="text"><span style="font-weight: bold;"><br /></span></span></span>Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-62747272642547345342008-05-10T11:26:00.000-07:002008-05-10T11:34:53.899-07:00Nokia 810 goodness...I just unwrapped my new 810 last night, and have been impressed. To have a Linux machine that you can carry in your pocket it wonderful, especially with apt-get. Add in a bash shell, vi, ssh, openvpn, and it just keeps getting better and better. I'm really impressed with the 800 x 480 screen. I think I've found my new sidekick. And I haven't even started in on the hacking sites. (Is that a python in your pocket or are you happy to see me?)Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-50746787886064558312008-05-02T12:35:00.000-07:002008-05-23T17:22:38.278-07:00Beginning of the end of Linux Driver issues...<a href="http://www.linux.com/feature/134125">linux.com </a>has an article on major OEM's (ASUS, Dell, Hewlett-Packard, and Lenovo) would put language into their acquisition contracts with their suppliers to require either Linux drivers or open api's to speed Linux driver development. In the last couple of years driver problems have gone from show stoppers to annoyances, but this, combined with the ever growing install base of Linux, promises to make driver issues a thing of the past. I was at Smart & Final the other day and picked up a usb key, and was amused to see that in addition to the windows logo, it had a linux penguin.Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-66464006590569310032008-05-01T14:13:00.000-07:002008-05-01T14:15:09.818-07:00Flash & Silverlight bait and switch...Beware of geeks bearing <a href="http://news.zdnet.com/2424-3515_22-199508.html?tag=nl.e550">gifts</a>...<br />Personally I've become so cynical that I use open source as a purely defensive measure. I've been burned too many times.Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-32466515847635885462008-05-01T14:07:00.000-07:002008-05-01T14:10:58.541-07:00About the title of this blog.It's my bit of homage to my favorite British comedy, <a href="http://www.bbc.co.uk/comedy/coupling/">Coupling</a>. If you've never seen it, you are missing out on one of the funniest shows in years. I don't watch tv, don't even have cable, but this was so good I went out and bought it. You can get it off netflix either on disk or via watch now, too. It ran four seasons before the actors went their separate ways. If I ever make a mint, I'll pay for a reunion series personally... ;-)Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0tag:blogger.com,1999:blog-5667529825707648584.post-63468300054111163612008-05-01T13:53:00.000-07:002008-05-01T16:18:51.362-07:00Ubuntu + Wine = Windows 7I was reading Joel's latest <a href="http://www.joelonsoftware.com/items/2008/05/01.html">post</a>, about the architecture astronauts that are running MS, and it occurred to me that they're going in exactly the wrong direction. After all, <a href="http://www.winehq.org/">Wine</a> is great. I can run most of my apps on it with minimal fuss. As wine doors takes off, I can see that I'll be able to run them with different settings. Since Linux is already a much more stable environment that the crap Microsoft keeps pushing out, how long is it going to be before people start writing against the wine api and let people run their software wherever they want?Jason Whitlarkhttp://www.blogger.com/profile/17671814988029338807noreply@blogger.com0