Sublime Text Sorting with Respect to Indentation - sublimetext2

I'm looking for a Sublime Text plugin or any kind of program that can sort alphabetically but respect the indentation.
For example,
beatsByUserPath: (userId) ->
"/api/beats/by_user/#{userId}"
sendMassMessagePath: ->
"/api/send_mass_message"
sendMessagePath: (userId) ->
"/api/send_message/#{userId}"
feedbackCreatePath: ->
"/api/feedbacks"
Would be sorted by the function names.
Using the default sort in Sublime Text leads to:
"/api/beats/by_user/#{userId}"
"/api/feedbacks"
"/api/send_mass_message"
"/api/send_message/#{userId}"
beatsByUserPath: (userId) ->
feedbackCreatePath: ->
sendMassMessagePath: ->
sendMessagePath: (userId) ->
Here is the full file that I would like to sort.
RouteHelper =
EXTERNAL:
soundcloudConvertPath: (url) ->
url = encodeURIComponent(url)
"http://streampocket.com/?stream=#{url}"
youtubeConvertPath: (url) ->
url = encodeURIComponent(url)
"http://www.video2mp3.net/loading.php?url=#{url}"
UTIL:
imageProxy: (url) ->
url = encodeURIComponent(url)
"/image-proxy/#{url}"
API:
beatsReportPath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/report"
beatsTrackDownloadPath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/track_download"
beatSetDownloadPath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/set_download_url"
beatsToggleVisibilityPath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/toggle_visibility"
beatsToggleRecordingPath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/toggle_recording"
beatsDisownPath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/disown"
beatsEditNotePath: (param) ->
beatId = param
beatId = param.id if typeof param is 'object' && param.id
"/api/beats/#{beatId}/edit_note"
beatsByUserPath: (userId) ->
"/api/beats/by_user/#{userId}"
discussPath: ->
"/api/discuss"
sendMassMessagePath: ->
"/api/send_mass_message"
sendMessagePath: (userId) ->
"/api/send_message/#{userId}"
feedbackCreatePath: ->
"/api/feedbacks"
feedbacksForRapPath: (arg) ->
rapId = if typeof rap is 'object' then arg.id else arg
"/api/feedbacks/feedback_for/#{rapId}"
followersPath: (userId) ->
"/api/followers/#{userId}"
followingPath: (userId) ->
"/api/following/#{userId}"
followPath: (userId) ->
"/api/follow/#{userId}"
unfollowPath: (userId) ->
"/api/unfollow/#{userId}"
propsPath: ->
"/api/props"
userBattlesPath_deprecated: (userId) ->
"/api/battles/for_user/#{userId}"
battlesLeaderboardPath: ->
"/api/battles/leaderboard"
battlesUsersWhoVotedForPath: (opts) ->
throw Error('RouteHelper: Expected ID and WHICH') if !opts.id || !opts.which
"/api/battles/#{opts.id}/users_who_voted_for/#{opts.which}"
rapProppersPath: (rapId) ->
"/api/raps/#{rapId}/proppers"
rapUntagPath: (rapId) ->
"/api/raps/#{rapId}/untag"
rapShowPath: (param) ->
if typeof param is 'object'
rapId = param.id
else rapId = param
"/api/raps/#{rapId}/show_v2"
userPinPath: ->
"/api/users/pin"
userBattlesPath: (userId) ->
"/api/users/#{userId}/battles"
userBeatsPath: (userId) ->
"/api/users/#{userId}/beats"
userRapsPath: (userId) ->
"/api/users/#{userId}/raps_v2"
userSetColorsPath: (userId) ->
"/api/users/#{userId}/set_colors"
userShowPath: (userId) ->
"/api/users/#{userId}"
usersWhoGaveProps: (userId) ->
"/api/users/#{userId}/users_who_gave_props"
userUnreadNotifCount: (userId) ->
"/api/users/#{userId}/unread_notif_count"
userRecordNetegoPath: ->
"/api/users/record_net_ego"
albumShowPath: (param) ->
param = param.slug if _.isObject(param)
"/albums/#{param}"
blueprintShowPathFromRap: (rap) ->
"/blueprints/#{rap.blueprint_id}"
battleDestroyPath: (battle) ->
"/battles/#{battle.id}"
battlesPath: ->
"/battles"
battleNewPath: ->
"/battles/new"
battleShowPath: (battle) ->
"/battles/#{battle.id}"
beatNewPath: ->
"/beats/new"
beatShowPath: (beat) ->
if typeof beat is 'number'
"/beats/#{beat}"
else if typeof beat is 'object'
if beat.slug
"/beats/#{beat.slug}"
else
"/beats/#{beat.id}"
beatTagShowPath: (beatTag) ->
"#{beatTag.slug}/instrumentals"
beatsSearchQueryPath: ->
"/beats/search_query"
beatsRecentSearchesPath: ->
"/beats/recent_searches"
cypherJudgeVotePath: ->
"/cyphers/judge-vote"
cypherJudgeShowPath: ->
"/cyphers/judge-show"
cypherSubmitPath: ->
"/cyphers/submit"
dashboardPath: ->
"/dashboard"
defaultRapThumbnailPath: ->
"/images/default_rap.png"
rhymePath: ->
"/rhyme"
contextPath: ->
"/context"
searchLyricsPath: ->
"/rap/search_lyrics"
editorSavePath: ->
"/editor/save"
editorPath: (param) ->
param = param.id if typeof param is 'object'
if param
"/editor/#{param}"
else
"/editor"
onRapSaveDialogPath: (rapId) ->
"/rap/#{rapId}/on_save_dialog"
lyricSyncSavePath: ->
"/lyric-sync/save"
lyricSyncDestroyPath: ->
"/lyric-sync/destroy"
notificationsPath: ->
"/notifications"
rapEditPath: (rap) ->
"/editor/#{rap.id}"
rapShowPath: (rap) ->
"/rap/#{rap.id}"
rapsForCypher: (cypherId) ->
"/cyphers/#{cypherId}/submissions"
isSubscribedPath: (listId) ->
"/is_subscribed/#{listId}"
subscribeToPath: (listId) ->
"/subscribe_to/#{listId}"
userShowPath: (username) ->
"/users/#{username}"
userNotificationSettingPath: ->
"/users/notification_setting"
#RouteHelper = RouteHelper

After seeing the updated question with an example of a full file, I went ahead and created a Sublime Text plugin with some basic options to control over sorting.
Install IndentRespectfulSort from Package Control (See https://packagecontrol.io/packages/Indent%20Respectful%20Sort for instructions).
Open the console by Ctrl + ` or Cmd(⌘) + `(OSX).
Assuming your file is indented by 2 spaces as in the question and you would like to sort function names only, type
view.run_command("indent_respectful_sort", {"indent": " ", "onlyDepth" : 3})
into the console and hit Enter. This will sort only the function names while respecting the rest of the structure.
If you would like to sort with different options, you may refer to the plugin page https://github.com/mvnural/sublime-indent-respectful-sort to see more options.

I'm not aware of a generic solution since the definition of a block (for sorting purposes) is highly dependent on the context.
For the specific case you have posted however, it can be easily accomplished by a simple macro with the following steps:
Merge each block into a single line (replace each occurrence of ->\n with ->)
Sort single lines
Unmerge each block from a single line back to original indented form (replace each occurrence of -> with ->\n)
The only problem is, Sublime doesn't record find & replace commands in a macro. Instead, we need to install RegReplace package which provides find & replace commands that can be recorded in a macro. (See http://facelessuser.github.io/RegReplace/installation/ for installation instructions)
Once RegReplace is installed, just put the following two files under Packages/User/ in your data directory (What is the full path to the Packages folder for Sublime text 2 on Mac OS Lion)
You can now run smart_sort by selecting Tools -> Macros -> smart_sort from the top menu.
Disclaimer: If you have more than 1 indented line per function, you need to adjust the regex definitions accordingly.
smart_sort.sublime-macro (the macro)
[
{
"args":
{
"replacements":
[
"merge_block_into_a_line"
]
},
"command": "reg_replace"
},
{
"args":
{
"case_sensitive": false
},
"command": "sort_lines"
},
{
"args":
{
"replacements":
[
"restore_block"
]
},
"command": "reg_replace"
}
]
reg_replace.sublime-settings (reg_replace definitions)
{
"replacements": {
"merge_block_into_a_line": {
"find": "->\\n",
"replace": "->",
"greedy": true,
"case": true
},
"restore_block": {
"find": "->",
"replace": "->\\n",
"greedy": true,
"case": true
}
}
}

Related

ejabberd add_channel API for MIX

Currently, there is no api for creating a MIX channel.
I'm written a custom module for the same.
So far, I have written the following code. But I'm not how to proceed further.
I would really appreciate someone's guidance here. Thanks in advance.
-module(mod_custom).
-behaviour(gen_mod).
-include("logger.hrl").
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2]).
-export([
% Create channel
add_channel/4
]).
-include("ejabberd_commands.hrl").
-include("ejabberd_sm.hrl").
-include("xmpp.hrl").
start(_Host, _Opts) ->
ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_commands:unregister_commands(get_commands_spec());
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[].
get_commands_spec() ->
[
#ejabberd_commands{name = add_channel, tags = [group],
desc = "Create a WhatsApp like group",
module = ?MODULE, function = add_channel,
args = [{jid, binary}, {channel, binary}, {id, binary}],
args_example = [<<"admin#localhost">>, <<"testgroup123#localhost">>, <<"abc123456">>],
args_desc = ["Admin JID", "Channel JID", "Unique ID"],
result = {res, rescode}}
].
add_channel(JID, Channel, ID) ->
%%% Create channel code goes here...
ok.
mod_options(_) -> [].
Try something like this:
-module(mod_custom).
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2]).
-export([create_channel/3]).
-include("logger.hrl").
-include("ejabberd_commands.hrl").
-include("ejabberd_sm.hrl").
-include_lib("xmpp/include/xmpp.hrl").
start(_Host, _Opts) ->
ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_commands:unregister_commands(get_commands_spec());
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[].
get_commands_spec() ->
[#ejabberd_commands{name = create_channel, tags = [group],
desc = "Create a WhatsApp like group",
module = ?MODULE, function = create_channel,
args = [{from, binary},
{channel, binary},
{service, binary}],
args_example = [<<"admin#localhost">>,
<<"testgroup123">>,
<<"mix.localhost">>],
args_desc = ["From JID", "Channel Name", "MIX Service"],
result = {res, rescode}}
].
create_channel(From, ChannelName, Service) ->
try xmpp:decode(
#xmlel{name = <<"iq">>,
attrs = [{<<"to">>, Service},
{<<"from">>, From},
{<<"type">>, <<"set">>},
{<<"id">>, p1_rand:get_string()}],
children =
[#xmlel{name = <<"create">>,
attrs = [{<<"channel">>, ChannelName},
{<<"xmlns">>, ?NS_MIX_CORE_0}]}
]},
?NS_CLIENT, []) of
#iq{type = set} = Iq ->
case mod_mix:process_mix_core(Iq) of
#iq{type = result} ->
ok;
_ ->
{error, unexpected_response}
end
catch _:{xmpp_codec, Why} ->
{error, xmpp:format_error(Why)}
end.
mod_options(_) -> [].

Playframework [Scala]: Binding a list/seq to the controller

I am trying to bind a list of input items on my html page to my controller.
My form is defined as:
def clientForm = Form( tuple(
"clients[]" -> seq( tuple(
"firstname" -> text,
"lastname" -> text) )
) )
In my HTML I have tried the following:
#b3.text(thisForm("clients[0]"), '_label -> "first client", 'value -> "('John','Snow')")
#b3.text(thisForm("clients[1]"), '_label -> "second client", 'value -> "('Frank','Carson')")
I have also tried:
#b3.text(thisForm("clients[0].firstname"), '_label -> "first client fistname", 'value -> "John")
#b3.text(thisForm("clients[0].lastname"), '_label -> "first client lastname", 'value -> "Snow")
#b3.text(thisForm("clients[1].firstname"), '_label -> "second client fistname", 'value -> "Frank")
#b3.text(thisForm("clients[2].lastname"), '_label -> "second client lastname", 'value -> "Carson")
In debug mode upon POST I can see that these values get bound to the form within the controller:
val boundFrom = inForm.bindFromRequest
But when I boundForm.fold mapping to clientForm the values to not map correctly to my "clients[]" element.
I'm at a loss and have spent ages looking for the answer to no avail.
Any help greatly appreciated
EDIT:
Here's a screenshot in debug mode. The values are being bound to the form but then do not get assigned.
Debug screenshot
Please note that your form definition can be as following -
def clientForm = Form(
single(
"clients[]" -> seq(
tuple(
"firstname" -> text,
"lastname" -> text
)
)
)
)
Then you second way of writing template is correct as you can see in documentation here https://www.playframework.com/documentation/2.6.x/ScalaForms#Repeated-values
Now what you will get when you fold this form is that a seq of tuples and you can use it like following -
clientForm.bindFromRequest.fold(
error => // Handle error
clients => {// this clients is of signature Seq[(String, String)]
clients.map{client => logger.log("First name: " + client._1 + ", Last name: " + client_2)}
}
)
Please note that you have to use tuple ._1 and ._2 to access your first name and last name here. As you are getting a tuple (String, String) not a Map[String, String] here.

Elixir: find by value prefix in nested JSON

I'm trying to find URLs in a nested JSON response and map them. My function so far looks like this:
def list(env, id) do
Service.get_document(env, id)
|> Poison.decode!
|> Enum.find(fn {_key, val} -> String.starts_with?(val, 'https') end)
end
The JSON looks roughly like this:
"stacks": [
{
"boxes": [
{
"content": "https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
"box": "photo"
}
]
}
],
"logo": "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"
So URLs can have any key, and be at any level.
With that code I get this error:
no function clause matching in String.starts_with?/2
Anyone got a better way to find in JSON responses?
You'll have to use recursive function for this, which handles three types of data:
For map, it recurses over all its values.
For list, it recurses over all its elements.
For string, it selects strings that start with "https"
Here's a simple implementation which accepts a term and a string to check with starts_with?:
defmodule A do
def recursive_starts_with(thing, start, acc \\ [])
def recursive_starts_with(binary, start, acc) when is_binary(binary) do
if String.starts_with?(binary, start) do
[binary | acc]
else
acc
end
end
def recursive_starts_with(map, start, acc) when is_map(map) do
Enum.reduce(map, acc, fn {_, v}, acc -> A.recursive_starts_with(v, start, acc) end)
end
def recursive_starts_with(list, start, acc) when is_list(list) do
Enum.reduce(list, acc, fn v, acc -> A.recursive_starts_with(v, start, acc) end)
end
end
data = %{
"stacks" => [
%{
"boxes" => [
%{
"content" => "https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
"box" => "photo"
}
]
}
],
"logo" => "https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"
}
data |> A.recursive_starts_with("https") |> IO.inspect
Output:
["https://ddd.cloudfront.net/photos/uploaded_images/000/001/610/original/1449447147677.jpg?1505956120",
"https://ddd.cloudfront.net/users/cmyk_banners/000/000/002/original/banner_CMYK.jpg?1397201875"]

XML Parsing - Erlang

I want to parse XML strings to erlang list and then to JSON.
Example Input :
<?xml version="1.0" encoding="UTF-8"?>
<!--some message here-->
<start>
<data>
<number id="333">test message</number>
<data>current date</data>
</data>
<mass>
<client>35</client>
<address>lattitude</address>
<code>3454343</code>
<foo tipo="casa">Some text message 2</foo>
<product>TEST</product>
</mass>
</start>
Output should be:
{
"start": {
"data": {
"number": {
"#id": "333",
"#text": "test message"
},
"data": "current date"
},
"mass": {
"client": "35",
"address": "lattitude",
"code": "3454343",
"foo": {
"#tipo": "casa",
"#text": "Some text message 2"
},
"product": "TEST"
}
}
}
I am trying to use erlsom:simple_form(Xml).
and getting :
{ok,{"start",[],
[{"data",[],
[{"number",[{"id","333"}],["test message"]},
{"data",[],["current date"]}]},
{"mass",[],
[{"client",[],["35"]},
{"address",[],["lattitude"]},
{"code",[],["3454343"]},
{"foo",[{"tipo","casa"}],["Some text message 2"]},
{"product",[],["TEST"]}]}]},
[]}
Now I want to delete these empty attrs. Is there any simple way to do this?
thanks in advance.
UPDATE: Make it work w/ solution from
Erlang xml to tuples and lists
BUT Getting
{"start",
[{"data",
[{"number","test message"},{"data","current date"}]},
{"mass",
[{"client","35"},
{"address","lattitude"},
{"code","3454343"},
{"foo","Some text message 2"},
{"product","TEST"}]}]}
there is no [{"id","333"}] & [{"tipo","casa"}] lists :(
The output of your simple parsing is in a set format: {Node, Attributes, Children}, so you can write a simple parser that turns that structure you have into a nested proplist. With that, you can either use mochijson or jsx to turn that proplist into a JSON string.
-module(transform).
-export([test/0]).
test() -> parse(data()).
parse({Node, [], [Value]}) when is_list(Value) ->
[{Node, Value}];
parse({Node, [], Children}) ->
V = children_to_struct(Children, []),
[{Node, V}];
parse({Node, Attributes, Children}) ->
V = attributes_to_struct(Attributes, []) ++ children_to_struct(Children, []),
[{Node, V}].
children_to_struct([], Acc) -> Acc;
children_to_struct([Value], Acc) when is_list(Value) ->
Acc ++ [{"#text", Value}];
children_to_struct([Value | T], Acc) when is_tuple(Value) ->
children_to_struct(T, Acc ++ parse(Value)).
attributes_to_struct([], Acc) -> Acc;
attributes_to_struct([{K, V}|T], Acc) ->
attributes_to_struct(T, Acc ++ [{"#" ++ K, V}]).
data() ->
{"start",[],
[{"data",[],
[{"number",[{"id","333"}],["test message"]},
{"data",[],["current date"]}]},
{"mass",[],
[{"client",[],["35"]},
{"address",[],["lattitude"]},
{"code",[],["3454343"]},
{"foo",[{"tipo","casa"}],["Some text message 2"]},
{"product",[],["TEST"]}]}]}.
Running it in the shell with mochijson:
Eshell V7.3 (abort with ^G)
1> c(transform).
{ok,transform}
2> T = transform:test().
[{"start",
[{"data",
[{"number",[{"#id","333"},{"#text","test message"}]},
{"data","current date"}]},
{"mass",
[{"client","35"},
{"address","lattitude"},
{"code","3454343"},
{"foo",[{"#tipo","casa"},{"#text","Some text message 2"}]},
{"product","TEST"}]}]}]
3>
4> iolist_to_binary(mochijson2:encode(T)).
<<"{\"start\":{\"data\":{\"number\":{\"#id\":[51,51,51],\"#text\":[116,101,115,116,32,109,101,115,115,97,103,101]},\"data\":{\"#text"...>>
i suggest to use jiffy for JSON and exml for XML.
jiffy and exml have native code which means they are so fast.
Clone and compile them.
before compiling them, you should install g++ and libexpat-dev
Example:
-module(test).
-export([convert/1]).
-include("exml/include/exml.hrl"). %% In my test
convert(XML) when erlang:is_binary(XML) ->
{ok, XMLEl} = exml:parse(XML),
jiffy:encode({[convert2(XMLEl)]}).
convert2(#xmlel{name = Name
,attrs = []
,children = [{xmlcdata, Data}]}) ->
{Name, Data};
convert2(#xmlel{name = Name
,attrs = Attrs
,children = Children}) ->
{Name, {convert_attrs(Attrs) ++ convert_children(Children)}}.
convert_attrs(Attrs) ->
convert_attrs(Attrs,[]).
convert_attrs([Attr|Attrs1], Attrs2) ->
convert_attrs(Attrs1, [convert_attr(Attr)|Attrs2]);
convert_attrs([], Attrs2) ->
lists:reverse(Attrs2).
convert_attr({Attr, Value}) ->
{<<$#, Attr/binary>>, Value}.
convert_children(Children) ->
convert_children(Children, []).
convert_children([Child|Children1], Children2) ->
convert_children(Children1, [convert_child(Child)|Children2]);
convert_children([], Children2) ->
lists:reverse(Children2).
convert_child({xmlcdata, Data}) ->
{<<"#text">>, Data};
convert_child(#xmlel{}=XMLEl) ->
convert2(XMLEl).
In the shell:
p#jahanbakhsh ~/Projects/test $ ls
exml jiffy test.erl
p#jahanbakhsh ~/Projects/test $ erl -pa jiffy/ebin exml/ebin
Erlang/OTP 19 [erts-8.2.2] [source-1ca84a4] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.2.2 (abort with ^G)
1> c(test).
{ok,test}
2> XML = <<"<start><data><number id=\"333\">test message</number><data>current date</data></data><mass><client>35</client><address>lattitude</address><code>3454343</code><foo tipo=\"casa\">Some text message 2</foo><product>TEST</product></mass></start>">>.
<<"<start><data><number id=\"333\">test message</number><data>current date</data></data><mass><client>35</client><address"...>>
3> test:convert(XML).
<<"{\"start\":{\"data\":{\"number\":{\"#id\":\"333\",\"#text\":\"test message\"},\"data\":\"current date\"},\"mass\":{\"client\":\"35\",\"addres"...>>
4> io:format("~s~n", [test:convert(XML)]).
{"start":{"data":{"number":{"#id":"333","#text":"test message"},"data":"current date"},"mass":{"client":"35","address":"lattitude","code":"3454343","foo":{"#tipo":"casa","#text":"Some text message 2"},"product":"TEST"}}}
ok
5>

parsing json return from http in erlang

I test with this code :
get_fee(Transaction,SourceNumber,Amount, Currency) ->
Url = lists:concat(["http://localhost/test.php","?transaction=", Transaction, "&saccount=", SourceNumber,Amount,"&currency=",Currency]),
inets:start(),
{Flag, Response} = http:request(get, {Url, []}, [], []),
case Flag of
ok ->
{ { _, ReturnCode, _ }, _, Body } = Response,
if ReturnCode =:= 200 ->
{ok,{_,[{_,Code},{_,Permission},{_,Payer},{_,Payee}]}} = json:decode_string(Body),
case Permission of true ->
if Code =:= 200 ->
{ok,{Code, Payer, Payee}};
Code =:= 204 ->
{nok,{Code, not_found}};
true ->
{nok,{Code, parameter_error}}
end;
false ->
{nok,{Code, parameter_error}}
end;
true->
{error, http_error}
end;
error ->
case Response of
nxdomain -> {error, dns_error};
_ -> {error, network_error}
end
end.
the response from the http is : {"code":200,"permission":true,"fee_payer":0,"fee_payee":19}
But now I like to do the same think but the return of http in this case for example is :
{"CIN":"08321224","Name":21}
so I have just CIN and Name in this case
I try to change the previous
get_fee(Num) ->
Url = lists:concat(["http://localhost/GTW/Operation.php","?ACCOUNT_NUM=", Num]),
inets:start(),
{Flag, Response} = http:request(get, {Url, []}, [], []),
case Flag of
ok ->
{ { _, ReturnCode, _ }, _, Body } = Response,
%% for debug
io:format("~p~n",[ReturnCode]),
if ReturnCode =:= "08321224" ->
{ok,{_,[{_,CIN},{_,Name}]}} = json:decode_string(Body),
case Name of 21 ->
io:format(CIN),
io:format(Name),
if CIN =:= "08321224"->
{ok,{CIN, Name}};
CIN =:= 204 ->
{nok,{CIN, not_found}};
true ->
{nok,{CIN, parameter_error}}
end;
false ->
{nok,{CIN, parameter_error}}
end;
true->
{error, http_error}
end;
error ->
case Response of
nxdomain -> {error, dns_error};
_ -> {error, network_error}
%% for debug
%%io:format("pass2~n ~p~n",[Response]),
end
end.
but it displys :
test:get_fee("0001").
200
{error,http_error}
So I'll nitpick on the style here, because you will be far better off if you follow the semantical idea of Erlang:
get_fee(Num) ->
Url = lists:concat(["http://localhost/GTW/Operation.php","?ACCOUNT_NUM=", Num]),
inets:start(),
This is the wrong place to start inets. It should be started outside this function as you only need to
do this one.
{Flag, Response} = http:request(get, {Url, []}, [], []),
This part is better coded with a pattern match. The discrimination of Flag and Response can
be decoded directly with a simple match. Write,
case http:request(get, {Url, []}, [], []) of
{ok, {{_, 200, _}, _, Body}} ->
{ok, R} = json:decode_string(Body),
get_fee_decode_(get_cin(R), get_name(R));
{error, Reason} -> {error, Reason}
end.
I would recommend against changing {error, nxdomain} to {error, dns_error} since nxdomain perfectly
codes this case in any case. Just pass the error tuple to the caller and have him handle it.
get_fee_decode_("08321224" = CIN, 21 = Name) -> {ok, {CIN, Name}};
get_fee_decode_("204" = CIN, 21) -> {nok, {CIN, not_found}};
get_fee_decode_(CIN, _Name) -> {nok, {CIN, parameter_error}};
Introduce a new function like this to handle the inner parts of your code base. And hoist the matching to
top-level. This helps in the long run by decoupling your code into functions.
Do note that in a JSON structure, there is no order on an "object" so you can't assume that the structure is
{"code":200,"permission":true,"fee_payer":0,"fee_payee":19}
But a decode does not have to preserve this structure, according to JSON. So a valid decode may be:
[{"fee_payee", 19}, {"fee_payer", 0}, {"permission", true}, {"code", 200}]
This will fail to match in your code and you are setting yourself up for some nasty errors later on.
You want something along the lines of:
get_fee_payer(PL) -> proplists:get_value("fee_payer", PL).
Another thing which will be a problem with your programming style is that you are hiding error-information cases. In Erlang, you can often get away with only handling the "happy path" through the code and leave all the error handling out until you know what kind of errors are there in the code base. Then you can begin adding in error handling slowly. Defensive programming is not a thing you should be doing if you can avoid it.
You changed:
if ReturnCode =:= 200 ->
to:
if ReturnCode =:= "08321224" ->
However, that needs to stay the same in your version. 200 is the HTTP status code for "OK" - the first step here is to verify that the server actually processed the request and returned a positive reply. You'll find that number only in Body - that is what the if CIN =:= "08321224"-> part is for.