Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions city-office/.exercism/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"authors": [
"angelikatyborska"
],
"contributors": [
"neenjaw",
"michallepicki"
],
"files": {
"solution": [
"lib/form.ex"
],
"test": [
"test/form_test.exs"
],
"exemplar": [
".meta/exemplar.ex"
]
},
"language_versions": ">=1.10",
"blurb": "Learn about writing documentation and typespecs by getting your code ready for the arrival of a new colleague at the city office."
}
1 change: 1 addition & 0 deletions city-office/.exercism/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"track":"elixir","exercise":"city-office","id":"6150971843e64550925d8c7595d13d51","url":"https://exercism.org/tracks/elixir/exercises/city-office","handle":"sangeethailango","is_requester":true,"auto_approve":false}
4 changes: 4 additions & 0 deletions city-office/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
24 changes: 24 additions & 0 deletions city-office/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
match_binary-*.tar

74 changes: 74 additions & 0 deletions city-office/HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Help

## Running the tests

From the terminal, change to the base directory of the exercise then execute the tests with:

```bash
$ mix test
```

This will execute the test file found in the `test` subfolder -- a file ending in `_test.exs`

Documentation:

* [`mix test` - Elixir's test execution tool](https://hexdocs.pm/mix/Mix.Tasks.Test.html)
* [`ExUnit` - Elixir's unit test library](https://hexdocs.pm/ex_unit/ExUnit.html)

## Pending tests

In test suites of practice exercises, all but the first test have been tagged to be skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

```elixir
# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
```

If you wish to run all tests at once, you can include all skipped test by using the `--include` flag on the `mix test` command:

```bash
$ mix test --include pending
```

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the file `test/test_helper.exs`.

```elixir
# ExUnit.configure(exclude: :pending, trace: true)
```

## Useful `mix test` options

* `test/<FILE>.exs:LINENUM` - runs only a single test, the test from `<FILE>.exs` whose definition is on line `LINENUM`
* `--failed` - runs only tests that failed the last time they ran
* `--max-failures` - the suite stops evaluating tests when this number of test failures
is reached
* `--seed 0` - disables randomization so the tests in a single file will always be ran
in the same order they were defined in

## Submitting your solution

You can submit your solution using the `exercism submit lib/form.ex` command.
This command will upload your solution to the Exercism website and print the solution page's URL.

It's possible to submit an incomplete solution which allows you to:

- See how others have completed the exercise
- Request help from a mentor

## Need to get help?

If you'd like help solving the exercise, check the following pages:

- The [Elixir track's documentation](https://exercism.org/docs/tracks/elixir)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)

Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.

If you're stuck on something, it may help to look at some of the [available resources](https://exercism.org/docs/tracks/elixir/resources) out there where answers might be found.
63 changes: 63 additions & 0 deletions city-office/HINTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Hints

## General

- Read the official documentation for [typespecs][typespecs].
- Read the official documentation about [writing documentation][writing-documentation].
- Read about using module attributes as annotations in the [official Getting Started guide][getting-started-module-attributes].
- Read about using typespecs in the [official Getting Started guide][getting-started-typespecs].

## 1. Document the purpose of the form tools

- The module attribute `@moduledoc` can be used to write documentation for a module.

## 2. Document filling out fields with blank values

- The module attribute `@doc` can be used to write documentation for a function.
- The module attribute `@spec` can be used to write a typespec for a function.
- Place the `@doc` and `@spec` attributes right before the first function clause of the function that those attributes describe.
- Refer to the [typespecs documentation][typespecs-types] for a list of all available types.
- The correct type for strings is [defined in the `String` module][string-t].

## 3. Document splitting values into lists of uppercase letters

- The module attribute `@doc` can be used to write documentation for a function.
- The module attribute `@spec` can be used to write a typespec for a function.
- Place the `@doc` and `@spec` attributes right before the first function clause of the function that those attributes describe.
- Refer to the [typespecs documentation][typespecs-types] for a list of all available types.
- The correct type for strings is [defined in the `String` module][string-t].
- A list is a parametrized type.

## 4. Document checking if a value fits a field with a max length

- The module attribute `@doc` can be used to write documentation for a function.
- The module attribute `@spec` can be used to write a typespec for a function.
- Place the `@doc` and `@spec` attributes right before the first function clause of the function that those attributes describe.
- Refer to the [typespecs documentation][typespecs-types] for a list of all available types.
- The correct type for strings is [defined in the `String` module][string-t].
- Literal values can be used in a typespec.
- The pipe `|` can be used to represent a union of types.

## 5. Document different address formats

- The module attribute `@type` can be use to define a custom public type.
- Types can be compound, e.g. when specifying a type that's a map, you can also specify the types of the values under the specific keys.
- [The type operator `::`][type-operator] can also be used to prepend a variable name to a type.
- Custom types can be used to define other custom types.

## 6. Document formatting the address

- The module attribute `@doc` can be used to write documentation for a function.
- The module attribute `@spec` can be used to write a typespec for a function.
- Place the `@doc` and `@spec` attributes right before the first function clause of the function that those attributes describe.
- Refer to the [typespecs documentation][typespecs-types] for a list of all available types.
- The correct type for strings is [defined in the `String` module][string-t].
- Custom types can be used in a typespec.

[writing-documentation]: https://hexdocs.pm/elixir/writing-documentation.html
[typespecs]: https://hexdocs.pm/elixir/typespecs.html
[typespecs-types]: https://hexdocs.pm/elixir/typespecs.html#types-and-their-syntax
[getting-started-module-attributes]: https://elixir-lang.org/getting-started/module-attributes.html#as-annotations
[getting-started-typespecs]: https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#types-and-specs
[string-t]: https://hexdocs.pm/elixir/String.html#t:t/0
[type-operator]: https://hexdocs.pm/elixir/Kernel.SpecialForms.html#::/2
173 changes: 173 additions & 0 deletions city-office/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# City Office

Welcome to City Office on Exercism's Elixir Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)

## Introduction

## Docs

Documentation in Elixir is a first-class citizen.

There are two module attributes commonly used to document your code - `@moduledoc` for documenting a module and `@doc` for documenting a function that follows the attribute. The `@moduledoc` attribute usually appears on the first line of the module, and the `@doc` attribute usually appears right before a function definition, or the function's typespec if it has one. The documentation is commonly written in a multiline string using the heredoc syntax.

Elixir documentation is written in [**Markdown**][markdown].

```elixir
defmodule String do
@moduledoc """
Strings in Elixir are UTF-8 encoded binaries.
"""

@doc """
Converts all characters in the given string to uppercase according to `mode`.

## Examples

iex> String.upcase("abcd")
"ABCD"

iex> String.upcase("olá")
"OLÁ"
"""
def upcase(string, mode \\ :default)
end
```

## Typespecs

Elixir is a dynamically typed language, which means it doesn't provide compile-time type checks. Still, type specifications can be used as a form of documentation.

A type specification can be added to a function using the `@spec` module attribute right before the function definition. `@spec` is followed by the function name and a list of all of its arguments' types, in parentheses, separated by commas. The type of the return value is separated from the function's arguments with a double colon `::`.

```elixir
@spec longer_than?(String.t(), non_neg_integer()) :: boolean()
def longer_than?(string, length), do: String.length(string) > length
```

### Types

Most commonly used types include:

- booleans: `boolean()`
- strings: `String.t()`
- numbers: `integer()`, `non_neg_integer()`, `pos_integer()`, `float()`
- lists: `list()`
- a value of any type: `any()`

Some types can also be parameterized, for example `list(integer)` is a list of integers.

Literal values can also be used as types.

A union of types can be written using the pipe `|`. For example, `integer() | :error` means either an integer or the atom literal `:error`.

A full list of all types can be found in the ["Typespecs" section in the official documentation][types].

### Naming arguments

Arguments in the typespec could also be named which is useful for distinguishing multiple arguments of the same type. The argument name, followed by a double colon, goes before the argument's type.

```elixir
@spec to_hex({hue :: integer, saturation :: integer, lightness :: integer}) :: String.t()
```

### Custom types

Typespecs aren't limited to just the built-in types. Custom types can be defined using the `@type` module attribute. A custom type definition starts with the type's name, followed by a double colon and then the type itself.

```elixir
@type color :: {hue :: integer, saturation :: integer, lightness :: integer}

@spec to_hex(color()) :: String.t()
```

A custom type can be used from the same module where it's defined, or from another module.

[markdown]: https://docs.github.com/en/github/writing-on-github/basic-writing-and-formatting-syntax
[types]: https://hexdocs.pm/elixir/typespecs.html#types-and-their-syntax

## Instructions

You have been working in the city office for a while, and you have developed a set of tools that speed up your day-to-day work, for example with filling out forms.

Now, a new colleague is joining you, and you realized your tools might not be self-explanatory. There are a lot of weird conventions in your office, like always filling out forms with uppercase letters and avoiding leaving fields empty.

You decide to write some documentation so that it's easier for your new colleague to hop right in and start using your tools.

## 1. Document the purpose of the form tools

Add documentation to the `Form` module that describes its purpose. It should read:

```
A collection of loosely related functions helpful for filling out various forms at the city office.
```

## 2. Document filling out fields with blank values

Add documentation and a typespec to the `Form.blanks/1` function. The documentation should read:

```
Generates a string of a given length.

This string can be used to fill out a form field that is supposed to have no value.
Such fields cannot be left empty because a malicious third party could fill them out with false data.
```

The typespec should explain that the function accepts a single argument, a non-negative integer, and returns a string.

## 3. Document splitting values into lists of uppercase letters

Add documentation and a typespec to the `Form.letters/1` function. The documentation should read:

```
Splits the string into a list of uppercase letters.

This is needed for form fields that don't offer a single input for the whole string,
but instead require splitting the string into a predefined number of single-letter inputs.
```

The typespec should explain that the function accepts a single argument, a string, and returns a list of strings.

## 4. Document checking if a value fits a field with a max length

Add documentation and a typespec to the `Form.check_length/2` function. The documentation should read:

```
Checks if the value has no more than the maximum allowed number of letters.

This is needed to check that the values of fields do not exceed the maximum allowed length.
It also tells you by how much the value exceeds the maximum.
```

The typespec should explain that the function accepts two arguments, a string and a non-negative integer, and returns one of two possible values. It returns either the `:ok` atom or a 2-tuple with the first element being the `:error` atom, and the second a positive integer.

## 5. Document different address formats

For some unknown to you reason, the city office's internal system uses two different ways of representing addresses - either as a map or as a tuple.

Document this fact by defining three custom public types:
- `address_map` - a map with the keys `:street`, `:postal_code`, and `:city`. Each key holds a value of type string.
- `address_tuple` - a tuple with three values - `street`, `postal_code`, and `city`. Each value is of type string. Differentiate the values by giving them names in the typespec.
- `address` - can be either an `address_map` or an `address_tuple`.

## 6. Document formatting the address

Add documentation and a typespec to the `Form.format_address/1` function. The documentation should read:

```
Formats the address as an uppercase multiline string.
```

The typespec should explain that the function accepts one argument, an address, and returns a string.

## Source

### Created by

- @angelikatyborska

### Contributed to by

- @neenjaw
- @michallepicki
Loading