@@ -1508,10 +1508,42 @@ defmodule Module do
1508
1508
"""
1509
1509
@ spec get_attribute ( module , atom , term ) :: term
1510
1510
def get_attribute ( module , key , default \\ nil ) when is_atom ( module ) and is_atom ( key ) do
1511
- case __get_attribute__ ( module , key , nil , true ) do
1512
- nil -> default
1513
- value -> value
1514
- end
1511
+ get_attribute ( module , key , nil , true , false , default , { :get_attribute , 2 } )
1512
+ end
1513
+
1514
+ @ doc """
1515
+ Gets the last set value of a given attribute from a module.
1516
+
1517
+ If the attribute was marked with `accumulate` with
1518
+ `Module.register_attribute/3`, the previous value to have been set will be
1519
+ returned. If the attribute does not accumulate, this call is the same as
1520
+ calling `Module.get_attribute/3`.
1521
+
1522
+ This function can only be used on modules that have not yet been compiled.
1523
+ Use the `c:Module.__info__/1` callback to get all persisted attributes, or
1524
+ `Code.fetch_docs/1` to retrieve all documentation related attributes in
1525
+ compiled modules.
1526
+
1527
+ ## Examples
1528
+
1529
+ defmodule Foo do
1530
+ Module.put_attribute(__MODULE__, :value, 1)
1531
+ Module.get_last_attribute(__MODULE__, :value) #=> 1
1532
+
1533
+ Module.get_last_attribute(__MODULE__, :not_found, :default) #=> :default
1534
+
1535
+ Module.register_attribute(__MODULE__, :acc, accumulate: true)
1536
+ Module.put_attribute(__MODULE__, :acc, 1)
1537
+ Module.get_last_attribute(__MODULE__, :acc) #=> 1
1538
+ Module.put_attribute(__MODULE__, :acc, 2)
1539
+ Module.get_last_attribute(__MODULE__, :acc) #=> 2
1540
+ end
1541
+
1542
+ """
1543
+ @ doc since: "1.15.0"
1544
+ @ spec get_last_attribute ( module , atom , term ) :: term
1545
+ def get_last_attribute ( module , key , default \\ nil ) when is_atom ( module ) and is_atom ( key ) do
1546
+ get_attribute ( module , key , nil , true , true , default , { :get_last_attribute , 2 } )
1515
1547
end
1516
1548
1517
1549
@ doc """
@@ -1871,8 +1903,20 @@ defmodule Module do
1871
1903
# Used internally by Kernel's @.
1872
1904
# This function is private and must be used only internally.
1873
1905
def __get_attribute__ ( module , key , caller_line , trace? ) when is_atom ( key ) do
1906
+ get_attribute ( module , key , caller_line , trace? , false , nil , { :get_attribute , 2 } )
1907
+ end
1908
+
1909
+ defp get_attribute (
1910
+ module ,
1911
+ key ,
1912
+ caller_line ,
1913
+ trace? ,
1914
+ last_accumulated? ,
1915
+ default ,
1916
+ function_name_arity
1917
+ ) do
1874
1918
assert_not_compiled! (
1875
- { :get_attribute , 2 } ,
1919
+ function_name_arity ,
1876
1920
module ,
1877
1921
"Use the Module.__info__/1 callback or Code.fetch_docs/1 instead"
1878
1922
)
@@ -1882,7 +1926,7 @@ defmodule Module do
1882
1926
case :ets . lookup ( set , key ) do
1883
1927
[ { _ , _ , :accumulate , traces } ] ->
1884
1928
trace_attribute ( trace? , module , traces , set , key , [ ] )
1885
- :lists . reverse ( bag_lookup_element ( bag , { :accumulate , key } , 2 ) )
1929
+ lookup_accumulate_attribute ( bag , key , default , last_accumulated? )
1886
1930
1887
1931
[ { _ , value , warn_line , traces } ] when is_integer ( warn_line ) ->
1888
1932
trace_attribute ( trace? , module , traces , set , key , [ { 3 , :used } ] )
@@ -1899,10 +1943,21 @@ defmodule Module do
1899
1943
"please remove access to @#{ key } or explicitly set it before access"
1900
1944
1901
1945
IO . warn ( error_message , attribute_stack ( module , caller_line ) )
1902
- nil
1946
+ default
1903
1947
1904
1948
[ ] ->
1905
- nil
1949
+ default
1950
+ end
1951
+ end
1952
+
1953
+ defp lookup_accumulate_attribute ( bag , key , _default , false ) do
1954
+ :lists . reverse ( bag_lookup_element ( bag , { :accumulate , key } , 2 ) )
1955
+ end
1956
+
1957
+ defp lookup_accumulate_attribute ( bag , key , default , true ) do
1958
+ case :ets . select ( bag , [ { { { :accumulate , key } , :"$1" } , [ ] , [ :"$1" ] } ] , 1 ) do
1959
+ { [ value ] , _ } -> value
1960
+ _ -> default
1906
1961
end
1907
1962
end
1908
1963
0 commit comments