Yaws Cyrillic output - yaws

Can you help me please?
At the web-page, I need print this:
team 1 vs team 2
team 3 vs team 4
team 5 vs team 6
team 7 vs team 8
My code:
<erl>
first_team_write(Head) ->
{ehtml,
%[{text,[], f("~p vs ", [Head])}]}. % try
[{p,[], [Val, " vs "]} || Val <- [Head]]}. % different
second_team_write(Head) ->
{ehtml,
%[{text,[], f("~p", Head)}, % ways
[{p,[], [Head]}, %
{p,[], []}]}.
write_teams([], Num) ->
Num;
write_teams([Head|Tail], Num) ->
if
(Num rem 2) /= 0 ->
first_team_write([Head]),
io:format("~p vs ", [Head]), %% debug
write_teams(Tail, Num+1);
(Num rem 2) == 0 ->
second_team_write(Head),
io:format("~p~n", [Head]), %% debug
write_teams(Tail, Num+1)
end.
out(A) ->
application:start(odbc),
ConnString =
%"Driver={MySQL ODBC 5.2 ANSI Driver};" ++
"Driver={MySQL ODBC 5.2 Unicode Driver};" ++
"Server=127.0.0.1;Database=erandom;" ++
"User=root;Password=1q2w3e;" ++
"Option=3;" ++
"CharSet=utf8;",
{ok, Conn} = odbc:connect(ConnString, []),
% Cyrillic results
Results = odbc:sql_query(Conn, "select team_name from teams where id in (select team_id from leagues where league_name = 'First League')"),
%{selected, [Selector], Results} = odbc:sql_query(Conn, "select team_name from teams where team_name in ('Sporting', 'Old School')"),
%{selected, [Selector], Results} = odbc:sql_query(Conn, "select team_name from teams where id in (select team_id from leagues where league_name = 'First League')"),
odbc:disconnect(Conn),
application:stop(odbc),
TeamList = element(3, Results),
{ehtml,
[{h4,[], "Пары этого тура:"},
{hr},
write_teams(TeamList,1)
]}.
</erl>
Result: My web page is blank.
What am I doing wrong?
I see in the debug console:
1>
=INFO REPORT==== 25-Feb-2014::23:08:27 ===
application: odbc
exited: stopped
type: temporary
1> {[208,159,209,128,208,190,208,188,208,181,209,130,208,181,208,185]} vs 1> {[208,161,208,145,208,162]}
1> {[208,158,208,187,208,184,208,188,208,191]} vs 1> {[208,159,209,128,208,190,208,179,209,128,208,181,209,129,209,129]}
1> {[208,164,208,154,32,208,148,209,142,208,182,208,184,208,189,208,176]} vs 1> {"Old school"}
1> {[208,161,208,191,208,176,209,128,209,130,208,176,208,186]} vs 1> {[208,160,208,190,209,130,208,190,209,128]}
1> {"Sporting"} vs 1> {[208,161,208,190,208,191,209,128,208,190,209,130,208,184,208,178,208,187,208, 181,208,189,208,184,208,181]}

I believe you're not specifying the charset correctly in your HTTP response. First, since you have Cyrillic text embedded in your .yaws page, make sure your editor is saving it as UTF-8. How you accomplish this depends on your development environment and editor, but I use emacs, so I just added the following HTML comment to the top of the .yaws file:
<!-- -*- coding: utf-8 -*- -->
The next problem is getting the charset set correctly in the HTTP headers of the response. To do that, set the Content-Type header appropriately in your reply like this:
[{header, {content_type, erase}},
{header, {content_type, "text/html; charset=UTF-8"}},
{ehtml,
[{h4,[], "Пары этого тура:"},
{hr},
write_teams(TeamList,1)
]}].
Note that the whole return type is an Erlang list. The first tuple in the list erases the default Content-Type header that Yaws applies by default to .yaws pages. The second tuple in the list specifies the correct value for Content-Type including the UTF-8 charset. The third element of the list is the same ehtml tuple you specified in your problem statement above.
BTW you can build Yaws with UTF-8 as the default charset by running the Yaws configure script as shown below:
configure --with-defaultcharset=UTF-8
After that, rebuild Yaws with make as usual. If you do this, the two tuples for setting the Content-Type of your reply are no longer needed, since UTF-8 would be the default charset for all replies.

Related

Reading files in Erlang

I have an Erlang app that fetches some key/value pair from files.
In one of the files, instead of the key/value pairs, I have a path to a different folder, in which it has the key/value pairs.
The reading of the files are working fine, but whenever I have to read the path from a file and just then read the subsequent file, the app is complaining that the file is nonexistent.
If I jump into the container and check, I can see the file and the subsequent file. So I guess I'm missing something.
Here is what I have:
get_kvs("keyvalue.properties"), %% <-- This works
{ok, PathToFile} = file:read_file("pathtofile.properties"),
get_kvs(PathToFile), %% <-- This crashes
Files:
keyvalue.properties:
key_1=val_1
key_2=val_2
key_3=val_3
pathtofile.properties:
/data/myfolder/hidden_keyvalue.properties
/data/myfolder/hidden_keyvalue.properties:
extra_key1=extra_val1
extra_key2=extra_val2
extra_key3=extra_val3
And the get_metadata function:
get_metadata(FileName) ->
io:format(FileName),
{ok, MetadataFile} = file:read_file(FileName),
io:format(MetadataFile),
Lines = binary:split(MetadataFile, <<"\n">>, [trim, global]),
make_tuples(Lines, []).
make_tuples([Line|Lines], Acc) ->
[Key, Value] = binary:split(Line, <<"=">>),
make_tuples(Lines, [{Key, Value}|Acc]);
make_tuples([], Acc) -> lists:reverse(Acc).
Whenever running it I can see that the PathToFile is being properly populated, but when I try to read the path I get the error below:
keyvalue.propertiesextra_key_1=extra_val_1
extra_key_2=extra_val_2
extra_key_3=extra_val_3
/data/myfolder/hidden_keyvalue.properties
=CRASH REPORT==== 23-Mar-2022::07:46:30.612093 ===
crasher:
initial call: cowboy_stream_h:request_process/3
pid: <0.735.0>
registered_name: []
exception error: no match of right hand side value {error,enoent}
in function hello_handler:get_metadata/1 (/data/apps/hello_server/src/hello_handler.erl, line 40)
in call from hello_handler:child_call/1 (/data/apps/hello_server/src/hello_handler.erl, line 28)
Any ideas of what am I missing?
After the OP has been modified to reflect the actual point of failure by removing try-catch, the error seems to be {error, enoent }which means The file does not exist. However, the same function is working in some scenarios and not when the path of the file to be read is itself taken from another file.
Just make sure there are mo additional characters, like newlines or non-printable characters after the content of the file which should actually be a valid path.
For example, when I tried with a value as such, <<"hidden_keyvalue.properties\n\n">>, then read_file gave me same result as {error, enoent}.
So it could be possible that the content of the file from which paths are read has additional non-printable characters at the end.
Ignore (Wrong Assumption)
I tried with a local setup and I think this line inside make_tuples is causing that behavior.
[Key, Value] = binary:split(Line, <<"=">>) %% <--- This match will succeed only if there are exactly 2 elements in the list produced by binary:split(...).
Given we are doing inside get_metadata
Lines = binary:split(MetadataFile, <<"\n">>, [trim, global]), %% <-- This splits the contents of the keyvalue.properties file into a List
make_tuples(Lines, [])
From the provided text, it appears that the content of keyvalue.proiperties file is ...
key_1=val_1
key_2=val_2
key_3=val_3
/data/myfolder/hidden_keyvalue.properties %% <-- This isn't a valid key-value pair
And with that last line, make_tuples while doing a match will fail the following way...
[Key, Value] = binary:split(<<"/data/myfolder/hidden_keyvalue.properties">>, <<"=">>).
** exception error: no match of right hand side value [<<"/data/myfolder/hidden_keyvalue.properties">>]
That pattern-match expression requires exactly 2 elements on the right-hand side(the Term/Value side). Since the line with path-entry in the keyvalue.properties does not have an =, so the split produces a list with 1 element, and so the match fails.
To address this...
We can change the format of keyvalue.properties so that every line is a valid key-value pair, but not sure how feasible it will be in the context of the program.
Or, we can change that pattern-match form of list so that it can accept non-exact number of terms while matching
make_tuples([Line|Lines], Acc) ->
%% [Key|Value] is at least 1 element list, [Key, Value] is exactly 2 elements list
[Key|Value] = binary:split(Line, <<"=">>), %% <-- Now Value becomes a list of binary
make_tuples(Lines, [{Key, get_value(Value)}|Acc]); %% <-- Additional get_value function to extract 'Value binary' from the head of the 'Value List' produced in the previous line
make_tuples([], Acc) -> lists:reverse(Acc).
get_value([]) ->
<<>>;
get_value([V|_]) -> V.
Assumptions
get_kvs invokes get_metadata which in turn invokes make_tuples.
keyvalue.properties file content has both valid key-value pairs and also some non-key-value entries

How to format a number to string (currency) with thousands and decimal separators using Erlang?

I'm working on an application in Erlang that has to deal with number formatting for several different currencies. There are many complexities to take in consideration, like currency symbols, where it goes (right or left), etc. But one aspect of this kind of formatting use to be straight forward in other programming languages but it's being hard in Erlang, which is the separators to use for thousands and decimal places.
For most of the English speaking countries (independent of the currency) the expected number format is:
9,999,999.99
For several other countries, like Germany and Brazil, the number format is different:
9.999.999,99
There are other variations, like the French variation, with spaces:
9 999 999,99
The problem is that I can't find a nice way to achieve these formats, starting from the floating point number. Sure the io_lib:format/2 can convert it to a string, but it seems to not offer any control over the symbols to use as decimal separator and doesn't output any separator for the thousands (which makes a workaround to "search-and-replace" impossible).
This is an example of what we have so far:
%% currency_l10n.erl
-module(currency_l10n).
-export([format/2]).
number_format(us, Value) ->
io_lib:format("~.2f", [Value]);
number_format(de, Value) ->
io_lib:format("~B,~2..0B", [trunc(Value), trunc(Value*100) rem 100]).
%% Examples on the Erlang REPL:
1> currency_l10n:number_format(us, 9999.99).
"9999.99"
2> currency_l10n:number_format(de, 9999.99).
"9999,99"
As you can see, already the workaround for the decimal separator is not exactly pretty and dealing with the delimiters for thousands won't be nicer. Is there anything we are missing here?
The problem you have is not solve (AFAIK) by any standard library in erlang. It needs several actions to produce the expected result: convert the float to string, split the string in packets, insert 2 kinds of separator and insert the currency sign at the beginning or the end. You need different functions for these tasks. the following code is an example of what you could do:
-module (pcur).
-export ([printCurrency/2]).
% X = value to print, must be a float
% Country = an atom representing the country
printCurrency(X,Country) ->
% convert to string, split and get the different packets
[Dec|Num] = splitInReversePackets(toReverseString(X)),
% get convention for the country
{Before,Dot,Sep,After} = currencyConvention(Country),
% build the result - Beware of unicode!
Before ++ printValue(Num,Sep,Dot ++ Dec) ++ After.
toReverseString(X) -> lists:reverse(io_lib:format("~.2f",[X])).
splitInThousands([A,B,C|R],Acc) -> splitInThousands(R,[[C,B,A]|Acc]);
splitInThousands([A,B|R],Acc) -> splitInThousands(R,[[B,A]|Acc]);
splitInThousands([A|R],Acc) -> splitInThousands(R,[[A]|Acc]);
splitInThousands([],Acc) -> Acc.
splitInReversePackets([A,B,$.|R]) -> lists:reverse(splitInThousands(R,[[B,A]])).
% return a tuple made of {string to print at the beginning,
% string to use to separate the integer part and the decimal,
% string to use for thousand separator,
% string to print at the end}
currencyConvention(us) -> {"",".",",","$"};
currencyConvention(de) -> {"",",","."," Euro"}; % not found how to print the symbol €
currencyConvention(fr) -> {"Euro ",","," ",""};
currencyConvention(_) -> {"",".",",",""}. % default for unknown country symbol
printValue([A|R=[_|_]],Sep,Acc) -> printValue(R,Sep, Sep ++ A ++ Acc);
printValue([A],_,Acc) -> A ++ Acc.
test in the shell:
1> c(pcur).
{ok,pcur}
2> pcur:printCurrency(123456.256,fr).
"Euro 123 456,26"
3> pcur:printCurrency(123456.256,de).
"123.456,26 Euro"
4> pcur:printCurrency(123456.256,us).
"123,456.26$"
5>
Edit Reading the other proposal and your comments, this solution is clearly not the direction you are going to. Nevertheless, it is in my opinion a valuable way to solve your problem. It should be fast and more important for me, straight and easy to read and update (add currency, split in different size, ??)
I suppose you can use re:replace/4 http://erlang.org/doc/man/re.html#replace-4
Eg for DE:
1> re:replace("9999.99", "\\.", ",",[global, {return,list}]).
"9999,99"
But, for adding comma to last two chars as in French, I suppose you can try do something like
1> N = re:replace("9.999.999.99", "\\.", " ",[global, {return,list}]).
"9 999 999 99"
2> Nr = lists:reverse(N).
"99 999 999 9"
3> Nrr = re:replace("99 999 999 9", "\\ ", ",",[{return,list}]).
"99,999 999 9"
4> lists:reverse(Nrr).
"9 999 999,99"
Or you can try create more better regex.
P.S. For convert integer/float to list, you can try use io_lib:format/2, eg
1> [Num] = io_lib:format("~p", [9999.99]).
["9999.99"]
2> Num.
"9999.99"

Query from ejabberd module with LIKE %%

I have this lines of code in ejabberd module, it works fine:
case catch ejabberd_odbc:sql_query(Server,["select COUNT(*) as total from spool where username='",IdUsername,"' AND xml LIKE '%message from%' AND xml LIKE '%chat%';"]) of
{selected, [<<"total">>], [[Totale]]} ->
Count = binary_to_list(Totale);
_ -> Count = "0"
end,
If I convert this:
LIKE '%chat%';
with this:
LIKE '%type=\'chat\'%';
I obtain an error, any ideas? or there's another way to get only the chat message?
Since you're typing this in an Erlang string, the Erlang escape sequences apply. In particular, \' is an escape sequence for just a single quote, '. (That's more useful inside atoms, which are delimited by single quotes.)
You can try it in an Erlang shell, and see that "\'" and "'" are equivalent:
1> "\'".
"'"
2> "\'" =:= "'".
true
To include an actual backslash in the string, escape it with another backslash:
"\\'"
In your case, that would be:
LIKE '%type=\\'chat\\'%';

Value from binding in LFE interpreter using Erlang

I'd like to use Lisp Flavored Erlang as a scripting extension language for an Erlang application. If you want, in a similar way GNU Emacs is configured and extended via Emacs Lisp.
I know that the argument is wide and structured; but in the specific case of this question I'd like being able to read a binding name (or variable, if you prefer) defined in LFE from Erlang code.
I'm not an expert of LFE internal architecture (which is an excellent example of software engineering and Erlang programming), but I was not able to find an answer neither in the sources nor in the documentation.
Looking at sources I can see that LFE contains both a compiler that target Erlang VM and an interpreter.
The latter is the one I'm trying to use.
If I start Erlang shell/REPL in LFE installation path (on my system $HOME/opt/lfe):
$ cd /path/to/LFE-install-dir
$ erl -pa ./ebin
I'm able to calculate a value:
1> {ok, Expr} = lfe_io:read_string("(+ 1 10)").
{ok,['+',1,10]}
2> Result = lfe_eval:expr(Expr).
11
This is a first step, but not exactly what I want. I'd like rather to bind a variable and read its value; that's my issue:
3> {ok, Expr2} = lfe_io:read_string("(set a 10)").
{ok,[set,a,10]}
4> lfe_eval:expr(Expr2).
** exception error: {unbound_func,{set,2}}
in function lfe_eval:eval_expr/2
Why set is recognized as an unbound function? In LFE REPL this expression is valid:
Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] ...
LFE Shell V6.4 (abort with ^G)
> (set a 10)
10
> a
10
I'm obviously using the API in the wrong way. How can I read the content of a and/or properly initialize the LFE interpreter?
(If this is explained somewhere, please provide the reference).
I won't attempt to answer completely your broader question about the "best practices" of adding scripting. It seems to me that choosing between "hook-based" solution (in which you define hook implementations by name convention and they are automatically recognized) and "explicit api" solution (in which you use functions predefinied in the scripting enviroment to register your hooks or otherwise call configuration functions) is largely a matter of taste. Explicit calls like (set-connection-timeout-handler ...) may be more readable, easier to debug (no misspelling problems, no surprises on api changes), easier to document, and a bit more flexible, but more, well, explicit.
Building from your simple variable definition example, here are a few ways you could get started going further the "interpreted" path:
1> {ok, Expr} = lfe_io:read_string("'((a 10))").
{ok,[quote,[[a,10]]]}
2> lfe_eval:expr (Expr).
[[a,10]]
3> EvalAll = fun (Conf) -> {ok, E} = lfe_io:read_string("'(" ++ Conf ++ ")"), lfe_eval:expr(E) end.
#Fun<erl_eval.6.90072148>
4> EvalAll ("(a 10) (b 11)").
[[a,10],[b,11]]
5> EvalAllL = fun (Conf) -> {ok, E} = lfe_io:read_string("(list " ++ Conf ++ ")"), lfe_eval:expr(E) end.
#Fun<erl_eval.6.90072148>
6> [{f, F}] = EvalAllL ("(tuple 'f (lambda (x) (+ 10 x)))").
[{f,#Fun<lfe_eval.12.2018457>}]
7> F (12).
22
8> G = fun (X) -> X * 2 end.
#Fun<erl_eval.6.90072148>
9> lfe_eval:expr (element (2, lfe_io:read_string ("(g 15)")), lfe_eval:add_lexical_func(g, 1, G, lfe_env:new ())).
30
A simple way is use to the 'lfe' command to run LFE scripts. The 'lfe' behaves in similar fashion to 'bash' in that you can use it to run scripts. The 'lfec' script defined in bin/lfec is a good example of this. It is an LFE shell script which parses it arguments and run the LFE compiler.

splitting of binaries

I tried to split two fields from a binary string:
-define(S,<<"M\0\0\0522039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,‌​0110,00,150,0,0,0\0">>).<<Message_length:4/binary,Msg/binary>> = S.
the first 4 bytes are the length of the following message, the other byte are the message,
a null byte terminates the string.
The result is:
** exception error: o match of right hand side value
EDIT
Just before the given code, there is:
[Sequence|Reste] = binary:split(T,<<"\0">>),
Does "Reste" bounded ?
Your code is ok, so either you dont have a binary string, or the length of Mystring does not comply with the pattern. Here's a quick test:
1> Mystring = <<"abcde">>.
<<"abcde">>
2> <<Message_length:4/binary,Msg/binary>> = Mystring.
<<"abcde">>
3> Message_length.
<<"abcd">>
4> Msg.
<<"e">>
If you have a string (a list of integers) instead of a binary string (<<"string">>), as Vincenzo suggested, call erlang:list_to_binary/1 first.
Hope it helps
EDIT: I've checked the example string you left in a comment of Vincenzo's answer. I've tried it with your code and still works. Is it possible that Message_length and/or Msg are already bound (and different to Mystring) when reaching that line of code? That would make the pattern matching fail.
EDIT2: Tested with the updated data in the question:
1> S = <<"M\0\0\0522039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,\342\200\214\342\200\2130110,00,150,0,0,0\0">>.
<<77,0,0,42,50,48,51,57,51,53,53,48,57,57,44,48,49,48,49,
48,48,48,48,48,48,48,56,44,48,...>>
2> <<Message_length:4/binary,Msg/binary>> = S.
<<77,0,0,42,50,48,51,57,51,53,53,48,57,57,44,48,49,48,49,
48,48,48,48,48,48,48,56,44,48,...>>
3> Message_length.
<<77,0,0,42>>
4> Msg.
<<"2039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,\342"...>>
There is issue with erlang string escape interpolation. The fourth byte is not interpolated as "\0" but "\052".
1> Bin = <<"M\0\0\0522039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,0110,00,150,0,0,0\0">>.
<<77,0,0,42,50,48,51,57,51,53,53,48,57,57,44,48,49,48,49,
48,48,48,48,48,48,48,56,44,48,...>>
So you have to write it in this manner.
2> f().
ok
3> Bin = <<"M\0\0\0","522039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,0110,00,150,0,0,0\0">>.
<<77,0,0,0,53,50,50,48,51,57,51,53,53,48,57,57,44,48,49,
48,49,48,48,48,48,48,48,48,56,...>>
Then usual way to parse this form of messages is:
4> <<L:32/little,Rest/binary>> = Bin.
<<77,0,0,0,53,50,50,48,51,57,51,53,53,48,57,57,44,48,49,
48,49,48,48,48,48,48,48,48,56,...>>
5> L.
77
6> <<Msg:L/binary,R/binary>> = Rest.
<<"522039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,0110,00,150,0,0,0"...>>
7> R.
<<0>>
8> Msg.
<<"522039355099,010100000008,0,010170000000,0,0,0,0,0,0,,,0,0,,0110,00,150,0,0,0">>
You have to call list_to_binary/1 on string to be matched.
If you have further problems, type example string please!

Resources