Buffy Reaches 1.0

Buffy The Byte Buffer Slayer

Buffy is a Clojure library to working with binary data, writing complete binary protocol implementations in Clojure, storing complex data structures in an off-heap cache, reading binary files and doing everything you would usually do with byte buffers.

Buffy is built on top of Netty byte buffers, which addresses many long standing issues with java.util.ByteBuffer.

Key Features

Buffy enables you to access buffers in the similar way you work with the regular Clojure data structures. You can get and set fields separately from one another, give names to different parts of binary representation and so on:

  • partial deserialization (read and deserialise parts of a byte buffer)
  • named access (access parts of your buffer by names)
  • composing/decomposing from key/value pairs
  • pretty hexdump
  • many useful default types that you can combine and extend easily

Data types used in Buffy buffers include:

  • primitive types, such as int32, boolean, byte, short, medium, float, and long
  • arbitrary-length string
  • byte arrays
  • composite types (combine multiple primitive values together)
  • repeated type (repeat any primitive arbitrary amount of times in payload)
  • enum type (for mapping between human-readable and binary representation of constants)

The 1.0 release

Starting with the 1.0 release Buffy API is considered to be moderately stable. Releases can be expected to follow a very SemVer-like change strategy. Most of the work now will go into minor improvements based on community feedback.

Buffy is a ClojureWerkz Project

Buffy is part of the group of libraries known as ClojureWerkz, together with

  • Langohr, a Clojure client for RabbitMQ that embraces the AMQP 0.9.1 model
  • Cassaforte, a Clojure Cassandra client built around CQL
  • Elastisch, a minimalistic Clojure client for ElasticSearch
  • Meltdown, Clojure interface to Reactor
  • EEP, a Clojure event processing library
  • Monger, a Clojure MongoDB client for a more civilized age
  • Neocons, a client for the Neo4J REST API
  • Quartzite, a powerful scheduling library

and several others. If you like Buffy, you may also like our other projects.

Let us know what you think on Twitter or on the Clojure mailing list.

@michaelklishin on behalf of the ClojureWerkz Team

Permalink

I'm writing a book about Building a System in Clojure

I thought about where to take my series about Building a System in Clojure next and realized that I don’t like the format of a blog series all that much. Instead, the format of a book seems like a better choice; one where you, the potential reader, are invited to provide feedback from the very first moment of the writing process. I have already started that process and for now I have transferred the existing articles from the series into the book without much further editing. Over the next couple of weeks, I will be working on making the content more consistent with the book format. The book is available for free on leanpub.com. Iff (if and only if) you find the content to be of value, you can pay a suggested price, but that’s entirely up to you and something you can decide on later.

The book format will allow me to write a consistent narrative around the flow of data through a system, where we will follow the journey of data from a streaming source to a user interface that updates changes according to new data from the streaming source immediately, or rather within a few hundred microseconds.

As a sample application, we will use tweets streaming live from the Twitter Streaming API. In case you haven’t seen it yet, this is how the application looks like:

New Design with Pure CSS

There’s also a live version of this application. I have ideas for additional sample applications, but they may or may not come into existence, depending on how much time I will have for this book project.

The process of writing this book will take place while exploring the problem space, not afterwards. All designs and implementations are fluid at this point and I will be happy to discuss all aspects of the system in this Google Group and adapt and rewrite aspects when better solutions arise in these discussions. You are also welcome to join the development process: do reach out when you have a suggestion on how to get involved. The project needs tests, better inline documentation, code reviews, and quite possibly better design and code.

Regarding the book writing process, first and foremost I would love questions for clarification so the content will come out as approachable as possible. If you find a typo, please correct it and submit a pull request right away.

I am looking forward to the weeks to come. It’s much better to be able to put some work in here and there on whatever I’m interested in that day instead of writing monolithic blog posts that aim at conclusively covering an aspect of the application, usually way before I have any conclusive understanding.

Would you do me a favor? 1 Please sign up as a reader (for free and with no commitment whatsoever) right away if you think you might at all be interested in what we’ll cover in this book. I would very much like to know how much interest there might be and I promise I will try to do what I can to make the time spent on reading this book or contributing to it worth your while.

Cheers and Happy Holidays, Matthias


  1. As a little christmas present, if you will.

Permalink

Understanding Macros and Code as Data

The other day, while having a conversation in the office about Clojure macros, I was reminded that not everyone fully understands the phrase “code as data” or its useful repercussions. This isn’t surprising, as there are still very few mainstream languages that feature this, and most of the ones that do are lisp dialects. I think that’s unfortunate, as it’s a conceptually simple tool that you can get a lot of leverage out of.

Metaprogramming & Homoiconicity

Metaprogramming is at the heart of this idea. Many languages support metaprogramming in various degrees; some common examples include generic type systems or runtime reflection. These features are nice because they allow you to design some level of run-time or compile-time flexibility into your code. There are limits, however, because you lack the freedom to affect the behavior of or rewrite all aspects of what your code is doing.

A special thing happens when you introduce the feature of homoiconicity to a language. Essentially, homoiconicity is when your language’s structure (and possibly syntax) is defined in its own data types. For more information on the details, you might be interested in reading Understanding Homoiconicity in Clojure. When your code is defined in terms of the data structures inherent to your language, it becomes exceedingly easy to rewrite, transform, or even generate code. And, as you are essentially manipulating source code itself, this means that there are no language features that are unavailable to you.

Benefits of Code as Data

The two main areas that I see this benefit in real-world practice are in reducing repetition and enhancing the expressiveness of the language.

Reducing repetition

Every once in a while, you run into a task that is not inherently hard but is difficult to easily express without significant repetition. Take this example, which is simply taking things out of a list, processing them, and building a more-friendly map of data.

1
2
3
4
5
6
7
8
9
10
11
(defn parse-system-state-vec [state-vec]
  (let [[standby-status
         fan-speed
         temperature
         hardware-version
         software-version] state-vec]
    {:standby-status (parse-zero-indexed-enum standby-status [:awake :standby-countdown :shutdown-countdown :standby])
     :fan-speed (parse-zero-indexed-enum fan-speed [:off :low :high])
     :temperature (parse-num temperature)
     :hardware-version (parse-num hardware-version)
     :software-version (parse-num software-version)}))

Most of the work here is simply bookkeeping. Instead, we could write a macro that would take a description of the work to be done, and then generate this ugly code underneath it. Let’s assume we did this, and the macro name is defparser. Defparser would accept a set of keys for the resulting map, paired with expressions showing how to compute the associated value. While we’re at it, we’ll even make it so that the raw value gets inserted as the first argument in the expression for us, so we don’t have to type it.

1
2
3
4
5
6
(defparser parse-system-state
  :standby-status (parse-zero-indexed-enum [:awake :standby-countdown :shutdown-countdown :standby])
  :fan-speed (parse-zero-indexed-enum [:off :low :high])
  :temperature (parse-num)
  :hardware-version (parse-num)
  :software-version (parse-num))

Underneath, defparser could be generating the same code. It would behave identically at runtime, but the latter example has less repetition and is less prone to breakage or simple errors. Also note that there are many ways that you could define the shape of data you accept while still retaining the ability to generate code as in the first example, which gives you a lot of freedom in how you want to think about your ideas.

Enhancing expressiveness

After you become comfortable with leveraging the ability to rewrite your code, you start to think about doing it in more ambitious ways than reducing simple repetition in your code. They key thing to remember here is that your macros are actually rewriting your source code, and since your source is represented by your language’s own data structures, you can return anything that looks like valid source code and use any language features you want.

So if you can express your ideas in a structured way, regardless of how high-level they are, you have the power to rewrite those ideas into valid code. Let’s look at this example using sqlkorma:

1
2
3
4
(select users
  (with account)
  (fields :name :email :account.status)
  (where {:id 42}))

Korma has implemented a DSL for describing SQL queries right within Clojure. Internally, when Clojure compiles this code, it passes it into Korma, which generates a simple data structure describing the constraints provided (a join table, which fields to select, etc.), and outputs code that, at runtime, will call a function to execute the query by providing it that generated data.

This is commonly referred to as building up the language to meet your problem domain.

Conclusion

When you get the chance to use a language that lets you treat code as data, you should take it. It’s actually not as scary of a concept as it seems, and once you wrap your mind around it, you’re left with the ability to create some truly powerful abstractions that you can leverage to write more succinct and expressive code.
 

The post Understanding Macros and Code as Data appeared first on Atomic Spin.

Related Posts

Permalink

The Ashton Disinterest Curve - Clojure

Do you like anything Rob? You said you were going to be nice and all you've done is complain about .NET and JS so far. Point taken - let's talk about Clojure.

An ode to the lisp

(parens (love (i))). I love s-expressions; code written in Clojure tends to be beautifully expressive thanks to the terseness and minimal syntax provided the humble s-expr. When you go on to add easy composition and a rich library of "All the basic things you might want to do to a list or a key-value structure" the magic starts to happen. The focus on data-oriented code that gave birth to that heavily re-usable core library and an emphasis on referential transparency means that things generally do just what you expect them to and you can usually just focus on the functionality that led you to open up an editor in the first place.

Speaking of editors, once you have your editor of choice set up with REPL integration, Paredit (if you want to edit expressions and not lines) and you've downloaded the internet with Maven, the low syntastical burden of the language makes it ideal for hacking around in even for people who are new to the language. Spending evenings at the London Clojure Dojo was one of my favouite ways to use time as every week there would be new people to play with in whatever bizarre challenge had been set by the group.

That community was instrumental in keeping me going with the journey as I wrote CravenDB and did my best to learn through that action. Over the year I wrote that I entered the top 100 committers on Github and entered the top five committers for Clojure itself. Being able to spend some time at MastodonC working on some unrelated OSS in Clojure was also an amazing experience and cemented my love of the humble paren and the people who wielded them.

Clojure is therefore a great gateway language in that it makes functional programming accessible to anybody with the JVM installed.

Concurrency

Clojure had a goal of making concurrency on the JVM easy with its built in constructs for utilising STM. Atoms, agents and simple support for transactions meant that managing access to shared data structures was very simple indeed. Then core.async came along and pretty much took over every library and application I tried writing with it. Core.async definitely made the experience of writing Clojurescript more elegant given its hosting environment and the forced asynchronisity of the JavaScript world. Setting up your entire application as a series of communicating sequential processes around managable chunks of state is definitely something to be celebrated in that ecosystem.

This is where the love affair starts to unravel however as there is some amount of pain this world.

The pain

Stack traces and errors; Ever seen a Java stack trace? Now add a few more pages of scrolling for all the Java written to make Clojure possible (shudder) - now make those errors occur inside a core.async block and be amazed if anything useful gets dumped out as your application ceases to work.

Start-up time; build a real application, now it takes 30 seconds to start-up on my MBA before hitting any actual code. The ardent Clojurites therefore commit a great deal of time and energy avoiding ever having to bounce the REPL - indeed designing their entire systems around development from a REPL standpoint. That isn't such an awful thing because I really enjoyed the feedback loop that REPL-first development provides and keeping small parts of your system bootstrappable makes it easy to write tests when needed but having this start-up time on my pet database and having to re-engineer it around not wanting to bounce the REPL just felt awkward.

The real world: Occasionally when programming it becomes useful to do things like read and write from files/sockets/etc. In Haskell we talk about Laziness requiring Purity, in most other places we're Strict and Impure (Unless you count the 100s of gloriously awful things done in the name of LINQ/C#). Clojure is both Lazy and Impure with no real control over where those side effects take place. You can call a seemingly pure function that calls a dozen other pure fuctions and at the bottom somebody is holding onto an atom just to spite you. That's a made up problem but replace that atom with a file handle and we start to have issues.

Types: Or lack of; we're lazy and impure and we have no type system, combine that with a complete lack of decent error handling constructs and giant stack traces and say hello to wasting hours debugging problems if they're so cruel as to slip through your REPL driven development process. Yes there is core.typed which is a wonderful project but it makes Erlang's spec notation looks beautiful and completely ruins the elegance of the original code. A big bucket of nope.

JVM: 'nuff said.

Rather than just repeat myself, there is a 10 minute video and slides of me trolling a Clojure conference with a talk on this very subject.

The path towards Erlang

So okay; simplifying things a lot - you either need to pass file handles (or something that represents them, so handle handles) up to the users of your library for short lived access or wrap up long lived resources in core.async blocks to manage concurrent access to them.

This ends up looking like this (keeping most of the code out of the core.async block so it can be tested in the REPL easily)

(defn go-index-head [_ {:keys [command-channel] :as engine}]   (debug "being asked to start")   (go     (loop [state (initial-state engine)]     (if-let [{:keys [cmd data]} (<! command-channel)]      (do       (debug "handling index loop command" cmd)        (recur (case cmd          :schedule-indexing (main-indexing-process state)          :notify-finished-indexing (main-indexing-process-ended state)          :new-index (add-chaser state data)          :chaser-finished (finish-chaser state data)          :storage-request (storage-request state data))))       (do         (debug "being asked to shut down")         (wait-for-main-indexing state)         (wait-for-chasers state)         (close-open-indexes state)))))) 

Basically re-invent actors but without decent error handling, supervision trees, named processes... So yeah - Clojure, glorious syntax, clever libraries, great data structures, but it's not Erlang for getting things done in the kind of projects I'm working on at the moment.

Where is it then?

I can't see myself using Clojure as a backend language on its own anytime soon. If I'm forced to do something on the JVM (legacy integration, cross my fingers I haven't got to do that for a while) I can see myself using Clojure to integrate with the legacy syste and export data using the wonderful library Liberator.

I can see myself using Clojurescript rather than the mess that is JS if I have to do any higher value front-end code. (Some of the React wrappers look amazing) and it's a lot more professional than trying to bodge it together in a broken language. There is a project coming up next year that represents value on the front-end and I suspect it'll make an appearance there if I can persuade our editors to do sensible CLJS/REPL integration and I can persuade my colleagues to adopt a REPL driven development method on the front-end.

Position on the curve: Still interested, I just don't have use for it at the moment.

      

Permalink

My First Leiningen Template

Every time I sit down to write a quick piece of code for a blog post, it starts with "lein new." This is amazing and wonderful: it's super fast to set up a clean project. Good practice, good play.[1]

But not fast enough! I usually start with a property-based test, so the first thing I do every time is add test.check to the classpath, and import generators and properties and defspec in the test file. And now that I've got the hang of declaring input and output types with prismatic.schema, I want that everywhere too.

I can't bring myself to do this again - it's time to shave the yak and make my own leiningen template.

The instructions are good, but there are some quirks. Here's how to make your own personal template, bringing your own favorite libraries in every time.

It's less confusing if the template project directory is not exactly the template name, so start with:

  lein new template your-name --to-dir your-name-template
  cd your-name-template

Next, all the files in that directory are boring. Pretty them up if you want, but the meat is down in src/leiningen/new.

In src/leiningen/new/your-name.clj is the code that will create the project when your template is activated. This is where you'll calculate anything you need to include in your template, and render files into the right location. The template template gives you one that's pretty useless, so I dugging into leiningen's code to steal and modify the default template's definition. Here's mine:

(defn jessitron
 [name]
 (let [data {:name name
             :sanitized (sanitize name)
             :year (year)}]
  (main/info "Generating fresh project with test.check and schema.")
  (->files data
     ["src/{{sanitized}}/core.clj" (render "core.clj" data)]
     ["project.clj" (render "project.clj" data)]
     ["README.md" (render "README.md" data)]
     ["LICENSE" (render "LICENSE" data)]
     [".gitignore" (render "gitignore" data)]
     ["test/{{sanitized}}/core_test.clj" (render "test.clj" data)]))

As input, we get the name of the project that someone is creating with our template.
The data map contains information available to the templates: that's both the destination file names and the initial file contents. Put whatever you like in here.
Then, set the message that will appear when you use the template.
Finally, there's a vector of destinations, paired with renderings from source templates.

Next, find the template files in src/leiningen/new/your-name/. By default, there's only one. I stole the ones leiningen uses for the default template, from here. They didn't work for me immediately, though: they referenced some data, such as {{namespace}}, that wasn't in the data map. Dunno how that works in real life; I changed them to use {{name}} and other items provided in the data.

When it's time to test, two choices: go to the root of your template directory, and use it.

lein new your-name shiny-new-project

This feels weird, calling lein new within a project, but it works. Now
cd shiny-new-project
lein test

and check for problems. Delete, change the template, try again.

Once it works, you'll want to use the template outside the template project. To get this to work, first edit project.clj, and remove -SNAPSHOT from the project version.[3] Then

lein install

Done! From now on I can lein new your-name shiny-new-project all day long.

And now that I have it, maybe I'll get back to the post I was trying to write when I refused to add test.check manually one last time.


[1] Please please will somebody make this for sbt? Starting a Scala project is a pain in the arse[2] compared to "lein new," which leans me toward Clojure over Scala for toy projects, and therefore real projects.

[2] and don't say use IntelliJ, it's even more painful there to start a new Scala project.

[3] At least for me, this was necessary. lein install didn't get it into my classpath until I declared it a real (non-snapshot) version.

Permalink

Improving compile times

Today’s post is about a compiler change in 1.7.0-alpha4 that reduces compile times on some (not all) projects, as tracked in CLJ-1529. This ticket was filed by the perspicacious Zach Tellman. Zach was specifically seeing issues with the compile times for his Aleph project and did a bit of profiling to find a hot spot.

In particular this method in the compiler code for host expressions (version before patch seen here). The purpose of this code is to determine whether a given form used in a host expression refers to a Class. The stringOk flag indicates whether a String representing a class name is valid too.

private static Class maybeClass(Object form, boolean stringOk) {
  if(form instanceof Class)
    return (Class) form;
  Class c = null;
  if(form instanceof Symbol) {
    Symbol sym = (Symbol) form;
    if(sym.ns == null) //if ns-qualified can't be classname
    {
      if(Util.equals(sym,COMPILE_STUB_SYM.get()))
        return (Class) COMPILE_STUB_CLASS.get();
      if(sym.name.indexOf('.') > 0 || sym.name.charAt(0) == '[')
        c = RT.classForName(sym.name);
      else {
        Object o = currentNS().getMapping(sym);
        if(o instanceof Class)
          c = (Class) o;
        else {
          try{
            c = RT.classForName(sym.name);
          } catch(Exception e){
            // aargh
            // leave c set to null -> return null
          }
        }
      }
    }
  }
  else if(stringOk && form instanceof String)
    c = RT.classForName((String) form);
  return c;
}

This method is used in several circumstances. A particularly problematic path occurs during macro-expansion, in trying to understand a host interop call like (.append sb (char c)). In this case, the question to be resolved is whether “sb” is a class or a reference to an instance.

The code above will fall all the way through to the RT.classForName() call. Asking a classloader to speculatively load the class just to determine whether it really is a class is expensive.

As it turns out, Rich Hickey had found this same hot spot earlier this year when working on investigating lazy var loading in his work on the fastload branch. Both Zach and Rich solved the problem in different ways. Zach proposed a modification to maybeClass to check whether the symbol in question was a symbol that had meaning in the local scope at the point of the check:

if(LOCAL_ENV.deref() != null && ((java.util.Map)LOCAL_ENV.deref()).containsKey(form)) {
    return null;	
  }

Zach inserted this right after casting form to sym. Unfortunately this had the side effect of altering the semantics of some (perhaps pathological) code like this:

(let [String "foo"] 
      (. String substring 0 1))

Before the patch, the String in the interop call would be treated as a class and after the patch it would be treated as a method invocation on the lexical instance. This change in semantics gave us some heartburn because while this situation is probably rare, we were unsure whether it would break existing code in the wild.

I decided to extract Rich’s approach from the fastload branch and take a look at that as an alternative. Rich’s approach was to create a cache around this maybeClass question and remember the answer.

Once I had two patches in hand, it was time to collect some data. I went out and tested the time to build and run the tests in a varied set of open source Clojure projects. I found that both patches helped but Zach’s patch was definitely making a bigger impact, particularly on macro-heavy projects like aleph, riemann, and lamina.

I went back to the patches and took another look at both of them. I was able to find a few additional efficiences in the cache case, and also get a better handle on the set of cases where semantics were changing from Zach’s patch. I eventually found that it was possible to delay Zach’s check till later in the logic, past the check for “.”’s and still catch all of the same cases without the change in semantics.

With the combination of all these changes, I made two versions of the patch - one that was the original patch changes plus the semantic change and another that added the cache as well. In testing I found that the version with the cache did performly slightly better (it caught a slightly broader set of cases) but really virtually all of the improvement was due to the locals check.

Based on that, we decided the complexity of the cache was not worth the slight benefit and only the simpler version was included in 1.7.0-alpha4. As it turns out, aleph seemed to be the project with the greatest benefit, going from 25.4 sec to 14.8 sec to run “lein test”, a really significant reduction in time. A number of other projects have also reported a signficant reduction in compile time with the alpha4 release. I’m very happy we were able to clean this up and get it included. Thanks Zach for filing the ticket and the patch!

Permalink

Couple of DataScript resources

There’s couple of new resources available about DataScript.

On December 4th I gave a talk at Clojure eXchange conference about motivation behind DataScript, a little bit about internals, and then about how DataScript can be used for application development. Beyond traditional SPAs, there were couple of examples of new kind of architectures that are trivial to execute given that DataScript exists.

You can watch video of the talk at SkillsMatter website (free, registration required) and check out slides:

Later this month I talked at ClojureScript NYC user group. During the webinar we developed ToDo application from scratch and touched, at least quickly, almost every aspect of DataScript. Here’s the agenda:

  • Create DB schema (multi-valued relations, references)
  • Add ability to create tasks (basic transact!)
  • Display list of tasks (basic query)
  • Display tags on tasks (multi-valued attrs)
  • Persist database to localStorage (serialization/deserialization)
  • Make tasks completable (transact functions)
  • Assign projects to tasks (entity navigation)
  • Display task count for projects (aggregate queries)
  • Display task count for inbox (“negate” query, query functions, query predicates)
  • Display “by month” grouping (custom fn call in a query)
  • Make left panel navigable (storing “view” app state in a db)
  • Add filter (implicit OR via rules and collection bindings)

The recording:

After the webinar I fixed couple of bugs in ToDo repo (and in DataScript as well), added comments here and there explaining what’s going on and implemented couple of new features:

  • DB filtering
  • Serialization via transit-cljs
  • History tracking and undo/redo support

DataScript-ToDo should be a good resource for learning DataScript and its applications in the wild. Source code is on github, live version here:

Stay tuned!

Permalink

Creating an interpose transducer

Continuing from yesterday’s post, I wanted to cover the interpose transducer from CLJ-1601.

The sequence version of interpose is a straightforward combination of other existing sequence functions (repeat, interleave, and drop). It’s implemented like this:

(defn interpose [sep coll]
 	(drop 1 (interleave (repeat sep) coll)))

Walking inside out, (repeat seq) will create an infinite sequence of separator strings. (interleave (repeat sep) coll) will interleave the infinite separate seq and the collection (possibly also an infinite sequence!) like this:

sep elem0 sep elem1 sep elem2

And finally the (drop 1) loses the first separator which is unnecessary.

In the transducer version, I chose to use a volatile to store a flag about whether this was the first input element. In the case of the first input element, we simply update the flag and invoke the inner reducing function on the first element. This effectively does the “drop 1” behavior of the sequence implementation. Forever after, we invoke the reducing function on both the separator and then on the element:

(defn interpose
  ([sep]
   (fn [rf]
     (let [started (volatile! false)]
       (fn
         ([] (rf))
         ([result] (rf result))
         ([result input]
          (if @started
            (let [sepr (rf result sep)]
              (if (reduced? sepr)
                sepr
                (rf sepr input)))
            (do
              (vreset! started true)
              (rf result input))))))))

As with distinct, the started flag is a volatile created once per transducing process and the real business happens in the reducing arity.

One issue that we need to deal with is being aware of reduced values. The calls to rf on the input are fine - they may return a reduced value that will be dealt with at a higher level (ultimately the transducing process itself). The special case is when a reduced value is returned from the separator. An example where this could happen would be:

(into [] (comp (interpose :x) (take 4)) (range 3))

The (range 3) produces sequence (0 1 2). The interpose should produce (0 :x 1 :x 2). The take should then grab just (0 :x 1 :x) and the reduced wrapper will be sent on a separator (:x).

So in the transducer code, we need to check if we’ve already encountered a reduced value when we invoke rf on the sepr, and if so stop and return the reduced value without invoking on the next input.

That’s it! Quick performance comparison:

expr time
(into [] (interpose nil v)) 316.0 µs
(into [] (interpose nil) v) 35.5 µs

This code has not yet been screened or added to 1.7, but I expect that it will be.

Permalink

Validateur 2.4.2 is released

TL;DR

Validateur is a functional validations library inspired by Ruby’s ActiveModel. Validateur 2.4 is a minor feature release.

Changes Between 2.3.0 and 2.4.0

Clojure 1.4 Support Dropped

The project no longer tries to maintain Clojure 1.4 compatibility.

validate-some

validate-some tries any number of validators, short-circuiting at the first failed validator. This behavior is similar to or.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(require '[validateur.validation :refer :all])

(let [v (validate-some
         (presence-of :cake-count :message "missing_cake")
         (validate-by :cake-count odd? :message "even_cake"))]

  "Odd cake counts are valid."
  (v {:cake-count 1})
  ;;=> [true #{}]


  "Even cake counts only throw the second error, since the first
  validation passed."
  (v {:cake-count 2})
  ;;=> [false {:cake-count #{"even_cake"}}]

  "The second validation never gets called and never throws a NPE, as
  it would if we just composed them up."
  (v {})
  ;;=> [false {:cake-count #{"missing_cake"}}]
  )

Contributed by Sam Ritchie (PaddleGuru).

errors? and errors

Errors in validateur are vectors if keys are nested. If keys are only one layer deep – :cake, for example – the error can live at :cake or [:cake].

The errors function returns the set of errors for some key, nested or bare. :cake will return errors stored under [:cake] and vice-versa.

errors? is a boolean wrapper that returns true if some key has errors, false otherwise.

Contributed by Sam Ritchie (PaddleGuru).

Full Change Log

Validateur change log is available on GitHub.

Validateur is a ClojureWerkz Project

Validateur is part of the group of libraries known as ClojureWerkz, together with

  • Langohr, a Clojure client for RabbitMQ that embraces the AMQP 0.9.1 model
  • Monger, a Clojure MongoDB client for a more civilized age
  • Elastisch, a minimalistic Clojure client for ElasticSearch
  • Cassaforte, a Clojure Cassandra client built around CQL
  • Neocons, a client for the Neo4J REST API
  • Welle, a Riak client with batteries included
  • Quartzite, a powerful scheduling library

and several others. If you like Validateur, you may also like our other projects.

Let us know what you think on Twitter or on the Clojure mailing list.

About The Author

Michael on behalf of the ClojureWerkz Team

Permalink

Greetings from the Functional Frontier!

or, How We Lost Grandma to Dysentary but Gained Stateless UI

We had a blast at Prismatic HQ last week, with a lineup of talks exploring the leading edge of functional programming on the frontend:

Prismatic's own Logan Linn kicked off the evening explaining how we increase predictability and reduce complexity at Prismatic using single-direction data flows with ClojureScript.

Jordan Garcia of Optimizely followed, with a talk on using immutability, one-way data flow and pure functions in the frontend with javascript. He also showed us how they're using Flux at Optimizely as a decoupled way to model application and UI state.

Richard Feldman of NoRedInk introduced us to Elm, a functional frontend language for making stateless UIs in the browser. He first tried Elm when building his own personal project, Dreamwriter, and fell down a glorious rabbit hole of immutable data and stateless functions from which he has yet to emerge.

For those who joined us, thanks so much for making the event such a blast. Thanks also to Marco, our esteemed MC for the evening, who dazzled the audience with his array of nerdy programming jokes.

For those who couldn’t make it (or just really want to hear Marco’s jokes again), click through to watch the videos of the entire event:

Part I: Logan Linn

Part II: Jordan Garcia

Part III: Richard Feldman

Oh yeah: we also had these sweet 8-bit tshirts designed by Carolyn Li-Madeo, and modeled by Lucy:

which were unimprovable, except perhaps with one clever addition (next time, Bill!)

Permalink

Copyright © 2009, Planet Clojure. No rights reserved.
Planet Clojure is maintained by Baishamapayan Ghose.
Clojure and the Clojure logo are Copyright © 2008-2009, Rich Hickey.
Theme by Brajeshwar.