Skip to content

Commit

Permalink
Merge pull request #69 from ba-st/update_docs
Browse files Browse the repository at this point in the history
Update docs and fix markdown lint issues
  • Loading branch information
gcotelli authored Jul 22, 2021
2 parents 0f356b1 + 4e3faf6 commit 33bedfe
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/markdown-lint.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Markdown Lint
on: [pull_request,workflow_dispatch]
on: [push,pull_request,workflow_dispatch]
jobs:
remark-lint:
name: runner / markdownlint
Expand Down
25 changes: 18 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Contributing

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.
There are 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.

## Reporting issues

Expand All @@ -9,17 +11,23 @@ You can report issues [here](https://github.com/ba-st/Buoy/issues/new)
## 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 repository using the Tonel format in the `source` folder.
- The `release-candidate` branch contains the latest changes and should always be in a releasable state.
- 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 repository using the Tonel format in the
`source` folder.
- The `release-candidate` branch contains the latest changes and should always
be in a releasable state.
- 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.
- Code contributions without test cases have a lower probability of being merged
into the main branch.

### Using Iceberg

1. Download a [Pharo Image and VM](https://get.pharo.org/64)
2. Clone the project or your fork using Iceberg
3. Open the Working Copy and using the contextual menu select `Metacello -> Install baseline...`
3. Open the Working Copy and using the contextual menu select
`Metacello -> Install baseline...`
4. Input `Development`
5. This will load the base code and the test cases
6. Create a new branch to host your code changes
Expand All @@ -30,4 +38,7 @@ You can report issues [here](https://github.com/ba-st/Buoy/issues/new)

## Contributing documentation

The project documentation is maintained in this repository in the `docs` folder and licensed under CC BY-SA 4.0. 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.
The project documentation is maintained in this repository in the `docs` folder
and licensed under CC BY-SA 4.0. 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.
19 changes: 13 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,37 @@ Quick links

### 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. Read the [online tutorial](docs/Assertions.md).
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. Read the [online tutorial](docs/Assertions.md).

### Collections

This library provides additional abstractions for Collections. See the [related documentation.](docs/Collections.md)

### Comparison

This library provides support to compare objects both for equality and identity. They are typically used to implement the `=` and `hash` methods. See the [related documentation.](docs/Comparison.md)
This library provides support to compare objects both for equality and identity.
They are typically used to implement the `=` and `hash` methods. See the
[related documentation.](docs/Comparison.md)

### Math

This library provides basic arithmetic abstractions like Percentages. See the [related documentation.](docs/Math.md)
This library provides basic arithmetic abstractions like Percentages. See the
[related documentation.](docs/Math.md)

### Bindings and Optionals

This library provides support to express optional values and required values, that can be unknown at the beginning of an execution. See the [related documentation.](docs/BindingsAndOptionals.md)
This library provides support to express optional values and required values,
that can be unknown at the beginning of an execution. See the [related documentation.](docs/BindingsAndOptionals.md)

### Exception Handling

Provides extensions to the [exception handling mechanics](docs/ExceptionHandling.md).

### Metaprogramming

This library provides some abstractions like [namespaces](docs/Namespaces.md) and [interfaces](docs/Interfaces.md).
This library provides some abstractions like [namespaces](docs/Namespaces.md)
and [interfaces](docs/Interfaces.md).

### SUnit

Expand All @@ -59,7 +65,8 @@ Provides [extensions to the SUnit framework](docs/SUnit.md).

## Installation

To load the project in a Pharo image, or declare it as a dependency of your own project follow this [instructions](docs/Installation.md).
To load the project in a Pharo image, or declare it as a dependency of your own
project follow this [instructions](docs/Installation.md).

## Contributing

Expand Down
54 changes: 39 additions & 15 deletions docs/Assertions.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# 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).
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's start with the most basic condition this kind of code must respect: a valid code consists of exactly two letters.
So let's 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:

Expand All @@ -18,13 +20,17 @@ AssertionChecker
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.
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.
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 requirements of the standard: a valid code consists only of letters. So let's rewrite our example:
Now in the previous example we missed some of the requirements of the standard:
a valid code consists only of letters. So let's rewrite our example:

```smalltalk
| code |
Expand All @@ -42,9 +48,15 @@ AssertionCheckerBuilder new
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 checked so you should get an error message combining both explanations, and if you handle the raised exception you can get all the failures by sending it the message `failures`.
Note that in this case we are 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 checked
so you should get an error message combining both explanations, and if you
handle the raised exception you can get all the failures by sending 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:
If you want the more usual behavior of stopping after the first failure you can
configure the builder to fail fast:

```smalltalk
| code |
Expand All @@ -63,11 +75,14 @@ AssertionCheckerBuilder new
buildAndCheck
```

If you `Do it` you will get only the first failure, the next conditions won't even be checked.
If you `Do it` you will get only the first failure, the next conditions won't
even be checked.

## 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. Now we will consider only the officially assigned codes as valid:
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. Now we will consider only the officially assigned codes as valid:

```smalltalk
| code officiallyAssignedCodes |
Expand All @@ -83,20 +98,27 @@ AssertionCheckerBuilder new
onSuccess: [ :sucessAsserter |
sucessAsserter
enforce: [ officiallyAssignedCodes includes: code ]
because: [ '<1s> is not an officially assigned code' expandMacrosWith: code ]
because: [ '<1s> is not an officially assigned code'
expandMacrosWith: code ]
]
];
buildAndCheck
```

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 condition is not met. In this case the argument is a literal String, so it makes no difference.
- 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
condition is not met. In this case the argument is a literal String, so 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:`.
Sometimes it's easier to explain a condition using negative logic, so
`enforce:because:` has an opposite partner: `refuse:because:`.

```smalltalk
| code unassignedCodes |
Expand All @@ -120,7 +142,8 @@ AssertionCheckerBuilder new

## 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 are two ways to configure it:
If not specified the library will raise `AssertionFailed` when some check fails.
If you want to raise a different kind of error there are two ways to configure it:

For single condition checks you can use `enforce:because:raising:` or `refuse:because:raising:`.

Expand Down Expand Up @@ -154,4 +177,5 @@ AssertionCheckerBuilder new
buildAndCheck
```

but keep in mind when using the builder that the error to raise must understand `signalAll:` in order to work.
but keep in mind when using the builder that the error to raise must understand
`signalAll:` in order to work.
34 changes: 25 additions & 9 deletions docs/BindingsAndOptionals.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## Bindings

A binding is useful for describing situations when there's a need for a required value that can be missing at the beginning. As this is a required value, the contract is to ask for it, and in case it is still missing we will raise an exception.
A binding is useful for describing situations when there's a need for a required
value that can be missing at the beginning. As this is a required value, the
contract is to ask for it, and in case it is still missing we will raise an exception.

```smalltalk
| definedBinding undefinedBinding |
Expand All @@ -14,9 +16,13 @@ undefinedBinding content "Raises and exception"

## Optionals

An optional is useful for describing situations when we have an object that can either be present or not. In some ways it's similar to the Maybe monad, but it does not pretend to be a monad.
An optional is useful for describing situations when we have an object that can
either be present or not. In some ways it's similar to the Maybe monad, but it
does not pretend to be a monad.

So let's say we have a user interface where we want to show the details of some file the user has to upload. At the beginning there is no file so we can start with an unused optional:
So let's say we have a user interface where we want to show the details of some
file the user has to upload. At the beginning there is no file so we can start
with an unused optional:

```smalltalk
fileOptional := Optional unused.
Expand All @@ -28,15 +34,17 @@ and we can have the following rendering code:
fileOptional withContentDo: [:file | self renderDetailsOf: file]
```

The first time we render our page we don't have a file, and so we won't render anything. Now we can let the user upload a file and change the optional:
The first time we render our page we don't have a file, and so we won't render
anything. Now we can let the user upload a file and change the optional:

```smalltalk
fileOptional := Optional containing: self uploadFile
```

and now the rendering code will take care of rendering the file details.

This is the simplest use, now suppose we want to take some action in case the file is not yet uploaded. We can change the rendering code to:
This is the simplest use, now suppose we want to take some action in case the
file is not yet uploaded. We can change the rendering code to:

```smalltalk
fileOptional
Expand All @@ -52,7 +60,8 @@ We can transform an optional:
fileOptional return: [:file | file asUrl ]
```

This will produce a new optional that will have an URL based on the file, or an unused one in case the file is missing.
This will produce a new optional that will have an URL based on the file, or an
unused one in case the file is missing.

We can easily combine two optionals:

Expand All @@ -63,17 +72,24 @@ fileOptional
'<1s>.<2s>' expandMacrosWith: fileName with: fileExtension ]
```

This will produce a new optional that will have the concatenation as its content, or an unused one in case some part is missing.
This will produce a new optional that will have the concatenation as its content,
or an unused one in case some part is missing.

If we have a list of optionals it's possible to combine them to get a new optional as a result. So suppose we have a list of possible numbers and we want to get the sum only if all are available. We can do that by sending the following message:
If we have a list of optionals it's possible to combine them to get a new
optional as a result. So suppose we have a list of possible numbers and we want
to get the sum only if all are available. We can do that by sending the
following message:

```smalltalk
Optional
withAll: numberOptionals
return: [:addends | addends sum ]
```

So we will get a new optional that contains the sum in case all the possible numbers are available, or an unused optional in case some number is not available. In that case we are performing the computation over a collection of the values. It can be expressed for each step, with an injection:
So we will get a new optional that contains the sum in case all the possible
numbers are available, or an unused optional in case some number is not
available. In that case we are performing the computation over a collection of
the values. It can be expressed for each step, with an injection:

```smalltalk
Optional
Expand Down
Loading

0 comments on commit 33bedfe

Please sign in to comment.