-
Notifications
You must be signed in to change notification settings - Fork 346
Documentation site overhaul #576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,19 +7,85 @@ position: 5 | |
|
||
# Contributing to Algebird | ||
|
||
This page lists recommendations and requirements for how to best contribute to Algebird. | ||
This page lists recommendations and requirements for how to best contribute to Algebird. We strive to obey these as best as possible. As always, thanks for contributing--we hope these guidelines make it easier and shed some light on our approach and processes. | ||
|
||
We strive to obey these as best as possible. As always, thanks for contributing--we hope these guidelines make it easier and shed some light on our approach and processes. | ||
## Key branches | ||
|
||
### Key branches | ||
- `master` is the latest, deployed version. | ||
- `develop` is where development happens and all pull requests should be submitted. | ||
|
||
### Pull requests | ||
## Pull requests | ||
|
||
- Submit pull requests against the `develop` branch. | ||
- Try not to pollute your pull request with unintended changes--keep them simple and small. | ||
Submit pull requests against the `develop` branch. Try not to pollute your pull request with unintended changes. Keep it simple and small. | ||
|
||
### License | ||
## Contributing Documentation | ||
|
||
The documentation for Algebird's website is stored in the `docs/src/main/tut` directory of the [docs subproject](https://github.com/twitter/algebird/tree/develop/docs). | ||
|
||
Algebird's documentation is powered by [sbt-microsites](https://47deg.github.io/sbt-microsites/) and [tut](https://github.com/tpolecat/tut). `tut` compiles any code that appears in the documentation, ensuring that snippets and examples won't go out of date. | ||
|
||
We would love your help making our documentation better. If you see a page that's empty or needs work, please send us a pull request making it better. If you contribute a new data structure to Algebird, please add a corresponding documentation page. To do this, you'll need to: | ||
|
||
- Add a new Markdown file to `docs/src/main/tut/datatypes` with the following format: | ||
|
||
```markdown | ||
--- | ||
layout: docs | ||
title: "<Your Page Title>" | ||
section: "data" | ||
source: "algebird-core/src/main/scala/com/twitter/algebird/<YourDataType>.scala" | ||
scaladoc: "#com.twitter.algebird.<YourDataType>" | ||
--- | ||
|
||
# Your Data Type | ||
|
||
..... | ||
``` | ||
|
||
- Make sure to add some code examples! Any code block of this form will get compiled using `tut`: | ||
|
||
|
||
```toot:book | ||
<your code> | ||
``` | ||
|
||
(Please replace `toot` with `tut`!) `tut` will evaluate your code as if you'd pasted it into a REPL and insert each line's results in the output. State persists across `tut` code blocks, so feel free to alternate code blocks with text discussion. See the [tut README](https://github.com/tpolecat/tut) for more information on the various options you can use to customize your code blocks. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was annoying... if I write |
||
- Add your page to the appropriate section in [the menu](https://github.com/twitter/algebird/tree/develop/docs/src/main/resources/microsite/data/menu.yml) | ||
|
||
### Generating the Site | ||
|
||
run `sbt docs/makeMicrosite` to generate a local copy of the microsite. | ||
|
||
### Previewing the site | ||
|
||
1. Install jekyll locally, depending on your platform, you might do this with any of the following commands: | ||
|
||
``` | ||
yum install jekyll | ||
apt-get install jekyll | ||
gem install jekyll | ||
``` | ||
|
||
2. In a shell, navigate to the generated site directory in `docs/target/site` | ||
3. Start jekyll with `jekyll serve --incremental` | ||
4. Navigate to http://127.0.0.1:4000/algebird/ in your browser | ||
5. Make changes to your site, and run `sbt docs/makeMicrosite` to regenerate the site. The changes should be reflected as soon as `sbt docs/makeMicrosite` completes. | ||
|
||
## Post-release | ||
|
||
After the release occurs, you will need to update the documentation. Here is a list of the places that will definitely need to be updated: | ||
|
||
* `README.md`: update version numbers | ||
* `CHANGES.md`: summarize changes since last release | ||
|
||
(Other changes may be necessary, especially for large releases.) | ||
|
||
You can get a list of changes between release tags `v0.1.2` and `v0.2.0` via `git log v0.1.2..v0.2.0`. Scanning this list of commit messages is a good way to get a summary of what happened, although it does not account for conversations that occured on Github. | ||
|
||
Once the relevant documentation changes have been committed, new [release notes](https://github.com/twitter/algebird/releases) should be added. You can add a release by clicking the "Draft a new release" button on that page, or if the relevant release already exists, you can click "Edit release". | ||
|
||
The website should then be updated via `sbt docs/publishMicrosite`. | ||
|
||
## License | ||
|
||
By contributing your code, you agree to license your contribution under the terms of the [APLv2](LICENSE). |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -194,7 +194,7 @@ object ExpHist { | |
|
||
// Drops all buckets with an expired timestamp, based on the | ||
// configured window and the supplied current time. | ||
def dropExpired(buckets: Vector[Bucket], currTime: Timestamp): (Long, Vector[Bucket]) = | ||
private[algebird] def dropExpired(buckets: Vector[Bucket], currTime: Timestamp): (Long, Vector[Bucket]) = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we haven't published yet, so it's fine to break here. |
||
ExpHist.dropExpired(buckets, expiration(currTime)) | ||
|
||
/** | ||
|
@@ -225,7 +225,7 @@ object ExpHist { | |
* @param cutoff buckets with ts <= cutoff are expired | ||
* @return the sum of evicted bucket sizes and the unexpired buckets | ||
*/ | ||
def dropExpired(buckets: Vector[Bucket], cutoff: Timestamp): (Long, Vector[Bucket]) = { | ||
private[algebird] def dropExpired(buckets: Vector[Bucket], cutoff: Timestamp): (Long, Vector[Bucket]) = { | ||
val (dropped, remaining) = buckets.reverse.span(_.timestamp <= cutoff) | ||
(dropped.map(_.size).sum, remaining.reverse) | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,38 +31,6 @@ trait Cuber[I] { | |
def apply(in: I): TraversableOnce[K] | ||
} | ||
|
||
/** | ||
* Given a TupleN, produces a sequence of (N + 1) tuples each of arity N | ||
* such that, for all k from 0 to N, there is a tuple with k Somes | ||
* followed by (N - k) Nones. | ||
* | ||
* This is useful for comparing some metric across multiple layers of | ||
* some hierarchy. | ||
* For example, suppose we have some climate data represented as | ||
* case class Data(continent: String, country: String, city: String, temperature: Double) | ||
* and we want to know the average temperatures of | ||
* - each continent | ||
* - each (continent, country) pair | ||
* - each (continent, country, city) triple | ||
* | ||
* Here we desire the (continent, country) and (continent, country, city) | ||
* pair because, for example, if we grouped by city instead of by | ||
* (continent, country, city), we would accidentally combine the results for | ||
* Paris, Texas and Paris, France. | ||
* | ||
* Then we could do | ||
* > import com.twitter.algebird.macros.Roller.roller | ||
* > val data: List[Data] | ||
* > val averageTemps: Map[(Option[String], Option[String], Option[String]), Double] = | ||
* > data.flatMap { d => roller((d.continent, d.country, d.city)).map((_, d)) } | ||
* > .groupBy(_._1) | ||
* > .mapValues { xs => val temps = xs.map(_.temperature); temps.sum / temps.length } | ||
*/ | ||
trait Roller[I] { | ||
type K | ||
def apply(in: I): TraversableOnce[K] | ||
} | ||
|
||
object Cuber { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved |
||
implicit def cuber[T]: Cuber[T] = macro cuberImpl[T] | ||
|
||
|
@@ -110,52 +78,3 @@ object Cuber { | |
c.Expr[Cuber[T]](cuber) | ||
} | ||
} | ||
|
||
object Roller { | ||
implicit def roller[T]: Roller[T] = macro rollerImpl[T] | ||
|
||
def rollerImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[Roller[T]] = { | ||
import c.universe._ | ||
|
||
ensureCaseClass(c) | ||
|
||
val params = getParams(c) | ||
val arity = params.length | ||
if (arity > 22) | ||
c.abort(c.enclosingPosition, s"Cannot create Roller for $T because it has more than 22 parameters.") | ||
if (arity == 0) | ||
c.abort(c.enclosingPosition, s"Cannot create Roller for $T because it has no parameters.") | ||
|
||
val tupleName = { | ||
val types = getParamTypes(c) | ||
val optionTypes = types.map { t => tq"_root_.scala.Option[$t]" } | ||
val tupleType = newTypeName(s"Tuple${arity}") | ||
tq"_root_.scala.$tupleType[..$optionTypes]" | ||
} | ||
|
||
val somes = params.zip(Stream.from(1)).map { | ||
case (param, index) => | ||
val name = newTermName(s"some$index") | ||
q"val $name = _root_.scala.Some(in.$param)" | ||
} | ||
|
||
val items = (0 to arity).map { i => | ||
val args = (1 to arity).map { index => | ||
val some = newTermName(s"some$index") | ||
if (index <= i) q"$some" else q"_root_.scala.None" | ||
} | ||
q"new K(..$args)" | ||
} | ||
|
||
val roller = q""" | ||
new _root_.com.twitter.algebird.macros.Roller[${T}] { | ||
type K = $tupleName | ||
def apply(in: ${T}): _root_.scala.Seq[K] = { | ||
..$somes | ||
Seq(..$items) | ||
} | ||
} | ||
""" | ||
c.Expr[Roller[T]](roller) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package com.twitter.algebird.macros | ||
|
||
import scala.language.experimental.{ macros => sMacros } | ||
import scala.reflect.macros.Context | ||
import scala.reflect.runtime.universe._ | ||
|
||
/** | ||
* Given a TupleN, produces a sequence of (N + 1) tuples each of arity N | ||
* such that, for all k from 0 to N, there is a tuple with k Somes | ||
* followed by (N - k) Nones. | ||
* | ||
* This is useful for comparing some metric across multiple layers of | ||
* some hierarchy. | ||
* For example, suppose we have some climate data represented as | ||
* case class Data(continent: String, country: String, city: String, temperature: Double) | ||
* and we want to know the average temperatures of | ||
* - each continent | ||
* - each (continent, country) pair | ||
* - each (continent, country, city) triple | ||
* | ||
* Here we desire the (continent, country) and (continent, country, city) | ||
* pair because, for example, if we grouped by city instead of by | ||
* (continent, country, city), we would accidentally combine the results for | ||
* Paris, Texas and Paris, France. | ||
* | ||
* Then we could do | ||
* > import com.twitter.algebird.macros.Roller.roller | ||
* > val data: List[Data] | ||
* > val averageTemps: Map[(Option[String], Option[String], Option[String]), Double] = | ||
* > data.flatMap { d => roller((d.continent, d.country, d.city)).map((_, d)) } | ||
* > .groupBy(_._1) | ||
* > .mapValues { xs => val temps = xs.map(_.temperature); temps.sum / temps.length } | ||
*/ | ||
trait Roller[I] { | ||
type K | ||
def apply(in: I): TraversableOnce[K] | ||
} | ||
|
||
object Roller { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this data structure has no changes. I moved it out of |
||
implicit def roller[T]: Roller[T] = macro rollerImpl[T] | ||
|
||
def rollerImpl[T](c: Context)(implicit T: c.WeakTypeTag[T]): c.Expr[Roller[T]] = { | ||
import c.universe._ | ||
|
||
ensureCaseClass(c) | ||
|
||
val params = getParams(c) | ||
val arity = params.length | ||
if (arity > 22) | ||
c.abort(c.enclosingPosition, s"Cannot create Roller for $T because it has more than 22 parameters.") | ||
if (arity == 0) | ||
c.abort(c.enclosingPosition, s"Cannot create Roller for $T because it has no parameters.") | ||
|
||
val tupleName = { | ||
val types = getParamTypes(c) | ||
val optionTypes = types.map { t => tq"_root_.scala.Option[$t]" } | ||
val tupleType = newTypeName(s"Tuple${arity}") | ||
tq"_root_.scala.$tupleType[..$optionTypes]" | ||
} | ||
|
||
val somes = params.zip(Stream.from(1)).map { | ||
case (param, index) => | ||
val name = newTermName(s"some$index") | ||
q"val $name = _root_.scala.Some(in.$param)" | ||
} | ||
|
||
val items = (0 to arity).map { i => | ||
val args = (1 to arity).map { index => | ||
val some = newTermName(s"some$index") | ||
if (index <= i) q"$some" else q"_root_.scala.None" | ||
} | ||
q"new K(..$args)" | ||
} | ||
|
||
val roller = q""" | ||
new _root_.com.twitter.algebird.macros.Roller[${T}] { | ||
type K = $tupleName | ||
def apply(in: ${T}): _root_.scala.Seq[K] = { | ||
..$somes | ||
Seq(..$items) | ||
} | ||
} | ||
""" | ||
c.Expr[Roller[T]](roller) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,23 +28,17 @@ class CombinatorTest extends CheckProperties { | |
Arbitrary.arbitrary[T].map { t => Max(t) } | ||
} | ||
|
||
implicit val sg: Semigroup[(Max[Int], List[Int])] = | ||
new SemigroupCombinator({ (m: Max[Int], l: List[Int]) => | ||
val sortfn = { (i: Int) => i % (scala.math.sqrt(m.get.toLong - Int.MinValue).toInt + 1) } | ||
l.sortWith { (l, r) => | ||
val (sl, sr) = (sortfn(l), sortfn(r)) | ||
if (sl == sr) l < r else sl < sr | ||
} | ||
}) | ||
private def fold(m: Max[Int], l: List[Int]): List[Int] = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I pulled the |
||
val sortfn = { (i: Int) => i % (scala.math.sqrt(m.get.toLong - Int.MinValue).toInt + 1) } | ||
l.sortWith { (l, r) => | ||
val (sl, sr) = (sortfn(l), sortfn(r)) | ||
if (sl == sr) l < r else sl < sr | ||
} | ||
} | ||
|
||
implicit val sg: Semigroup[(Max[Int], List[Int])] = new SemigroupCombinator(fold) | ||
implicit val mond: Monoid[(Max[Int], List[Int])] = new MonoidCombinator(fold) | ||
|
||
implicit val mond: Monoid[(Max[Int], List[Int])] = | ||
new MonoidCombinator({ (m: Max[Int], l: List[Int]) => | ||
val sortfn = { (i: Int) => i % (scala.math.sqrt(m.get.toLong - Int.MinValue).toInt + 1) } | ||
l.sortWith { (l, r) => | ||
val (sl, sr) = (sortfn(l), sortfn(r)) | ||
if (sl == sr) l < r else sl < sr | ||
} | ||
}) | ||
// Make sure the lists start sorted: | ||
implicit def pairArb(implicit lista: Arbitrary[List[Int]]): Arbitrary[(Max[Int], List[Int])] = | ||
Arbitrary { | ||
|
@@ -84,5 +78,4 @@ class CombinatorTest extends CheckProperties { | |
property("MonoidCombinator with top-K forms a Monoid") { | ||
monoidLaws[(Map[Int, Int], Set[Int])] | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this webhook reports our build status to gitter.