Contrib library readmes should contain:
Instructions for including the library as a dependency in Maven / Leiningen
Links to the library’s pages on Jenkins and JIRA
Links to the available releases on Maven Central and oss.sonatype.org
Link to generated API documentation, if available
General usage instructions (should mention which namespace to use/require)
Developer Information: links to GitHub project, Bug Tracker, Continuous Integration, Compatibility Test Matrix
Change log of all releases (may also be in separate file)
Things you should do if you are a Clojure Contrib committer:
maintain your library and respond to questions/issues that arise
do your work on the master branch, or (if you are working on a significant chunk you want to keep temporarily separate) on a feature-specific branch that you create yourself
use the GitHub "Release on demand" Action to make releases
coordinate with other committers before making changes to their libraries
accept contributions from others only if they have signed the CA
Things to avoid:
please do not push to the release branches (names like 1.2.x)
do not take non-contributor patches
please do not take pull requests from contributors. Patches only.
do not change the version number in pom.xml - use the Maven Release process mentioned above
Here’s the process outline for what it takes to become a committer:
Get your CA on file
Join the clojure-dev mailing list
Create a JIRA account
Let Clojure core team know your github username and JIRA username so they can set up the correct permissions
Clojure core team need to create your account on build.clojure.org as well - see below
Moving an existing project into contrib:
All past contributors must:
Submit a Clojure Contributor Agreement
Send an email to the clojure-dev mailing list granting permission, such as: "I, (NAME), give my permission to release my contributions to (PROJECT) under the Clojure Contributor Agreement."
Setting up a new contrib project:
Email clojure-dev mailing list to get new project approved and admin privileges in GitHub, Jira, and Jenkins.
Ask for a new GitHub repo under the clojure organization
Specify project name (must be approved by Clojure core)
Specify description
Collaborators - add Team: Contrib Commit which includes:
clojure-build - for Jenkins to tag releases, build autodocs, etc
Disable Issues tab (we use JIRA instead)
Project structure (see existing projects for example)
/README.md - readme, see above
/CHANGES.md - changelog
/CONTRIBUTING.md - example
/epl.html - EPL license information
/pom.xml - according to the build.poms instructions - used for build/deploy
/src/main/clojure - Clojure sources
/src/test/clojure - Clojure tests
/src/main/cljs - ClojureScript sources
/src/test/cljs - ClojureScript tests
/src/main/java - Java sources, if needed
Create a new JIRA project (requires JIRA admin privileges):
Specify name (same as GitHub project name)
Specify key (approved by Clojure core, derived from project name) - should usually be first char of first part with up to 5 chars of second part - TBENCH, DJSON, etc.
Specify project lead’s JIRA account
Edit project to add url and description (same as GitHub project)
Set notification scheme - usually "Default scheme plus notify project lead"
Setting up builds (requires Jenkins admin privileges, except step 2):
Create Jenkins user account for authors
Edit ci_data.clj in the build.ci repo add the new project / update authors (so they can run builds / cut releases)
Request on clojure-dev mailing list to run build.ci Jenkins job - this will recreate all Jenkins job definition files!
Force Jenkins to reload its configuration files
Autodoc
WIP
Performing releases
Snapshot releases are automatically created every time the job builds (triggered by any source change)
To use snapshots, see Maven Settings and Repositories
Perform a release according to How to Make Releases section below
Prep
Your project must have a pom.xml file with a -SNAPSHOT version
The pom.xml file must specify a parent, the latest released version of pom.contrib in build.poms
How to make a -SNAPSHOT release
Your project must have a pom.xml file with a -SNAPSHOT version
Push to "master" branch on GitHub
Jenkins polls GitHub and builds automatically
Or you can click "Build Now" on the project page
Jenkins builds and uploads a uniquely-numbered JAR file to the Sonatype OSS Snapshot Repository
How to make a numbered release
The "master" branch in GitHub must have a pom.xml file with a -SNAPSHOT version, not a bare version number
Log in to Jenkins
Navigate to your project’s job
Click "Perform Maven Release" link on the left
On the "Perform Maven Release" page:
Select "Specify one version for all modules"
In the "Release Version" field, enter the version number for this release of your project
This will normally be the current development version with the "-SNAPSHOT" suffix removed
In the "Development version" field, enter the version number for the subsequent development version of your project
This will end with "-SNAPSHOT"
Click "Schedule Maven Release Build"
After the build completes successfully:
git pull
on your development machine to get the new release tags
The release JAR file will be uploaded to the Sonatype OSS staging repository
The release will automatically be copied to the Maven Central repository within 24 hours (usually within 15 minutes)
Don’t forget to update the project README if it recommends a version to users.
Contrib Release Numbering Policy
major.minor.patch
Follow guidelines for accretion and fixation, not breakage, if at all possible
Disclaimer:
Rules are made to be broken. Know the standards, but do not treat them as absolutes.
The Standards:
Get the name and signature right. Rich strongly respects Java’s commitment to not break existing code. In practice, that means we can tweak the implementation forever, but once we publish a name and signature we need to stick with it. (In practice I think this means that we want many people to review the name and sig, even if they don’t review the implementation details.)
Use type hints for functions that are likely to be on critical code; otherwise keep code simple and hint-free.
Only use type hints that matter. If you are not certain a type hint helps, don’t add it.
Use good names, and don’t be afraid to collide with names in other namespaces. That’s what the flexible namespace support is there for.
OTOH, using the same name with a different signature or semantics begs the question as to whether one of them is less than ideal.
Be explicit and minimalist about dependencies on other packages. (Prefer :require :refer to :use)
Don’t use a macro when a function can do the job. If a macro is important for ease-of-use, expose the function version as well.
If you are sure you have all the information at compile time, use a macro where it would improve performance sensitive code.
Provide a library-level docstring.
Provide automated tests.
Use '?' suffix for predicates, and return booleans.
Use '_' for destructuring targets and formal arguments names whose value will be ignored by the code at hand.
Include a docstring.
When in doubt, expose the performant version. Clojure goes to great lengths to enable performance when you need it, and lib should too. (That’s why we don’t have multimethod + in core, for instance.) Users can always create more polymorphic APIs on their own, hijacking symbols if they want to.
If you take a good name that collides with core, make sure your semantics are parallel (possibly minus laziness). Good example of this is string functions that shadow core seq functions.
Use assert and pre- and post- conditions.
Be lazy where possible.
Follow clojure.core’s example for idiomatic names like pred and coll.
in fns
f, g, h - function input
n - integer input usually a size
index - integer index
x, y - numbers
s - string input
coll - a collection
pred - a predicate closure
& more - variadic input
in macros
expr - an expression
body - a macro body
binding - a macro binding vector
Do NOT follow idioms from clojure.core’s preamble code. That code runs in a limited environment because Clojure is not bootstrapped yet.
Decompose the pieces. If your name isn’t Rich, don’t write a form as long as, say, the definition of doseq.
Use keyword-first syntax to access properties on objects: (:property object-like-map)
Use collection-first syntax to extract values from a collection (or use get if the collection might be nil): (collection-like-map key)
or (get collection-like-map key)
. Note that not all collections are keyed by keyword.
Idiomatic code uses destructuring a lot. However, you should only destructure in the arg list if you want to communicate the substructure as part of the caller contract. Otherwise, destructure in a first-line let.
Prefer updating over setting. Many reasons: the unified update model provides a simple standard way to do this. Helps you discover commutative operations. Reduces the surface area of assumptions you are making about the object you are updating.
Don’t support operations on the wrong collection type. If your algorithm is only performant with random access, then require an arg that has random access.
Use *earmuffs* only for things intended for rebinding. Don’t use a special notation for constants; everything is assumed a constant unless specified otherwise.
Use the bang! only for things not safe in an STM transaction.
Prefer sequence-library composition over explicit loop/recur.
Rebindable vars should be paired with scoping macros, e.g. in and with-in-str.
Lazy seqs should be exposed as functions that hold only the minimum state needed, a.k.a. "let go of your head." Let the caller decide how much local memory they want to use.
Use Klass/staticField, (Klass/staticMethod), (Klass.) and (.method obj) interop styles with the only exception being in code-generating-code where the older (. obj method) style may be easier to produce.
If you present an interface that implicitly passes a parameter via dynamic binding (e.g. db in sql), also provide an identical interface but with the parameter passed explicitly.
When providing a default case for cond, use the keyword :else as a condition instead of true
To access a private var (e.g. for testing), use the @#'some.ns/var form
Protocols:
One should only extend a protocol to a type if they own either the type or the protocol.
If one breaks the previous rule, they should be prepared to withdraw, should the implementor of either provide a definition
If a protocol comes with Clojure itself, avoid extending it to types you don’t own, especially e.g. java.lang.String and other core Java interfaces. Rest assured if a protocol should extend to it, it will, else lobby for it.
The motive is, as stated by Rich Hickey, to prevent "people extend protocols to types for which they don’t make sense, e.g. for which the protocol authors considered but rejected an implementation due to a semantic mismatch.". "No extension will be there (by design), and people without sufficient understanding/skills might fill the void with broken ideas."