@@ -127,21 +127,28 @@ defmodule Record do
127
127
## Types
128
128
129
129
Every record defines a type named `t` that can be accessed in typespecs.
130
- For example, assuming the `Config` record defined above, it could be used
131
- in typespecs as follow:
130
+ Those types can be passed at the moment the record is defined:
132
131
133
- @spec handle_config(Config.t) :: boolean()
132
+ defrecord User,
133
+ name: "" :: string,
134
+ age: 0 :: integer
134
135
135
- Inside the record definition, a developer can define his own types too:
136
+ All the fields without a specified type are assumed to have type `term`.
137
+
138
+ Assuming the `User` record defined above, it could be used in typespecs
139
+ as follow:
140
+
141
+ @spec handle_user(User.t) :: boolean()
142
+
143
+ If the developer wants to define their own types to be used with the
144
+ record, Elixir allows a more lengthy definition with the help of the
145
+ `record_type` macro:
136
146
137
147
defrecord Config, counter: 0, failures: [] do
138
148
@type kind :: term
139
149
record_type counter: integer, failures: [kind]
140
150
end
141
151
142
- When defining a type, all the fields not mentioned in the type are
143
- assumed to have type `term`.
144
-
145
152
## Importing records
146
153
147
154
It is also possible to import a public record (a record, defined using
@@ -173,29 +180,50 @@ defmodule Record do
173
180
"""
174
181
def defrecord(name, values, opts) do
175
182
block = Keyword.get(opts, :do, nil)
183
+ { fields, types } = record_split(values)
176
184
177
185
quote do
178
- unquoted_values = unquote(values )
186
+ unquoted_fields = unquote(fields )
179
187
180
188
defmodule unquote(name) do
181
189
@moduledoc false
182
190
import Elixir.Record.DSL
183
191
184
192
@record_fields []
185
- @record_types []
193
+ @record_types unquote(types)
186
194
187
- # Reassign values to inner scope to
195
+ # Reassign fields to inner scope to
188
196
# avoid conflicts in nested records
189
- values = unquoted_values
197
+ fields = unquoted_fields
190
198
191
- Elixir.Record.deffunctions(values , __ENV__)
199
+ Elixir.Record.deffunctions(fields , __ENV__)
192
200
value = unquote(block)
193
- Elixir.Record.deftypes(values , @record_types, __ENV__)
201
+ Elixir.Record.deftypes(fields , @record_types, __ENV__)
194
202
value
195
203
end
196
204
end
197
205
end
198
206
207
+ defp record_split(fields) when is_list(fields) do
208
+ record_split(fields, [], [])
209
+ end
210
+
211
+ defp record_split(other) do
212
+ { other, [] }
213
+ end
214
+
215
+ defp record_split([{ field, { :::, _, [default, type] }}|t], defaults, types) do
216
+ record_split t, [{ field, default }|defaults], [{ field, Macro.escape(type) }|types]
217
+ end
218
+
219
+ defp record_split([other|t], defaults, types) do
220
+ record_split t, [other|defaults], types
221
+ end
222
+
223
+ defp record_split([], defaults, types) do
224
+ { Enum.reverse(defaults), types }
225
+ end
226
+
199
227
@doc " ""
200
228
Import public record definition as a set of private macros ( as defined by defrecordp / 2 )
201
229
@@ -224,12 +252,27 @@ defmodule Record do
224
252
in `values`. This is invoked directly by `Kernel.defrecordp`,
225
253
so check it for more information and documentation.
226
254
"""
227
- def defrecordp( name, fields) do
255
+ def defrecordp( name, fields) when is_atom ( name ) and is_list ( fields ) do
256
+ { fields , types } = recordp_split ( fields , [ ] , [ ] )
257
+
228
258
quote do
229
259
Record . defmacros ( unquote ( name ) , unquote ( fields ) , __ENV__ )
260
+ @ opaque unquote ( name ) ( ) :: { unquote ( name ) , unquote_splicing ( types ) }
230
261
end
231
262
end
232
263
264
+ defp recordp_split( [ { field , { ::: , _ , [ default , type ] } } | t ] , defaults, types) do
265
+ recordp_split t, [ { field, default } | defaults ] , [ type| types ]
266
+ end
267
+
268
+ defp recordp_split( [ other | t ] , defaults, types) do
269
+ recordp_split t, [ other| defaults ] , [ quote( do: term) | types ]
270
+ end
271
+
272
+ defp recordp_split( [ ] , defaults , types ) do
273
+ { Enum . reverse ( defaults ) , Enum . reverse ( types ) }
274
+ end
275
+
233
276
@ doc """
234
277
Defines record functions skipping the module definition.
235
278
This is called directly by `defrecord`. It expects the record
0 commit comments