Skip to content

Commit 8d2034f

Browse files
committed
Implement Record.import macro that allows using private macros on public records
1 parent 26599e7 commit 8d2034f

File tree

4 files changed

+46
-8
lines changed

4 files changed

+46
-8
lines changed

lib/elixir/lib/record.ex

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,28 @@ defmodule Record do
4444
end
4545
end
4646

47+
@doc """
48+
Import public record definition as a set of private macros (as defined by defrecordp/2)
49+
50+
## Usage
51+
52+
Record.import Record.Module, as: macro_name
53+
54+
## Example
55+
56+
defmodule Test do
57+
Record.import File.Stat, as: :file_stat
58+
59+
def size(file_stat(size: size)), do: size
60+
end
61+
62+
"""
63+
defmacro import(module, as: name) do
64+
quote do
65+
Record.defmacros(unquote(name), unquote(module).__record__(:fields), __ENV__, unquote(module))
66+
end
67+
end
68+
4769
@doc """
4870
Main entry point for private records definition. It defines
4971
a set of macros with the given `name` and the fields specified
@@ -128,31 +150,32 @@ defmodule Record do
128150
end
129151
130152
"""
131-
def defmacros(name, values, env) do
153+
def defmacros(name, values, env, tag // nil) do
132154
escaped = lc value inlist values do
133155
{ key, value } = convert_value(value)
134156
{ key, Macro.escape(value) }
135157
end
136158

137159
contents = quote do
160+
138161
defmacrop unquote(name)() do
139-
Record.access(__MODULE__, unquote(escaped), [], __CALLER__)
162+
Record.access(unquote(tag) || __MODULE__, unquote(escaped), [], __CALLER__)
140163
end
141164

142165
defmacrop unquote(name)(record) when is_tuple(record) do
143-
Record.to_keywords(__MODULE__, unquote(escaped), record)
166+
Record.to_keywords(unquote(tag) || __MODULE__, unquote(escaped), record)
144167
end
145168

146169
defmacrop unquote(name)(args) do
147-
Record.access(__MODULE__, unquote(escaped), args, __CALLER__)
170+
Record.access(unquote(tag) || __MODULE__, unquote(escaped), args, __CALLER__)
148171
end
149172

150173
defmacrop unquote(name)(record, key) when is_atom(key) do
151-
Record.get(__MODULE__, unquote(escaped), record, key)
174+
Record.get(unquote(tag) || __MODULE__, unquote(escaped), record, key)
152175
end
153176

154177
defmacrop unquote(name)(record, args) do
155-
Record.dispatch(__MODULE__, unquote(escaped), record, args, __CALLER__)
178+
Record.dispatch(unquote(tag) || __MODULE__, unquote(escaped), record, args, __CALLER__)
156179
end
157180
end
158181

lib/elixir/src/elixir_dispatch.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ default_functions() ->
1818
default_macros() ->
1919
[ { ?BUILTIN, ordsets:union(in_elixir_macros(), in_erlang_macros()) } ].
2020
default_requires() ->
21-
[ ?BUILTIN, 'Elixir.Kernel.Typespec' ].
21+
[ ?BUILTIN, 'Elixir.Kernel.Typespec', 'Elixir.Record' ].
2222

2323
find_import(Meta, Name, Arity, S) ->
2424
Tuple = { Name, Arity },

lib/elixir/test/elixir/kernel/errors_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ defmodule Kernel.ErrorsTest do
119119
end
120120

121121
test :unrequired_macro do
122-
assert "nofile:2: tried to invoke macro Kernel.ErrorsTest.UnproperMacro.unproper/1 but module was not required. Required: Kernel, Kernel.Typespec" ==
122+
assert "nofile:2: tried to invoke macro Kernel.ErrorsTest.UnproperMacro.unproper/1 but module was not required. Required: Kernel, Kernel.Typespec, Record" ==
123123
format_rescue 'defmodule Foo do\nKernel.ErrorsTest.UnproperMacro.unproper([])\nend'
124124
end
125125

lib/elixir/test/elixir/record_test.exs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ Code.require_file "../test_helper.exs", __FILE__
33
defrecord RecordTest.FileInfo,
44
Record.extract(:file_info, from_lib: "kernel/include/file.hrl")
55

6+
defmodule RecordTest.FileInfo.Helper do
7+
Record.import RecordTest.FileInfo, as: :file_info
8+
9+
def new do
10+
file_info
11+
end
12+
13+
def size(file_info(size: size)), do: size
14+
end
15+
616
defrecord RecordTest.SomeRecord, a: 0, b: 1
717
defrecord RecordTest.WithNoField, []
818

@@ -183,6 +193,11 @@ defmodule RecordTest do
183193
end)
184194
end
185195

196+
test :import do
197+
assert RecordTest.FileInfo.Helper.new == RecordTest.FileInfo.new
198+
assert RecordTest.FileInfo.Helper.size(RecordTest.FileInfo.new(size: 100)) == 100
199+
end
200+
186201
defp file_info do
187202
{ :ok, file_info } = :file.read_file_info(__FILE__)
188203
file_info

0 commit comments

Comments
 (0)