@@ -1508,10 +1508,42 @@ defmodule Module do
15081508 """
15091509 @ spec get_attribute ( module , atom , term ) :: term
15101510 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 } )
15151547 end
15161548
15171549 @ doc """
@@ -1871,8 +1903,20 @@ defmodule Module do
18711903 # Used internally by Kernel's @.
18721904 # This function is private and must be used only internally.
18731905 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
18741918 assert_not_compiled! (
1875- { :get_attribute , 2 } ,
1919+ function_name_arity ,
18761920 module ,
18771921 "Use the Module.__info__/1 callback or Code.fetch_docs/1 instead"
18781922 )
@@ -1882,7 +1926,7 @@ defmodule Module do
18821926 case :ets . lookup ( set , key ) do
18831927 [ { _ , _ , :accumulate , traces } ] ->
18841928 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? )
18861930
18871931 [ { _ , value , warn_line , traces } ] when is_integer ( warn_line ) ->
18881932 trace_attribute ( trace? , module , traces , set , key , [ { 3 , :used } ] )
@@ -1899,10 +1943,21 @@ defmodule Module do
18991943 "please remove access to @#{ key } or explicitly set it before access"
19001944
19011945 IO . warn ( error_message , attribute_stack ( module , caller_line ) )
1902- nil
1946+ default
19031947
19041948 [ ] ->
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
19061961 end
19071962 end
19081963
0 commit comments