Erlang syntax error before: 'end' - erlang

I have recently started learning erlang, but have encountered an error that just leaves me puzzled.
The error is syntax error before: 'end' on the very last line. I have looked at examples an tried to find the error but I'm totally lost at the moment. Any Ideas?
ChannelToJoin = list:keysearch(ChannelName,1,State#server_st.channels),
case ChannelToJoin of
% Channel exists.
{value, Tuple} ->
if
%User is not a member of the channel
not list:member(UserID, Tuple) ->
%Add the user to the channel
Tuple#channel.users = list:append(Tuple#channel.users, [UserID]);
% If the user is already a member of the channel.
true -> true
end;
%Channel doesn't exist
false ->
%Create new channel and add the user to it.
NewState = State#server_st{channels = list:append(State#server_st.channels, NewChannel = #channel{name = ChannelName, users = [UserID]}
end

The second to last line, NewState = ..., is missing two closing parentheses: )}
Also note that you can't use lists:member inside if, as function calls are not allowed inside guard expressions (which is what if lets you use). Instead, use case:
case lists:member(UserID, Tuple#channel.users) of
false ->
%% Add the user to the channel
...;
true ->
%% Already a member
ok
end

Related

The expected Ejabberd filter_packet function not being called

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 ?

What am I doing wrong with erl_parse:parse_form?

I wrote a Hello-World module and compiled it successfully. And then I tried to learn the things under the hood by using erl_scan and erl_parse.
-module(useless).
-export([hello/0]).
hello() -> io:format("hello world\n").
and I type in the erl shell
{ok, S} = file:read_file("useless.erl").
and
{ok, T, _} = erl_scan:string(binary_to_list(S)).
It works fine apparently. But when I try erl_parse:parse_form(T).
It gives {error,{2,erl_parse,["syntax error before: ","'-'"]}}
What am I doing wrong?
Edit:
the module compile is helpful.
Also this is cool.
The function erl_parse:parse_form/1 works on one form only. So you must split the result of erl_scan:string/1 into individual forms first.
you can use erl_scan:tokens to achieve this (this code wors, but I am not sure I use the function in the right way):
-module(useless).
-export([eval/1]).
eval(File) ->
{ok, B} = file:read_file(File),
Forms = scan(erl_scan:tokens([],binary_to_list(B),1),[]),
F = fun(X) -> {ok,Y} = erl_parse:parse_form(X), Y end,
[F(X) || X <- Forms].
scan({done,{ok,T,N},S},Res) ->
scan(erl_scan:tokens([],S,N),[T|Res]);
scan(_,Res) ->
lists:reverse(Res).
Robert suggestion is to use the re-entrant feature of erl_scan:tokens/3.
The docs on this function are not explicit and it took me some time to understand that the initial string needed to be closed by eof. (if not the tokens function runs an endless loop).
Here is the code I finally came up with, I hope that will help others.
eval(File)->
{ok, Data} = file:read_file(File),
String=erlang:binary_to_list(Data),
scan(String).
scan(String) when is_list(String)->
scan(String++eof,[]). %% appended eof
scan({done, Result, LeftOverChars},Acc)->
scan_done(Result,LeftOverChars,Acc);
scan({more, Continuation},Acc)->
scan(erl_scan:tokens(Continuation,[],1),Acc);
scan(String,Acc) when is_list(String)->
scan(erl_scan:tokens([],String,1),Acc).
scan_done({error,ErrorMsg,_Location},_LeftOverChars,_Acc)->
ErrorMsg;
scan_done({eof,Location},LeftOverChars,Acc)->
Acc;
scan_done({ok,Tokens,Location},LeftOverChars,Acc)->
case erl_parse:parse_form(Tokens) of
{ok,R}->scan(LeftOverChars,Acc++[R]);
{error,R}->scan(LeftOverChars,R)
end.

unknow erlang syntax

In the file of "jobs_info.erl" of github project "jobs", it contains the following codes:
pp(L) when is_list(L) ->
[pp(X) || X <- L];
pp(X) ->
case '#is_record-'(X) of %<=========What's meaning of '"#is_record-'?
true ->
RecName = element(1,X),
{RecName, lists:zip(
'#info-'(RecName,fields), %<=======what's meaning of ''#info-'?
pp(tl(tuple_to_list(X))))};
false ->
if is_tuple(X) ->
list_to_tuple(pp(tuple_to_list(X)));
true ->
X
end
end.
What's the expression of '#is_record-' and ''#info-'?
"is_record" may be refered to erlang:is_record?
But what's "'#info-'"?
As Kemal points out have a look at the method declaration at github.com/esl/parse_trans/blob/master/src/exprecs.erl#L116.
If it is the ' that confuses you, remember that a function name in Erlang is an atom and that an atom needs to start with a lower-case letter and if it does not it should be surrounded by '. Check the docs for more info.

Get field from mnesia

I have a mnesia table users with user and password field.
Data from my table:
[{users, <<"user_name">>, <<"password">>}].
I need to get password by user name. I make:
mnesia:dirty_read({users, <<"user_name">>}).
But it returns [].
How can I get the password by user name?
You didn't specify the record syntax you are using, but it looks like
-record(users, {username, password}).
... or something similar. So, assuming that, when you created the table, did you do anything special to set the id? In this example "username" (the first entry in the users record) should be the id by default, unless you did something special.
If you continue to have issues, consider using mnesia:match_object/1 or /3. You specify, in pattern / record syntax the portion you have to match (in this case, the username), and use ='' to match anything you don't know or care about (in this case, that'd be the password portion).
I hope that helps!
You can do something like below:
YourPasswd = mnesia:ets(fun()-> mnesia:dirty_read({users,UserId}) end),
case YourPasswd of
[] -> {error, 'No User Found'};
[{users,_UserID,Passwd}] ->
{success, Passwd}
end.
Hope data is correctly written into mnesia :)
look at this function:
-define(TABLE_NAME,users).
get_password_by_username(Username)->
F = fun(U)-> mnesia:read({?TABLE_NAME,U}) end,
mnesia:activity(transaction,F,[Username],mnesia_frag).
That will give you the result. The good thing with mnesia:activity/4 is that wether the table is fragmented or not, the answers are okay.
good luck
I understand you want your passwords checked as quickly as possible, but is your project at the stage where you need to optimize this?
I've an authentication module in one of my projects and I have a similar goals as yours. As I haven't had occasion to optimize just yet, I am using mnsesia transactions and lists for user names and passwords.
Here is part of my auth module.
-module(auth).
-export([init/1, add_user/2, start_session/2]).
-record(auth_user, {username, password}).
init(Nodes) ->
mnesia:create_table(auth_user,
[{disc_copies, Nodes},
{attributes, record_info(fields, auth_user)}]).
add_user(Username, Password) ->
T = fun() ->
mnesia:write(#auth_user {
username = Username,
password = Password})
end,
mnesia:transaction(T).
start_session(Username, Password) ->
T = fun() ->
mnesia:read(auth_user, Username)
end,
{atomic, Ret} = mnesia:transaction(T),
case Ret of
[U] ->
if (U#auth_user.password == Password) ->
true;
true ->
false
end;
_Else ->
false
end.
After compiling and starting up the erlang shell.
Eshell V5.8.3 (abort with ^G)
1> mnesia:create_schema([node()]).
ok
3> mnesia:start().
ok
4> auth:init([node()]).
{atomic,ok}
5> auth:add_user("rodericktaylor", "password").
{atomic,ok}
6> true = auth:start_session("rodericktaylor", "password").
true
7>
To see if I get the same issue you are having, I switched to binary values and did dirty reads.
start_session_dirty(Username, Password) ->
case mnesia:dirty_read(auth_user, Username) of
[U] ->
if (U#auth_user.password == Password) ->
true;
true ->
false
end;
_Else ->
false
end.
The following commands in the erl shell show that It works how you expect it to.
12> auth:add_user(<<"rodericktaylor">>, <<"binarypassword">>).
{atomic,ok}
14> true = auth:start_session_dirty(<<"rodericktaylor">>, <<"binarypassword">>).
true
15>
I hope I've helped.

Unable to use Erlang/ets in receive block

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

Resources