How can I integrate Webmachine into an Erlang Application? - erlang

I read and re-read the docs and tutorials, but my understanding of how to create Erlang Applications, and Rebar for that matter, still has enough holes to resemble Swiss cheese. Very simple stuff throws me.
I'm working toward an Erlang Release that will eventually include several applications of my own plus Webmachine and maybe a nosql db of one flavor or another. Using Rebar I've successfully compiled and tested my applications: ZZZ and ZZZ_Lib. my directory structure is shown below. I'm not confident that it's optimal, but it works.
I've installed Webmachine under the ...learn1/apps directory.
My next step has been to integrate Webmachine with the very simple webmachine_demo_resource shown below under the name test_resource:erl.
http://webmachine.basho.com/example_resources.html
But when I try to compile, I get this error message:
src/test_resource.erl:3: can't find include lib "webmachine/include/webmachine.hrl"
Here's the offending line in test_resource.erl:
-include_lib("webmachine/include/webmachine.hrl").
I've tried to set both ERL_LIBS (which I don't fully understand) and PATH with no success. So, clearly, I don't understand how to set the proper path or how best to integrate Webmachine.
Any and all guidance would be gratefully welcomed.
LRP
* Directory structure
learn1$ ls
apps rebar rebar.config
learn1/apps$ ls
webmachine zzz zzz_lib
learn1/apps/zzz_lib/src$ ls
yada yada test_resource.erl yada yada
* rebar.config
{sub_dirs,
["apps/zzz",
"apps/zzz/src",
"apps/zzz_lib",
"apps/zzz_lib/src"
]
}.
* zzz_lib.app.src
{application, zzz_lib,
[
{description, ""},
{vsn, "1"},
{modules, [
yada yada
]},
{applications, [
kernel,
stdlib,
webmachine
]},
{mod, { zzz_lib_app, []}},
{env, []}
]}.

You most likely will end up happier including it as a dependency, not as a contained app. See for example how Riak Core does it: https://github.com/basho/riak_core/blob/master/rebar.config
For more insight, you might find asking the mailing lists to be worthwhile:
http://lists.therestfulway.com/mailman/listinfo/webmachine_lists.therestfulway.com
http://lists.basho.com/mailman/listinfo/rebar_lists.basho.com

Using ERL_LIBS in your case, you'd need to set it to /.../learn1/apps.
When compiling, you may also add a {i, Dir} option.
According to the documentation however, it only mentions -include and -include_dir, not -include_lib.
{i,Dir} Add Dir to the list of directories to be searched when
including a file. When encountering an -include or -include_dir
directive, the compiler searches for header files in the following
directories:
".", the current working directory of the file server;
the base name of the compiled file;
the directories specified using the i option. The directory specified
last is searched first.

Related

Erlang rebar3 builds

I am building a server based on 'cowboy', using 'jiffy' for json processing. The problem I am running into is that after starting the shell by executing the binary generated by rebar3, only 'cowboy' is functioning as expected, but I have no access to mnesia, which is part of OTP. Also running rebar3 using the .config file produces this output:
>There are missing function calls in the release.
===> Make sure all applications needed at runtime are included in the release.
===> db_access:init_tables/0 calls undefined function **mnesia:create_table/2**
===> db_access:insert_apod_entries/1 calls undefined function **mnesia:transaction/1**
===> db_access:insert_apod_entries/1 calls undefined function **mnesia:write/1**
If I start Erlang by issuing the command 'erl' then mnesia is availabe.
I've include the rebar.config I'm using. Any help with this issue would be greatly appreciated.
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
%% escript_incl_extra is for internal rebar-private use only.
%% Do not use outside rebar. Config interface is not stable.
{require_erts_vsn, ".*"}.
{require_otp_vsn, ".*"}.
{require_min_otp_vsn, ".*"}.
{escript_incl_extra, [{"priv/templates/*", "."}]}.
{include_src, false}.
{deps, [
{jiffy, "1.1.1", "deps/"},
{cowlib,"2.11.0", "deps/"},
{cowboy, "2.9.0", "deps/"}
]}.
{erl_first_files, ["src/db_access.erl",
"src/tar_watch_handler.erl",
"star_watch_server_app.erl",
"star_watch_server_sup.erl"
]}.
% {artifacts, ["/home/oleg/Projects/erlang_practice/star_watch_server/deps/jiffy/priv/jiffy.so"]}.
{plugins, [
{ pc, {git, "git#github.com:blt/port_compiler.git", {branch, "master"}}}
]}.
{overrides,
[{override, jiffy, [
{plugins, [pc]},
{artifacts, ["priv/jiffy.so"]},
{provider_hooks, [
{post,
[
{compile, {pc, compile}},
{clean, {pc, clean}}
]
}]
}
]}
]}.
{base_dir, "_build"}.
%% directory in '<base_dir>/<profile>/' where deps go
{deps_dir, "lib"}.
%% where rebar3 operates from; defaults to the current working directory
{root_dir, "."}.
%% where checkout dependencies are to be located
{checkouts_dir, "_checkouts"}.
%% where, under <base_dir>/<profile> checkout dependencies are to be built
{checkouts_out_dir, "checkouts"}.
%% directory in '<base_dir>/<profile>/' where plugins go
{plugins_dir, "plugins"}.
%% directories where OTP applications for the project can be located
{project_app_dirs, ["apps/*", "lib/*", "."]}.
%% Directories where source files for an OTP application can be found
{src_dirs, ["src"]}.
%% Paths to miscellaneous Erlang files to compile for an app
%% without including them in its modules list
{extra_src_dirs, []}.
%% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17.
%% At the same time, their counterparts dict() and digraph() are to be
%% deprecated in Erlang 18. namespaced_types option is used to select proper
%% type name depending on the OTP version used.
{erl_opts,
[
{platform_define, "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'},
{platform_define, "(linux|freebsd)", 'BACKLOG', 128},
{platform_define, "R13", 'old_inets'},
{src_dirs, ["src"]}
]}.
{minimum_otp_vsn, "25.0.4"}.
{application_resource_extensions, [
".app.src.script", ".app.src"
]}.
{cover_enabled, true}.
{validate_app_modules, true}.
{base_dir, "_build"}.
%% directory in '<base_dir>/<profile>/' where deps go
{deps_dir, "lib"}.
%% where rebar3 operates from; defaults to the current working directory
{root_dir, "."}.
%% where checkout dependencies are to be located
{checkouts_dir, "_checkouts"}.
%% where, under <base_dir>/<profile> checkout dependencies are to be built
{checkouts_out_dir, "checkouts"}.
{xref_checks, []}.
{xref_queries,
[{"(XC - UC) || (XU - X - B
- (\"escript\":\"foldl\"/\"3\")
- (\"eunit_test\":\"function_wrapper\"/\"2\")
- (\"eflame\":\"apply\"/\"5\")
- (\"abnfc\":\"file\"/\"2\")
- (\"erlydtl\":\"compile\"/\"3\")
- (\"lfe_comp\":\"file\"/\"2\")
- (\"neotoma\":\"file\"/\"2\")
- (\"protobuffs_compile\":\"scan_file\"/\"2\")
- (\"gpb_compile\":\"file\"/\"2\")
- (\"gpb_compile\":\"format_error\"/\"1\")
- (\"diameter_codegen\":\"from_dict\"/\"4\")
- (\"diameter_dict_util\":\"format_error\"/\"1\")
- (\"diameter_dict_util\":\"parse\"/\"2\")
- (\"erlang\":\"timestamp\"/\"0\")
- (\"rebar_rnd\":\"seed\"/\"1\")
- (\"rebar_rnd\":\"uniform\"/\"0\"))",
[]}]}.
{dialyzer,
[
{plt_extra_apps, [diameter]},
{warnings,
[
unmatched_returns,
error_handling,
race_conditions
]}
]}.
You will need to add mnesia to your app's app.src file.
From http://rebar3.org/docs/workflow/#setting-up-dependencies
If the dependency is needed at runtime by your application in order for it to work (e.g. you need a web server or call the library directly), add it to your application’s .app.src file under the {applications, [stdlib, kernel, ...]} tuple.
The tool that generates the release (relx) also uses the same definition to figure out which applications to include in the release.

What is the "application configuration file" in a rebar3 app?

The inets httpd server docs say,
The following is to be put in the Erlang node application
configuration file to start an HTTP server at application startup:
[{inets, [{services, [{httpd, [{proplist_file,
"/var/tmp/server_root/conf/8888_props.conf"}]},
{httpd, [{proplist_file,
"/var/tmp/server_root/conf/8080_props.conf"}]}]}]}].
Where does that go in an app created by rebar3?
The OTP Application docs say,
7.8 Configuring an Application
An application can be configured using configuration parameters. These
are a list of {Par,Val} tuples specified by a key env in the .app
file:
{application, ch_app,
[{description, "Channel allocator"},
{vsn, "1"},
{modules, [ch_app, ch_sup, ch3]},
{registered, [ch3]},
{applications, [kernel, stdlib, sasl]},
{mod, {ch_app,[]}},
{env, [{file, "/usr/local/log"}]}
]}.
Par is to be an atom. Val is any term.
That seems to suggest that you create environment variables with {Name, Value} tuples. However, the required code specified in the httpd server docs does not seem to be in that format.
Just drop this into the sys.config file which is in config folder of your release. If you have anything there already, it will be in the format of:
[
{some_app, [{env_var, value},{...}]},
{another_app, [{env_var, value},{...}]},
% add here without outer[]...,
{kernel,
[{distributed, [{app_name, 5000,
['node#10.0.211.153', 'node_failover#10.8.222.15']}]},
{sync_nodes_mandatory, []},
{sync_nodes_optional, ['node_failover#10.8.222.15']},
{sync_nodes_timeout, 5000}]}
]

Erlang; OTP application "app.config"

I have an OTP application. So far, I have two configuration files: rebar.config and config/vm.args, the latter of which is referenced in the former: {vm_args, "config/vm.args"}.
In the lager documentation: https://github.com/erlang-lager/lager
There is mention of another configuration file: app.config. Where does this file go and how do I reference it from my rebar config? In /src or at the root of the application? I ask because I tried adding that lager section in my rebar.config and that didn't do anything: lager is still operating with the defaults. So I probably need this app.config.
OK figured this out. In Rebar you can specify sys.config: https://www.rebar3.org/docs/releases
So I have a new file config/sys.config and my relx section now reads:
{relx, [
{release,
{myapp,"3.4.1"},
[myapp]
},
%{extend_start_script,true},
%
%for the following two fancyiness see https://www.rebar3.org/docs/releases
%Supply our own vm.args
{vm_args, "config/vm.args"},
%supply our own application configuration
{sys_config, "config/sys.config"}
]}.

Rebar fails to compile application with badarg on is_app_available

My erlang application named "myapp1" builds fine using rebar as far as I do not include another erlang application "my_other_app" under its dependencies (deps). Once I add it to my dependencies, and specify it under rebar.config, its build fails in error as such:
> rebar compile
Uncaught error in rebar_core: {'EXIT',
{badarg,
[{re,run,[0.9,".*",[{capture,none}]],[]},
{rebar_deps,is_app_available,5,
[{file,"src/rebar_deps.erl"},{line,418}]},
{rebar_deps,find_dep_in_dir,3,
[{file,"src/rebar_deps.erl"},{line,380}]},
{rebar_deps,find_dep,3,
[{file,"src/rebar_deps.erl"},{line,362}]},
{rebar_deps,find_deps,4,
[{file,"src/rebar_deps.erl"},{line,345}]},
{rebar_deps,preprocess,2,
[{file,"src/rebar_deps.erl"},{line,63}]},
{rebar_core,acc_modules,5,
[{file,"src/rebar_core.erl"},{line,492}]},
{rebar_core,process_dir1,6,
[{file,"src/rebar_core.erl"},{line,195}]}]}}
The rebar.config is
%%-*- mode: erlang -*-
{deps, [
{webmachine, "1.10.*", {git, "git://github.com/basho/webmachine", "HEAD"}},
amqp_client,
rabbit_common,
my_other_app
]
}.
The tree structure is as such:
myapp1
-src/...
-ebin/...
-rebar.config
-deps/
-webmachine
-mochiweb
-my_other_app
-src/...
-ebin/...
-include/...
As far as I remove the my_other_app from the rebar.config, everything works (except that the application does not function given the fact I am excluding a required depdency, but at least it compiles after I comment the code that uses that dependency.
I am trying to understand what the "is_app_available" function does in rebar, as my my_other_app is indeed an OTP application, but it is not started at the time I compile with rebar (it will be started by the myapp1 itself once some services start.
It looks like the version for one of your applications might be an atom, 0.9, rather than the string, "0.9". The re module requires an iodata() for that argument. Try finding out which application that is and changing the atom to a string.
Found the issue...
The dependency application had a Vsn (version) to its my_other_app.app.src file that was not a string ( numeric value instead ). This caused rebar to error. Certainly rebar could have a more friendly capture/error message...
Therefore, make sure to verify the {vsn, "1.1"} tag, so it has a string rather than decimal/numeric.
{application, my_other_app, [
{description, "my other appapplication"},
{vsn, "1.1"},
{applications, [kernel, stdlib]},
{modules, [.........etc.etc.etc......]},
{registered, [my_other_app]},
{mod,{ my_other_app, {true} } }
]}.

Erlang: when to perform `inets:start()`?

What is the appropriate place for performing inets:start() ?
in `applicationname_app' module?
in applicationname_sup supervisor module?
in a child process hanging from the supervisor?\
someplace else?
(I am still struggling with inets:httpd)
Note: the answer cannot be to the tune " use a boot file " , please.
inets is a "stand-alone" Erlang application; inets:start() is just an alias to application:start(inets). I guess the answer pretty much depends on how you maintain your code.
If your code is packaged as an application, your .app file should list inets as required to be started before yours (see the applications tag). Starting your applicaion with application:start(my_app). will now ensure that inets is also started. Consequence: if you make a boot file, it will also start inets for you :-P
If you are keen on not using applications, I guess the choice depends on how your code works. If you will always need inets to be started, it is better started by any of your supervisors. If it is rarely needed, you can always make sure it is started with something like:
ensure_app_started(App) ->
case application:started(App) of
ok -> ok;
{error, already_started} -> ok;
Error -> Error
end.
In 2019, we use rebar3 to create an application and manage its dependencies. For dependencies that need to be downloaded, you add them to rebar.config, and rebar3 will download the dependencies. For example, if you add hackney (an http client) to rebar.config:
{erl_opts, [debug_info]}.
{deps, [
{hackney, ".*", {git, "git://github.com/benoitc/hackney.git", {branch, "master"}}}
]}.
{shell, [
% {config, "config/sys.config"},
{apps, [http_client]}
]}.
Then do:
../your_app_name$ rebar3 compile
rebar3 will download hackney and compile all the files in the application.
To make sure that all your dependencies get started before your app, you add the names of the dependencies to:
src/your_app_name.app.src
For instance,
{application, http_client,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {http_client_app, []}},
{applications,
[kernel,
stdlib,
hackney %%%<=========HERE
]},
{env,[]},
{modules, []},
{licenses, ["Apache 2.0"]},
{links, []}
]}.
The actual .app file gets created here:
_build/default/lib/your_app_name/ebin/your_app_name.app
To start your app in the shell along with all its dependencies, you do:
../your_app_name$ rebar3 shell
The inets application comes with erlang, so it doesn't need to be downloaded, so you don't specify inets as a dependency in rebar.config (you'll get an error when you $ rebar3 compile). You still need to specify inets as a dependency in your application in the file:
src/your_app_name.app.src
But rebar3 itself uses inets (to download your dependencies), so even if you didn't specify inets as a dependency in your application, inets would still get started before your app. You can test that by not specifying inets as a dependency in your application, then doing:
$ rebar3 shell
...
...
1> application:start(inets)
{error,{already_started,inets}}
But, don't rely on that and DO specify inets as a dependency in your application.
If your code is packaged as an application, list inets in the application resource file:
% Filename: ebin/flamingo.app
{application, flamingo,
[{vsn, "1.0.0"},
{modules, [flamingo_app,
flamingo_sup,
flamingo]},
{applications, [kernel,
stdlib,
inets]},
{mod, {flamingo_app, []}}
]}.
Then you can start the application using application:ensure_all_started(flamingo). This ensures that inets is started automatically for you (i.e. there is no need to explicitly call inets:start()).
For example (assuming the *.app file and *.beam files and are in ebin/):
$ erl -pa ebin/
Eshell V9.2 (abort with ^G)
1> application:ensure_all_started(flamingo).
{ok,[inets,flamingo]}

Resources