Skip to content

Commit

Permalink
Merge pull request #5 from ba-st/docs
Browse files Browse the repository at this point in the history
Documentation improvement
  • Loading branch information
gcotelli committed Feb 4, 2017
2 parents 99b6a4b + 37c667f commit 2d98c84
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 2 deletions.
33 changes: 33 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# How to Contribute

There's several ways to contribute to the project: reporting bugs, sending feedback, proposing ideas for new features, fixing or adding documentation, promoting the project, or even contributing code changes.

## Reporting issues

Use the issue tracker in this GitHub repository.

## Contributing Code

- This project is MIT licensed, so any code contribution must be under the same license.
- This project uses [semantic versioning](http://semver.org/), so keep it in mind when you make backwards-incompatible changes. If some backwards incompatible change is made the major version MUST be increased.
- The source code is hosted in this GitHub repository using the filetree format in the `source` folder. The master branch contains the latest changes, feel free to send pull requests or fork the project.
- Code contributions without test cases have a lower probability of being merged into the main branch.


- Clone this repository or a fork of it
- Load the corresponding development version evaluating in a Playground:
```smalltalk
Metacello new
baseline: 'Buoy';
repository: 'filetree://REPO_LOCATION/source';
load: 'Development'.
```
where `REPO_LOCATION` is the location of the cloned repo in the local file system.
- Do the changes and save it from Pharo (don't forget to add some test cases)
- Create a branch, commit using the usual Git tooling and open a Pull Request

## Contributing documentation

The project documentation is mantained in this repository in the `docs` folder. To contribute some documentation or improve the existing, feel free to create a branch or fork this repository, make your changes and send a pull request.

Remember the docs are licensed under a CC Attribution-ShareAlike license.
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
# Buoy

![logo](https://maxcdn.icons8.com/Color/PNG/48/Transport/buoy-48.png)

[![Build Status](https://travis-ci.org/ba-st/Buoy.svg?branch=master)](https://travis-ci.org/ba-st/Buoy)
[![Coverage Status](https://coveralls.io/repos/github/ba-st/Buoy/badge.svg?branch=master)](https://coveralls.io/github/ba-st/Buoy?branch=master)

This project aims to complement Pharo (www.pharo.org) adding useful extensions.
This project aims to complement [Pharo](www.pharo.org) adding useful extensions.

## License
The project source code is [MIT](LICENSE) licensed. Any contribution submitted to the code repository is considered to be under the same license.

The documentation is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).

## Assertions

This library is aimed at providing a simpler way to enforce and check assertions. The main focus point is to use it in the business model.

### Get started!

- Download a [Pharo Image and VM](http://get.pharo.org)
- Open a Playground and evaluate:

```smalltalk
Metacello new
baseline: 'Buoy';
repository: 'github://ba-st/Buoy:master/source';
load
```
- Read the [online tutorial](docs/Assertions.md)

## Contributing

If you want to help check the [contribution guidelines.](CONTRIBUTING.md)

---
[Icon pack by Icons8](https://icons8.com)
154 changes: 154 additions & 0 deletions docs/Assertions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Assertions Tutorial

For this tutorial we will use a simple model: ISO 3166-1 Alpha-2 codes. This codes are two-letter country codes defined in the corresponding [ISO standard](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).

## Single Conditions

So let start with the most basic condition this kind of code must respect: a valid code consists of exactly two letters.

Open a playground and `Do it` this:

```smalltalk
| code |
code := 'AR'.
AssertionChecker
enforce: [ code size = 2 ]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters'
```

Now change `code` to something failing the condition like `'ARG'` and `Do it` again, you should get a Debugger with an `AssertionFailed` exception raised.

So, to enforce a single condition we can send the message `enforce:because:` to `AssertionChecker` and if the condition is not met an `AssertionFailed` exception is raised including the provided explanation.

## Multiple Conditions

Now in the previous example we missed some of the requisites of the standard: a valid code must consists only of letters. So let's refrain our example:

```smalltalk
| code |
code := 'AR'.
AssertionCheckerBuilder new
checking: [ :asserter |
asserter
enforce: [ code size = 2 ]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters';
enforce: [ code allSatisfy: #isLetter ]
because: 'ISO 3166-1 Alpha-2 codes must only contain letters'
];
buildAndCheck
```
Note that in this case we're creating an `AssertionCheckerBuilder` and configuring all the conditions to enforce. Let's try now replacing `code` with `'AR3'` and `Do it` again. By default all the conditions to enforce are tried so you should get an error message combining both explanations, and if you handle the raised exception you can get all the failures sending to it the message `failures`.

If you want the more usual behavior of stopping after the first failure you can configure the builder to fail fast:

```smalltalk
| code |
code := 'AR3'.
AssertionCheckerBuilder new
failFast;
checking: [ :asserter |
asserter
enforce: [ code size = 2 ]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters';
enforce: [ code allSatisfy: #isLetter ]
because: 'ISO 3166-1 Alpha-2 codes must only contain letters'
];
buildAndCheck
```

If you `Do it` you will get only the first failure and the next conditions aren't even tried.

## Conditional Checking

Sometimes you want to check a condition but only after other conditions are met. So let's make our example more complex: not every two letters combination is a valid code, even for defined codes are several categories. Now we will consider only the officially assigned codes as valid:

```smalltalk
| code officiallyAssignedCodes |
code := 'AA'.
officiallyAssignedCodes := #('AR' 'BR' 'US').
AssertionCheckerBuilder new
checking: [ :asserter |
asserter
enforce: [ code size = 2 and: [ code allSatisfy: #isLetter ]]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters'
onSuccess: [ :sucessAsserter |
sucessAsserter
enforce: [ officiallyAssignedCodes includes: code ]
because: [ '<1s> is not an officially assigned code' expandMacrosWith: code ]
]
];
buildAndCheck
```

Now here we are introducing two new features:
- First `enforce:because:onSuccess:`, the main idea is that the conditions enforced in the success block will be evaluated only if the outer condition is satisfied. So we can make assumptions about what `code` looks like at this point.
- Second, using a block as the `because:` argument. This avoids creating unnecessary objects because the explanation will only be evaluated if the conditions is not met. In the case the argument is a literal String it makes no difference.

## Refusing

Sometimes it's easier to explain a condition using negative logic, so `enforce:because:` has an opposite partner: `refuse:because:`.

```smalltalk
| code unassignedCodes |
code := 'AR'.
unassignedCodes := #('LO' 'LP' 'OU').
AssertionCheckerBuilder new
checking: [ :asserter |
asserter
enforce: [ code size = 2 and: [ code allSatisfy: #isLetter ]]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters'
onSuccess: [ :sucessAsserter |
sucessAsserter
refuse: [ unassignedCodes includes: code ]
because: [ '<1s> is an unassigned code' expandMacrosWith: code ]
]
];
buildAndCheck
```

## Configuring the error to raise

If not specified the library will raise `AssertionFailed` when some check fails. If you want to raise a different kind of error there's two ways of configuring it:
- For single condition checks you can use `enforce:because:raising:` or `refuse:because:raising:`.

```smalltalk
| code |
code := 'AR'.
AssertionChecker
enforce: [ code size = 2 ]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters'
raising: Error
```

- When using the builder you can configure it:

```smalltalk
| code |
code := 'AR'.
AssertionCheckerBuilder new
raising: InstanceCreationFailed;
checking: [ :asserter |
asserter
enforce: [ code size = 2 ]
because: 'ISO 3166-1 Alpha-2 codes must have exactly two letters';
enforce: [ code allSatisfy: #isLetter ]
because: 'ISO 3166-1 Alpha-2 codes must only contain letters'
];
buildAndCheck
```

but keep in mind when using the builder that the error to raise must understand `signalAll:` to work.

0 comments on commit 2d98c84

Please sign in to comment.