Skip to content

Commit ec415cd

Browse files
authored
Feature/tut docs (#75)
* Adding tut plugin * Add in readme for the project * Updating the documentation to use tut * Adding examples of how to derive new types. * Simplifying the logic with env variables * Removing assertions test as assertions have been removed. * Updating patience configuration * Adding environment exports * Trying to fix Travis logic * Adding docs folder output * Changing entrypoint into readme
1 parent 20ffacf commit ec415cd

File tree

15 files changed

+585
-495
lines changed

15 files changed

+585
-495
lines changed

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@ branches:
1717
- develop
1818
matrix:
1919
include:
20-
- scala: 2.12.6
20+
- scala: 2.12.7
2121
jdk: oraclejdk8
22+
env: RUN_WITH_COVERAGE=true PUBLISH_ARTIFACT=true
2223

2324
env:
2425
global:
26+
- RUN_WITH_COVERAGE: false
27+
- PUBLISH_ARTIFACT: false
2528
- GH_REF: github.com/outworkers/util.git
2629
- secure: Onl6jQhDgCHVhsxIhC2FltwTlvTWI5815lI9wsb79OvE+Xl/hh8XcafOBzUJ/LtKmt021oieOsR53RAdIJDKhNrKo3AQYoyp3rAX48zCInE0Y29slKVCwj51w5Mns+aYlPbJcHJvRNWkFIpaQ1AmBvkHfc0A0rxfoSB1lOIrtHs=
2730
- secure: k/DGy5KkvzmQNJEfazsEoD6biwkIoYC9DyjhDTMCxhXLz/mURsCtfhdWo3Uz4nhuX3qDK0N/6C6BTwl0ktVEA7eH8XZQY/dW1lLTY3jglD2U/FhAfCngbcI+ToL5kdK77Zy4LKvu4XBKXfSusI20E1gbK+Tjp1uCkNvVaAyyVv0=

README.md

Lines changed: 2 additions & 333 deletions
Original file line numberDiff line numberDiff line change
@@ -7,341 +7,10 @@ latest version of `util` available. The badges are automatically updated when a
77

88
![Util](https://s3-eu-west-1.amazonaws.com/websudos/oss/logos/util.png "Outworkers Util")
99

10-
### Table of contents ###
10+
### Documentation
1111

12-
<ol>
13-
<li><a href="#integrating-the-util-library">Integrating the util library</a></li>
14-
15-
<li>
16-
<p><a href="#util-parsers">util-parsers</a></p>
17-
<ul>
18-
<li><a href="#option-parsers">Option parsers</a></li>
19-
<li><a href="#applicative-parsers">Applicative parsers</a></li>
20-
</ul>
21-
</li>
22-
23-
<li>
24-
<p><a href="#util-parsers">util-parsers-cats</a></p>
25-
<ul>
26-
<li><a href="#option-parsers">Option parsers</a></li>
27-
<li><a href="#applicative-parsers">Applicative parsers</a></li>
28-
</ul>
29-
</li>
30-
31-
<li>
32-
<p><a href="#util-testing">util-testing</a></p>
33-
<ul>
34-
<li><a href="#async-assertions">Async assertions</a></li>
35-
<li><a href="#data-sampling">Data sampling</a></li>
36-
<li><a href="#automated-sampling">Automated sampling</a></li>
37-
</ul>
38-
</li>
12+
The official documentation is found [here](./docs/README.md)
3913

40-
<li>
41-
<p><a href="#util-zookeeper">util-zookeeper</a></p>
42-
<ul>
43-
<li><a href="#zookeeperinstance">ZooKeeperInstance</a></li>
44-
<li><a href="#zookeeperconf>ZooKeeperConf</a></li>
45-
</ul>
46-
</li>
47-
48-
<li><a href="#contributors">Copyright</a></li>
49-
<li><a href="#style-guidelines">Scala Style Guidelines</a></li>
50-
<li><a href="#git-flow">Git Flow</a></li>
51-
<li><a href="#contributing">Contributing</a></li>
52-
<li><a href="#copyright">Copyright</a></li>
53-
</ol>
54-
55-
56-
### Integrating the util library ###
57-
<a href="#table-of-contents">Back to top</a>
58-
59-
60-
The util library is designed to wrap common functionality in all our frameworks and offer it at the convenience of a dependency. Anything that will be useful
61-
long term to a great number of people belongs in these modules, to avoid duplication and help make our devs aware they can simply use what already exists.
62-
63-
The full list of available modules is:
64-
65-
```scala
66-
67-
libraryDependencies ++= Seq(
68-
"com.outworkers" %% "util-lift" % Versions.util,
69-
"com.outworkers" %% "util-domain" % Versions.util,
70-
"com.outworkers" %% "util-parsers" % Versions.util,
71-
"com.outworkers" %% "util-parsers-cats" % Versions.util,
72-
"com.outworkers" %% "util-validators" % Versions.util,
73-
"com.outworkers" %% "util-validators-cats" % Versions.util,
74-
"com.outworkers" %% "util-play" % Versions.util,
75-
"com.outworkers" %% "util-urls" % Versions.util,
76-
"com.outworkers" %% "util-testing" % Versions.util
77-
)
78-
```
79-
80-
81-
### util-testing ###
82-
<a href="#table-of-contents">Back to top</a>
83-
84-
The testing module features the ```AsyncAssertionsHelper```, which builds on top of ScalaTest to offer simple asynchronous assertions. We use this pattern
85-
heavily throughout the Outworkers ecosystem of projects, from internal to DSL modules and so forth. Asynchronous testing generally offers a considerable
86-
performance gain in code.
87-
88-
89-
### Async assertions ###
90-
<a href="#table-of-contents">Back to top</a>
91-
92-
93-
The async assertions module features a dual API, so you can call the same methods on both ```scala.concurrent.Future``` and ```com.twitter.util.Future```.
94-
The underlying mechanism will create an async ```Waiter```, that will wait for the future to complete within the given ```PatienceConfiguration```. The
95-
awaiting is done asynchronously and the assertions are invoked and evaluated once the future in question has returned a result.
96-
97-
```scala
98-
import com.outworkers.util.testing._
99-
100-
class MyTests extends FlatSuite with Matchers {
101-
102-
"The async computation" should "return 0 on completion" in {
103-
val f: Future[Int] = .. // Pretend this is a Future just like any other future.
104-
f.successful {
105-
res => {
106-
res shouldEqual 0
107-
}
108-
}
109-
}
110-
111-
"This async computation" should "fail by design" in {
112-
val f: Future[Unit] = ..
113-
114-
// You don't even need to do anything more than failure at this stage.
115-
// If the Future fails, the test will succeed, as this method is used when you "expect a failure".
116-
// You can however perform assertions on the error returned.
117-
f.failing {
118-
err => {
119-
}
120-
}
121-
}
122-
123-
"This async computation" should "fail with a specific error" in {
124-
val f: Future[Unit] = ..
125-
f.failingWith[NumberFormatException] {
126-
err => {
127-
}
128-
}
129-
}
130-
131-
}
132-
```
133-
134-
135-
You can directly customise the ```timeout``` of all ```Waiters``` using the ScalaTest specific time span implementations and interval configurations.
136-
137-
138-
```scala
139-
import org.scalatest.concurrent.PatienceConfiguration
140-
import org.scalatest.time.SpanSugar._
141-
142-
implicit val timeout: PatienceConfiguration.Timeout = timeout(20 seconds)
143-
144-
```
145-
146-
Summary:
147-
148-
- The dependency you need is ```"com.outworkers" %% "util-testing" % UtilVersion```.
149-
- You have to import ```com.outworkers.util.testing._```.
150-
- You have three main assertion methods, ```successful```, ```failing```, and ```failingWith```.
151-
- You can configure the timeout of waiters with ```implicit val timeout: PatienceConfiguration.Timeout = timeout(20 seconds)```.
152-
- The default timeout value is ```1 second```.
153-
154-
155-
### Data sampling ###
156-
<a href="#table-of-contents">Back to top</a>
157-
158-
This is a very common pattern we use in our testing and it's very easy to interchange this generation with something like ScalaCheck. The idea is very simple, you use type classes to define ways to sample a given type.
159-
After you define such a one-time sampling type class instance, you have access to several methods that will allow you to generate test data.
160-
161-
It's useful to define such typeclass instances inside package objects, as they will be "invisibly" imported in to the scope you need them to. This is often really neat, albeit potentially confusing for novice Scala users.
162-
163-
164-
```scala
165-
166-
import com.outworkers.util.testing._
167-
168-
@sample case class MyAwesomeClass(name: String, age: Int, email: String)
169-
```
170-
171-
You may notice this pattern is already available in better libraries such as ScalaMock and we are not trying to provide an alternative to ScalaMock or compete with it in any way. Our typeclass generator approach only becomes very useful where you really care about very specific properties of the data.
172-
For instance, you may want to get a user with a valid email address, or you may use the underlying factories to get a name that reassembles the name of a real person, and so on.
173-
174-
It's also useful when you want to define specific ways in which hierarchies of classes are composed together into a sample. If generation for the sake of generation is all you care about, then ScalaMock is probably more robust.
175-
176-
177-
### Automated sampling
178-
179-
<a href="#table-of-contents">Back to top</a>
180-
181-
One interesting thing that happens when using the `@sample` annotation is that using `gen` immediately after it will basically
182-
give you an instance of your `case class` with the fields appropiately pre-filled, and some of the basic scenarios are also name aware.
183-
184-
What this means is that we try to make the data feel "real" with respect to what it should be. Let's take the below example:
185-
186-
```scala
187-
@sample case class User(
188-
id: UUID,
189-
firstName: String,
190-
lastName: String,
191-
email: String
192-
)
193-
```
194-
This is interesting and common enough. What's more interesting is the output of `gen`.
195-
196-
```scala
197-
198-
val user = gen[User]
199-
200-
Console.println(user.trace())
201-
202-
/**
203-
User(
204-
id = 6be8914c-4274-40ee-83f5-334131246fd8
205-
firstName = Lindsey
206-
lastName = Craft
207-
email = rparker@hotma1l.us
208-
)
209-
*/
210-
211-
```
212-
213-
So as you can see, the fields have been appropriately pre-filled. The email is a valid email, and the first and last name look like first and last names. For
214-
anything that's in the default generation domain, including dates and country codes and much more, we have the ability to produce automated
215-
appropriate values.
216-
217-
During the macro expansion phase, we check the annotation targets and try to infer the "natural" value based on the field name and type. So
218-
if your field name is either "email" or "emailAddress" or anything similar enough, you will get an "email" back.
219-
220-
221-
### Generating data
222-
223-
There are multiple methods available, allowing you to generate more than just the type:
224-
225-
- ```gen[T]```, used to generate a single instance of T.
226-
- ```gen[X, Y]```, used to generate a tuple based on two samples.
227-
- ```genOpt[T]```, convenience method that will give you back a ```Some[T](..)```.
228-
- ```genList[T](limit)```, convenience method that will give you back a ```List[T]```. The numbers of items in the list is equal to the ```limit``` and has a default value of 5 if not specified.
229-
- ```genMap[T]()```, convenience method that will give you back a ```Map[String, T]```.
230-
231-
232-
There is also a default list of available generators for some default types, and to get to their value simply use the `value` method if the type is not a primitive. For things like ```EmailAddress```, the point of the extra class is obviously to distinguish the type during implicit resolution, but you don't need to use our abstraction at all, there will always be an easy way to get to the underlying generated primitives.
233-
234-
In the case of email addresses, you can use ```gen[EmailAddress].value```, which will correctly generate a valid ```EmailAddress``` but you can work directly with a ```String```.
235-
236-
- ```scala.Int```
237-
- ```scala.Double```
238-
- ```scala.Float```
239-
- ```scala.Long```
240-
- ```scala.String```
241-
- ```scala.math.BigDecimal```
242-
- ```scala.math.BigInt```
243-
- ```java.util.Date```
244-
- ```java.util.UUID```
245-
- ```org.joda.time.DateTime```
246-
- ```org.joda.time.LocalDate```
247-
- ```com.outworkers.util.domain.Definitions.EmailAddress(value)```
248-
- ```com.outworkers.util.domain.Definitions.FirstName(value)```
249-
- ```com.outworkers.util.domain.Definitions.LastName(value)```
250-
- ```com.outworkers.util.domain.Definitions.FullName(value)```
251-
- ```com.outworkers.util.domain.Definitions.CountryCode(value)```
252-
- ```com.outworkers.util.domain.Definitions.Country(value)```
253-
- ```com.outworkers.util.domain.Definitions.City(value)```
254-
- ```com.outworkers.util.domain.Definitions.ProgrammingLanguage(value)```
255-
- ```com.outworkers.util.domain.Definitions.LoremIpsum(value)```
256-
257-
258-
### util-parsers ###
259-
<a href="#table-of-contents">Back to top</a>
260-
261-
The parser module features an easy to use and integrate set of ScalaZ Applicative based parsers, with an ```Option``` based parser variant. It allows us to
262-
seamlessly deal with validation chains at REST API level or whenever validation is involved. Whether it's monadic composition of options or chaining of
263-
applicative functors to obtain a "correct" chain, the parser module is designed to offer an all-you-can-eat buffet of mini parsers that can be easily
264-
composed to suit any validation needs.
265-
266-
Each parser comes in three distinct flavours, a ```ValidationNel``` parser that parsers the end type from a ```String``` and returns the type itself,
267-
a parser that parses an end result from an ```Option[String]``` and parserOpt variant that returns an ```Option[T]``` instead of a ```ValidationNel[String,
268-
T]```, which allows for Monadic composition, where you need to "short-circuit" evaluation and validation, instead of computing the full chain by chaining
269-
applicatives.
270-
271-
### Option parsers ###
272-
<a href="#table-of-contents">Back to top</a>
273-
274-
The full list of optional parsers is:
275-
276-
| Type | Input type | Parser Output type |
277-
| --------------- |---------------------------| --------------------------------- |
278-
| Int | String\|Option[String] | ValidationNel[String, Int] |
279-
| Long | String\|Option[String] | ValidationNel[String, Long] |
280-
| Double | String\|Option[String] | ValidationNel[String, Double] |
281-
| Float | String\|Option[String] | ValidationNel[String, Float] |
282-
| UUID | String\|Option[String] | ValidationNel[String, UUID] |
283-
| Email | String\|Option[String] | ValidationNel[String, String] |
284-
| DateTime | String\|Option[String] | ValidationNel[String, org.joda.time.DateTime] |
285-
286-
Option parsers are designed for chains where you want to short-circuit and exit to result as soon a parser fails. This short-circuit behaviour is the default
287-
```flatMap``` behaviour of an ```Option```, as soon as an ```Option``` is ```None``` the chain breaks. Unlike applicatives,
288-
the evaluation sequence of options will be escaped and you cannot for instance return an error for every parser that couldn't validate. Instead,
289-
you will only get the first error in the sequence.
290-
291-
An example of how to use ```Option``` parsers might be:
292-
293-
```scala
294-
295-
import com.outworkers.util.parsers._
296-
297-
object Test {
298-
def optionalParsing(email: String, age: String): Unit = {
299-
for {
300-
validEmail <- parseOpt[EmailAddress](email)
301-
validAge <- parseOpt[Int](age)
302-
} yield s"This person can be reached at $validEmail and is $validAge years old"
303-
}
304-
}
305-
306-
```
307-
308-
309-
### Applicative parsers ###
310-
<a href="#table-of-contents">Back to top</a>
311-
312-
The full list of ScalaZ Validation based applicative parsers is:
313-
314-
| Type | Input type | Parser Output type |
315-
| --------------- |---------------------------| --------------------------------- |
316-
| Int | String\|Option[String] | ValidationNel[String, Int] |
317-
| Long | String\|Option[String] | ValidationNel[String, Long] |
318-
| Double | String\|Option[String] | ValidationNel[String, Double] |
319-
| Float | String\|Option[String] | ValidationNel[String, Float] |
320-
| UUID | String\|Option[String] | ValidationNel[String, UUID] |
321-
| Email | String\|Option[String] | ValidationNel[String, String] |
322-
| DateTime | String\|Option[String] | ValidationNel[String, org.joda.time.DateTime] |
323-
324-
To illustrate the basic usage of applicative parsers and how to chain them, have a look below.
325-
326-
```scala
327-
328-
import scalaz._
329-
import scalaz.Scalaz._
330-
import com.outworkers.util.parsers._
331-
332-
object Test {
333-
334-
def registerUser(str: String, age: String): Unit = {
335-
(parse[EmailAddress](str) |@| parse[Int](age)) {
336-
(validEmail, validAge) => {
337-
}
338-
}.fold {
339-
// ..
340-
}
341-
}
342-
343-
}
344-
```
34514

34615
### Contributors
34716
<a href="#table-of-contents">Back to top</a>

build.sbt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ lazy val baseProjectList: Seq[ProjectReference] = Seq(
179179
testing,
180180
testingTwitter,
181181
macros,
182-
tags
182+
tags,
183+
readme
183184
)
184185

185186
lazy val util = (project in file("."))
@@ -414,3 +415,26 @@ lazy val validators = (project in file("util-validators"))
414415
parsers,
415416
testing % Test
416417
)
418+
419+
lazy val readme = (project in file("readme"))
420+
.settings(sharedSettings: _*)
421+
.dependsOn(
422+
domain,
423+
lift,
424+
liftCats,
425+
parsers,
426+
parsersCats,
427+
validatorsCats,
428+
validators,
429+
samplers,
430+
testing,
431+
testingTwitter,
432+
macros,
433+
tags
434+
).settings(
435+
tutSourceDirectory := sourceDirectory.value / "main" / "tut",
436+
tutTargetDirectory := util.base / "docs",
437+
libraryDependencies ++= Seq(
438+
"org.scalatest" %% "scalatest" % Versions.scalatest % "tut"
439+
)
440+
).enablePlugins(TutPlugin)

0 commit comments

Comments
 (0)