Skip to content

Commit b32ccac

Browse files
authored
Merge pull request #20 from kianmeng/misc-doc-changes
2 parents 0ece2e8 + bab374b commit b32ccac

File tree

5 files changed

+37
-326
lines changed

5 files changed

+37
-326
lines changed

LICENSE renamed to LICENSE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
MIT License
1+
# MIT License
22

3-
Copyright (c) 2018-2020 Jean-Philippe Cugnet and Contributors
3+
Copyright © 2018-2021 Jean-Philippe Cugnet and Contributors
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
# TypedStruct
22

33
[![Build Status](https://travis-ci.com/ejpcmac/typed_struct.svg?branch=develop)](https://travis-ci.com/ejpcmac/typed_struct)
4-
[![hex.pm version](http://img.shields.io/hexpm/v/typed_struct.svg?style=flat)](https://hex.pm/packages/typed_struct)
4+
[![hex.pm version](https://img.shields.io/hexpm/v/typed_struct.svg?style=flat)](https://hex.pm/packages/typed_struct)
5+
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg?style=flat)](https://hexdocs.pm/typed_struct/)
6+
[![Total Download](https://img.shields.io/hexpm/dt/typed_struct.svg?style=flat)](https://hex.pm/packages/typed_struct)
7+
[![License](https://img.shields.io/hexpm/l/typed_struct.svg?style=flat)](https://github.com/ejpcmac/typed_struct/blob/master/LICENSE.md)
8+
9+
<!-- MDOC !-->
510

611
TypedStruct is a library for defining structs with a type without writing
712
boilerplate code.
@@ -191,7 +196,7 @@ typedstruct do
191196
end
192197
```
193198

194-
You can also document submodules this way:
199+
You can also document submodules this way:
195200

196201
```elixir
197202
typedstruct module: MyStruct do
@@ -370,6 +375,7 @@ defmodule MyModule do
370375
end
371376
end
372377
```
378+
<!-- MDOC !-->
373379

374380
## Initial roadmap
375381

@@ -401,6 +407,6 @@ Before contributing to this project, please read the
401407

402408
## License
403409

404-
Copyright © 2018-2020 Jean-Philippe Cugnet and Contributors
410+
Copyright © 2018-2021 Jean-Philippe Cugnet and Contributors
405411

406-
This project is licensed under the [MIT license](LICENSE).
412+
This project is licensed under the [MIT license](./LICENSE.md).

lib/typed_struct.ex

Lines changed: 5 additions & 312 deletions
Original file line numberDiff line numberDiff line change
@@ -1,316 +1,9 @@
11
defmodule TypedStruct do
2-
@moduledoc """
3-
TypedStruct is a library for defining structs with a type without writing
4-
boilerplate code.
5-
6-
## Rationale
7-
8-
To define a struct in Elixir, you probably want to define three things:
9-
10-
* the struct itself, with default values,
11-
* the list of enforced keys,
12-
* its associated type.
13-
14-
It ends up in something like this:
15-
16-
defmodule Person do
17-
@moduledoc \"\"\"
18-
A struct representing a person.
19-
\"\"\"
20-
21-
@enforce_keys [:name]
22-
defstruct name: nil,
23-
age: nil,
24-
happy?: true,
25-
phone: nil
26-
27-
@typedoc "A person"
28-
@type t() :: %__MODULE__{
29-
name: String.t(),
30-
age: non_neg_integer() | nil,
31-
happy?: boolean(),
32-
phone: String.t() | nil
33-
}
34-
end
35-
36-
In the example above you can notice several points:
37-
38-
* the keys are present in both the `defstruct` and type definition,
39-
* enforced keys must also be written in `@enforce_keys`,
40-
* if a key has no default value and is not enforced, its type should be
41-
nullable.
42-
43-
If you want to add a field in the struct, you must therefore:
44-
45-
* add the key with its default value in the `defstruct` list,
46-
* add the key with its type in the type definition.
47-
48-
If the field is not optional, you should even add it to `@enforce_keys`. This
49-
is way too much work for lazy people like me, and moreover it can be
50-
error-prone.
51-
52-
It would be way better if we could write something like this:
53-
54-
defmodule Person do
55-
@moduledoc \"\"\"
56-
A struct representing a person.
57-
\"\"\"
58-
59-
use TypedStruct
60-
61-
typedstruct do
62-
@typedoc "A person"
63-
64-
field :name, String.t(), enforce: true
65-
field :age, non_neg_integer()
66-
field :happy?, boolean(), default: true
67-
field :phone, String.t()
68-
end
69-
end
70-
71-
Thanks to TypedStruct, this is now possible :)
72-
73-
## Usage
74-
75-
### Setup
76-
77-
To use TypedStruct in your project, add this to your Mix dependencies:
78-
79-
{:typed_struct, "~> #{Mix.Project.config()[:version]}"}
80-
81-
If you do not plan to compile modules using TypedStruct at runtime, you can
82-
add `runtime: false` to the dependency tuple as TypedStruct is only used at
83-
build time.
84-
85-
If you want to avoid `mix format` putting parentheses on field definitions,
86-
you can add to your `.formatter.exs`:
87-
88-
[
89-
...,
90-
import_deps: [:typed_struct]
91-
]
92-
93-
### General usage
94-
95-
To define a typed struct, use `TypedStruct`, then define your struct within a
96-
`typedstruct` block:
97-
98-
defmodule MyStruct do
99-
# Use TypedStruct to import the typedstruct macro.
100-
use TypedStruct
101-
102-
# Define your struct.
103-
typedstruct do
104-
# Define each field with the field macro.
105-
field :a_string, String.t()
106-
107-
# You can set a default value.
108-
field :string_with_default, String.t(), default: "default"
109-
110-
# You can enforce a field.
111-
field :enforced_field, integer(), enforce: true
112-
end
113-
end
114-
115-
Each field is defined through the `field/2` macro.
116-
117-
### Options
118-
119-
If you want to enforce all the keys by default, you can do:
120-
121-
defmodule MyStruct do
122-
use TypedStruct
123-
124-
# Enforce keys by default.
125-
typedstruct enforce: true do
126-
# This key is enforced.
127-
field :enforced_by_default, term()
128-
129-
# You can override the default behaviour.
130-
field :not_enforced, term(), enforce: false
131-
132-
# A key with a default value is not enforced.
133-
field :not_enforced_either, integer(), default: 1
134-
end
135-
end
136-
137-
You can also generate an opaque type for the struct:
138-
139-
defmodule MyOpaqueStruct do
140-
use TypedStruct
141-
142-
# Generate an opaque type for the struct.
143-
typedstruct opaque: true do
144-
field :name, String.t()
145-
end
146-
end
147-
148-
If you often define submodules containing only a struct, you can avoid
149-
boilerplate code:
150-
151-
defmodule MyModule do
152-
use TypedStruct
153-
154-
# You now have %MyModule.Struct{}.
155-
typedstruct module: Struct do
156-
field :field, term()
157-
end
158-
end
159-
160-
### Documentation
161-
162-
To add a `@typedoc` to the struct type, just add the attribute in the
163-
`typedstruct` block:
164-
165-
typedstruct do
166-
@typedoc "A typed struct"
167-
168-
field :a_string, String.t()
169-
field :an_int, integer()
170-
end
171-
172-
You can also document submodules this way:
173-
174-
typedstruct module: MyStruct do
175-
@moduledoc "A submodule with a typed struct."
176-
@typedoc "A typed struct in a submodule"
177-
178-
field :a_string, String.t()
179-
field :an_int, integer()
180-
end
181-
182-
### Plugins
183-
184-
It is possible to extend the scope of TypedStruct by using its plugin
185-
interface, as described in `TypedStruct.Plugin`. For instance, to
186-
automatically generate lenses with the [Lens](https://github.com/obrok/lens)
187-
library, you can use
188-
[`TypedStructLens`](https://github.com/ejpcmac/typed_struct_lens) and do:
189-
190-
defmodule MyStruct do
191-
use TypedStruct
192-
193-
typedstruct do
194-
plugin TypedStructLens
195-
196-
field :a_field, String.t()
197-
field :other_field, atom()
198-
end
199-
200-
@spec change(t()) :: t()
201-
def change(data) do
202-
# a_field/0 is generated by TypedStructLens.
203-
lens = a_field()
204-
put_in(data, [lens], "Changed")
205-
end
206-
end
207-
208-
## What do I get?
209-
210-
When defining an empty `typedstruct` block:
211-
212-
defmodule Example do
213-
use TypedStruct
214-
215-
typedstruct do
216-
end
217-
end
218-
219-
you get an empty struct with its module type `t()`:
220-
221-
defmodule Example do
222-
@enforce_keys []
223-
defstruct []
224-
225-
@type t() :: %__MODULE__{}
226-
end
227-
228-
Each `field` call adds information to the struct, `@enforce_keys` and the type
229-
`t()`.
230-
231-
A field with no options adds the name to the `defstruct` list, with `nil` as
232-
default. The type itself is made nullable:
233-
234-
defmodule Example do
235-
use TypedStruct
236-
237-
typedstruct do
238-
field :name, String.t()
239-
end
240-
end
241-
242-
becomes:
243-
244-
defmodule Example do
245-
@enforce_keys []
246-
defstruct name: nil
247-
248-
@type t() :: %__MODULE__{
249-
name: String.t() | nil
250-
}
251-
end
252-
253-
The `default` option adds the default value to the `defstruct`:
254-
255-
field :name, String.t(), default: "John Smith"
256-
257-
# Becomes
258-
defstruct name: "John Smith"
259-
260-
When set to `true`, the `enforce` option enforces the key by adding it to the
261-
`@enforce_keys` attribute.
262-
263-
field :name, String.t(), enforce: true
264-
265-
# Becomes
266-
@enforce_keys [:name]
267-
defstruct name: nil
268-
269-
In both cases, the type has no reason to be nullable anymore by default. In
270-
one case the field is filled with its default value and not `nil`, and in the
271-
other case it is enforced. Both options would generate the following type:
272-
273-
@type t() :: %__MODULE__{
274-
name: String.t() # Not nullable
275-
}
276-
277-
Passing `opaque: true` replaces `@type` with `@opaque` in the struct type
278-
specification:
279-
280-
typedstruct opaque: true do
281-
field :name, String.t()
282-
end
283-
284-
generates the following type:
285-
286-
@opaque t() :: %__MODULE__{
287-
name: String.t()
288-
}
289-
290-
When passing `module: ModuleName`, the whole `typedstruct` block is wrapped in
291-
a module definition. This way, the following definition:
292-
293-
defmodule MyModule do
294-
use TypedStruct
295-
296-
typedstruct module: Struct do
297-
field :field, term()
298-
end
299-
end
300-
301-
becomes:
302-
303-
defmodule MyModule do
304-
defmodule Struct do
305-
@enforce_keys []
306-
defstruct field: nil
307-
308-
@type t() :: %__MODULE__{
309-
field: term() | nil
310-
}
311-
end
312-
end
313-
"""
2+
@external_resource "README.md"
3+
@moduledoc "README.md"
4+
|> File.read!()
5+
|> String.split("<!-- MDOC !-->")
6+
|> Enum.fetch!(1)
3147

3158
@doc false
3169
defmacro __using__(_) do

0 commit comments

Comments
 (0)