I'm reading Programming Erlang by Joe Armstrong(Pragmatic Bookshelf). In name_server.erl source code on Chapter 16, Where's Dict variable from? Calling dict:new() generates Dict automatically? And, reference says that dict:new() creates dictionary. Don't I need to store it as a variable like Dict = dict:new()?
-module(name_server).
-export([init/0, add/2, whereis/1, handle/2]).
-import(server1, [rpc/2]).
add(Name, Place) ->
rpc(name_server, {add, Name, Place}).
whereis(Name) ->
rpc(name_server, {whereis, Name}).
init() ->
dict:new().
handle({add, Name, Place}, Dict) ->
{ok, dict:store(Name, Place, Dict)};
handle({whereis, Name}, Dict) ->
{dict:find(Name, Dict), Dict}.
This is part of a two file example. The other file (immediately before it in the book) is server.erl. It contains a loop function that calls the handle function in name_server.erl (or whatever module you pass to it):
The line is:
{Response, State1} = Mod:handle(Request, State),
where Mod is the module passed to start earlier. And State is initialised earlier as Mod:init() in the start function.
So State is initialised to name_server:init() which in your file returns dict:new(). However, as loop is called recursively State will take the next value of State1.
So when handle is called, Dict is set to the current value of State.
Related
The ejabberd module I'm using, mod_pottymouth is not filtering messages as expected. After adding logging I see a generic handler method being called instead of the one that does the actual filtering. Problem is, I am not able to parse the ejabberd message to ensure the proper function is called. Can anyone help?
on_filter_packet({_From, _To, {xmlel, <<"message">>, _Attrs, Els} = _Packet} = _Msg) ->
%This is what should be called to filter messages, but is never called
FilteredEls = filterMessageBodyElements(Els, []),
{_From, _To, {xmlel, <<"message">>, _Attrs, FilteredEls}};
on_filter_packet(Msg) ->
% This is what actually gets called
Msg.
This is using ejabberd 17.01
Starting from 16.12 ejabberd doesn't route xmlel elements. You should process new style records: message, presence or iq.
Please read https://docs.ejabberd.im/developer/guide/#ejabberd-router
and https://github.com/processone/xmpp/blob/master/README.md
So, basically, your code should look like this:
on_filter_packet(#message{body = Body} = Msg) ->
NewBody = filterMessageBody(Body),
Msg#message{body = NewBody};
on_filter_packet(Stanza) ->
Stanza.
Have you tried using xmlel as record instead of tuple ?
As an exercise to test my understanding of sequential Erlang I wrote a simple-minded html tag editor.
It maintains a current position in the text, e.g. edit state, in a record like this:
-record(ed_txt, {a=[], b=[], c=[]).
...where a holds text above the current paragraph, b holds the current paragraph, and c holds text below the current paragraph.
It is called like this:
tag(App, FileName) ->
{ok, File1} = get_file(App, FileName),
{ok, File2} = file_to_paragraphs(File1),
{ok, Record} = create_record(File2),
....
tag(Record) ->
{ok, InputTuple} = get_input(Record), % gets edit command
...
do(Command, Record)
The function do/2 is recursive function that looks like this:
do("h", Record) -> tag(help(Record));
do("down", Record) -> tag(down(Record));
do("up",Record) -> tag(up(Record));
do("h1", Record) -> tag(wrap("H1", Record));
...
do("quit", Record) -> {ok, quit(Record)};
do(_, Record) -> tag(Record).
Now I'd like to turn this module into an OTP gen-server, but realize that I don't really understand how to maintain state.
I created a state record:
-record(state, {app, filename, a=[], b=[], c=[]}).
My thought is to use a one-to-one supervisor so each user is working in his/her own process, ditch the edit loop, and use handle_call (or handle_cast) to call the respective
edit functions.
But what confuses me is this:
As long as the process is running, presumably user state is maintained in the process, but both handle_call and handle_cast take State as a parameter and return State.
Does this mean that I also have to maintain state for each user OUTSIDE of their respective processes, in ets perhaps?
Or is there a better way to do this altogether?
Many thanks,
LRP
I have skimmed through the Mochiweb code, but have not found any sign of the State variable.
Does something similar to gen_server's State variable exist in Mochiweb?
I need to store some small amount of state-related server-side (not session-related) data on the server and I do not want to use ETS or Mnesia for that.
I think you have somewhat a misunderstanding of what gen_server state is.
First, let me explain briefly how mochiweb works.
Mochiweb doesn't produce a gen_server process per client. Instead, it just spawns a new process using proc_lib:spawn/3 and creates a parametrized module, which is, basically, a tuple of the following kind:
{mochiweb_request, #Port<0.623>, get, "/users", {1, 1}, []}
which is
{mochiweb_request, Socket, Method, RawPath, HTTPVersion, Headers}
This tuple is used as an argument to a function that you pass as a loop parameter to mochiweb_http:start/1. So, when this "loop" function is called, it will look like this:
handle_request(Req) ->
%% The pattern matching below just shows what Req really is
{mochiweb_request, _, _, _, _, _} = Req,
...
Now, to explanation of gen_server state.
Basically, gen_server is a process with approximately the following structure. Of course, IRL it's more complicated, but this should give you the general idea:
init(Options)
State = ...
loop(Module, State).
loop(Module, State)
NewState = receive
{call, Msg, From} -> Module:handle_call(Msg, From, State)
{cast, Msg} -> Module:handle_cast(Msg, State)
Info -> Module:handle_info(Info, State)
end,
loop(Module, NewState).
So, state is just an argument that you drag through all the function calls and change inside your loop. It doesn't actually matter if your process is a gen_server or not, it doesn't have what lifetime it has. In the following example the term [1, 2, 3] is a state too:
a() ->
b([1, 2, 3], now()).
b(State, Timestamp) ->
Result = do_something(Timestamp)
c(State, Result).
c(State, Payload) ->
exit({State, Payload}).
Now, back to mochiweb.
If you need to create a state of your own, you can just add an extra function argument:
handle_request(Req) ->
User = Req:get(path),
UserData = load_user_data(User),
handle_request(Req, UserData).
handle_request(Req, UserData) ->
...
Now UserData is a state too. You can loop this process, or let it respond and end right away – but you won't lose UserData as long as you pass it as an argument.
Finally, if you really want to make this process a gen_server (which is really unreasonable in most cases), you can use gen_server:enter_loop/3 function that will make your current process a gen_server. And The 3rd argument of this function will be your state that will be stored inside the started gen_server.
I am trying to use Erlang/ets to store/update various informations by pattern matching received data. Here is the code
start() ->
S = ets:new(test,[]),
register(proc,spawn(fun() -> receive_data(S) end)).
receive_data(S) ->
receive
{see,A} -> ets:insert(S,{cycle,A}) ;
[[f,c],Fcd,Fca,_,_] -> ets:insert(S,{flag_c,Fcd,Fca});
[[b],Bd,Ba,_,_] -> ets:insert(S,{ball,Bd,Ba})
end,
receive_data(S).
Here A is cycle number, [f,c] is center flag , [b] is ball and Fcd,Fca, Bd, Ba are directions and angle of flag and ball from player.
Sender process is sending these informations. Here, pattern matching is working correctly which I checked by printing values of A, Fcd,Fca..etc. I believe there is something wrong with the use of Erlang/ets.
When I run this code I get error like this
Error in process <0.48.0> with exit value: {badarg,[{ets,insert,[16400,{cycle,7}]},{single,receive_data,1}]
Can anybody tell me what's wrong with this code and how to correct this problem?
The problem is that the owner of the ets-table is the process running the start/1 function and the default behavior for ets is to only allow the owner to write and other processes to read, aka protected. Two solutions:
Create the ets table as public
S = ets:new(test,[public]).
Set the owner to your newly created process
Pid = spawn(fun() -> receive_data(S) end,
ets:give_away(test, Pid, gift)
register(proc,Pid)
Documentation for give_away/3
The following code gives me an error: "syntax error before: Some_ets"
-module(tut).
-export([incr/1]).
Some_ets = ets:new(?MODULE, [bag]).
incr(X) ->
X+1.
But I am able to declare the ETS within a function, like:
-module(tut).
-export([incr/1]).
incr(X) ->
Some_ets = ets:new(?MODULE, [bag]),
X+1.
Can't I declare a ETS outside a function?
No - unlike other languages there isn't a concept of static initialization - there's no appropriate time for an Erlang system to execute that piece of code.
Erlang does have the concept of a parameterized module however, and that may be what you're after. Have a look here http://www.lshift.net/blog/2008/05/18/late-binding-with-erlang which is a good write up of that - it would allow you to instantiate an "instance" of your tut module bound to a given ets table and save passing around that handle explicitly in your module function calls.
Or if you are into OTP you could have the handle to the ets table passed around in the state variable:
init(_) ->
Some_ets = ets:new(?MODULE, [bag]),
{ok, Some_ets}.
and then use it in your handle_call methods:
get_ets_handle() ->
gen_server:call(?MODULE, {getETSHandle}, infinity).
handle_call({getETSHandle}, _From, Some_ets) ->
{reply, Some_ets, Some_ets}.
You can't do variable assignments like that in a module. See here.