Developing web apps using Erlang-Web

  • Published on
    16-Apr-2017

  • View
    5.142

  • Download
    3

Embed Size (px)

Transcript

<p>Server-side web developmentUsing ErlangWeb</p> <p>Stefan Comanescu</p> <p>A brief history of Erlang</p> <p>Initially developed by Joe Armstrong in 1986, as a proprietary language within Ericsson for usage in telephony applications</p> <p>Named after the Danish mathematician Agner Krarup Erlang</p> <p>First versions were running on a Prolog interpreter, a C emulator and a compiler were later written, for reasons concerning performance</p> <p>In 1997 bit syntax and binaries were added (for protocol programming) and in the same year work started on OTP</p> <p>Released as open source in 1998</p> <p>SMP support was added in 2006 </p> <p>Why Erlang ?</p> <p>ConcurrencyActor model (no threads, no locks, no shared memory, only asynchronous message passing)</p> <p>Fault ToleranceProcess linking, supervising trees, distribution and event managers help in keeping the system going when processes crash.</p> <p>DistributionAbstracts the difference between local and distributed message passing</p> <p>Runs on Yaws or Inets web server</p> <p>MVC (Model-View-Controller) paradigmModel data (stored in mnesia/dets tables) manipulation</p> <p>Templates XHTML files and templating engine</p> <p>Controller Erlang functions called on dispatching rules</p> <p>Dispatcher Request routing based on regexp </p> <p>Request dictionary </p> <p>Reusable components (ecomponent)</p> <p>Caching system</p> <p>Annotations</p> <p>Directory structure</p> <p>For creating the directory tree of a new application, ./bin/add is executed which generates the directories: </p> <p>config dispatch.conf, errors.conf, projects.conf, server specific .conf</p> <p>docroot static files and folders (except html templates), e.g : js, css, img</p> <p>lib framework applications and applications created by the user </p> <p>log logs created while running in embedded mode and server specific log</p> <p>pipes OS pipes for cummunicating with the shell while in embeded mode </p> <p>priv project specific data (usually static html)</p> <p>release separated subdirectories for each release version </p> <p>templates all the template filestemplates/cache cached files </p> <p>Generator script(bin/generate.erl)</p> <p>Module responsible for generating the boilerplate code </p> <p>General format: ./bin/generate.erl TYPE -argument_name -argument_value</p> <p>Generating a controller:./bin/generate.erl controller --app app_name --name name_of_the_generated_model --functions (fun1, fun2)</p> <p>./bin/generate.erl model --app app_name --name name_of_the_generated_model --hrl name_of_the_header_file Generates a file named wtype_name where 'name' is the given arg</p> <p>Generates basic CRUD functionality for the given header </p> <p>DispatcherDispatching types</p> <p>Static dispatching: serving static content, like xml pages, media or javascript. The controller is not accessed. The server first searches for the file in the templates dir, afterward in the docroot dir. For faster retrieval of static content, they should be placed in the docroot dir and enoent should be placed instead of File {static, Regexp, File} | {static, Regexp, enoent}</p> <p>Dynamic dispatching: the controller will handle the URL requests allowing specific content to be created or retrieved on demand{dynamic, Regexp, {Module, Function}} </p> <p>Alias dispatching: if the URL matches the Regexp, the rule of NewURL will be triggered{alias, Regexp, NewURL}</p> <p>Delegates: passing the URL to another dispatch config file, only helps in structuring the application{dynamic, delegate, Regexp, File}</p> <p>Dispatcher evaluation order</p> <p>Docroot content is served (files that point to 'enoent')</p> <p>Dynamic entries are matched with their URLs</p> <p>Static routes are matched</p> <p>If no patterns are matched, a 404 error page is displayed</p> <p>Request dictionary</p> <p>Temporary storage created for each HTTP request, lives as long as the request lives</p> <p>Provides an easy to use API for setting and getting values from the dictionary</p> <p>Used for retrieving GET/POST request data from the query string and also used by the expanding templates</p> <p>API:wpart:fget(post:user_address).</p> <p>wpart:fget(get:page_number).</p> <p>wpart:fset(user,[[{name,bogdan},{age,21}],[{name,marian},{age,18}]]).</p> <p>Request dictionarySample browser request</p> <p>{,[ {"get",[]}, {"post",[ {"action_type","add"},{"id",[]},{"type","user"},{"user_password","nuitizic"}, {"user_phone","07273447823"},{"user_cnp","1901231231723"}, {"user_lastname","alex"},{"user_firstname","popescu"} ] }, {"__https",false}, {"__cookies",[{"eptic_cookie","nonode@nohost-7985065742948673537"}]}, {"session",[{"user_id","1111111111111"}, {"groups",["admin"]}]}, {"__path","admin/add"}, {"__cookie_key","nonode@nohost-7985065742948673537"},{"__ip",{127,0,0,1}}, {"__controller",controller} ] }</p> <p>Controller</p> <p>Responsible for handling the user's requests</p> <p>Has the arity 1, getting a proplist (list of key-value pairs)</p> <p>Handles user input, model manipulation, etc.</p> <p>Must return a tuple, which tells the template what to do:{redirect, URL} | {content,html,Data} | {content,text,Data} | {json,Data} | {template,Template} | {error,Code} | {headers, Headers, RetVal} where : Headers is a list of headers, of the form {cookie, CookieName, CookieValue, CookiePath, CookieExpDate}</p> <p>Retval is one of the previous touples </p> <p>Templating Engine</p> <p>The view is represented by a regular XHTML file with a few namespaces defined. </p> <p>The most commonly used namespace, wpart, is always expanded by the framework . When the parser meets for example, it will call the function wpart_lookup:handle_call/1, with the whole xmerl parsed tag as an argument. </p> <p>Templating EngineWpart tags</p> <p>wpart:choose provides if functionality; it contains the tags: </p> <p> Something else </p> <p>The test is an expression of the form test=1 + 1 eq 2, where the operators can be:eq, neq, lt, le, gt, ge (for =:=, =/=, , ==)</p> <p>Templating EngineWpart tags</p> <p>wpart:list provides for functionality; it contain the attributes:select, specified what part of the list we want, attributes:map renders every element in the list</p> <p>head renders only the first element</p> <p>tail renders the last element of the list </p> <p>filter has an attribute named pred for filtering the list</p> <p>find the first element of the list where pred is evaled to true</p> <p>sort sorts according to the pred attribute</p> <p>as, every element of the list from inside the wpart:list will be known as the value of this attribute</p> <p>Templating EngineWpart tags</p> <p>list, specifies the list from the request dictionary that we want to traverse</p> <p>pred, predicate used for some of the select tags, it's optional</p> <p>e.g. : </p> <p>Templating EngineWpart tags</p> <p>wpart:lookup provides access to a value from inside the request dictionary, it contains the attributes:key, the key from the dictionary that we are looking for</p> <p>format, optional formatters</p> <p>type, can have the values text (default) or html, setting html turns off html-escaping and can be dangerous</p> <p>e.g : </p> <p>Templating Engine (wtpl)</p> <p>Tool for faster building of html pages, by expanding or replacing content from other template files</p> <p>Allows building of websites from small chunks of html</p> <p>It helps with :Code re-usage</p> <p>Not writing the same thing multiple times</p> <p>Updating the common part of the pages only one time</p> <p>Templating Engine (wtpl)Wtpl tags</p> <p> - the location where the wtpl:content with the corresponding name will be placed</p> <p> - specifies which template will be filled with the content provided by the wtpl:content content</p> <p> - content that will fill the wtpl:include tag which has the same name as the tag 'name' attribute, in the file specified by wtpl:parent</p> <p>Templating Engine wpart_gen</p> <p>Enables building of HTML code directly from within Erlang modules.</p> <p>wpart_gen:load_tpl(Namespace, Name, Path) loads the html snippet specified by Path, and saves it in the memory under the key {Namespace,Name}</p> <p>Tpl = wpart_gen:tpl_get(Namespace,Name) gets the snippet from memory </p> <p>wpart_gen:build_html(Tpl,Values) builds the template file with the values from the list Values</p> <p>Templating Engine wpart_gen Template Format</p> <p>Anonymous slots: </p> <p>Named slots: </p> <p>Templating Engine wpart_gen Passed Values</p> <p>Anonymous slots: [Value | ] All fields must be filled.</p> <p>Named slots: [{ slot_name, Value} | ] The unfilled slots will remain empty.</p> <p>Controlling cache</p> <p>persistent data cached this way is not removed until the server is restarted or the cache is invalidated</p> <p>timeout every T minutes the cache is traversed and expired content is removed, each URL can have a different timeout.</p> <p>no_cache </p> <p>Controlling cache </p> <p>The caching configuration is made directly from the dispatch configuration file. E.g. : {dynamic, ^/index/?$,{main,home},[{cache,persistent}]}.</p> <p>{static, ^/faq$,doc/faq.html,[{cache,normal}]}.</p> <p>{dynamic, ^/posts/?$,[{cache,{timeout,30}}]}.</p> <p>{dynamic, ^/the_time/?$,[{cache,no_cache}]}.</p> <p>Controlling cache </p> <p>For controlling cache based on aspects other than URL, the tuple {is_cachable_mod, my_mod} should be placed in the configuration(config/project.conf) file. </p> <p>Before processing any request, the function my_mod:is_cachable() will be called</p> <p>The function is_cachable() should return true if the request should be cached under the URL key</p> <p>{true,NewId} , if the request should be cached under NewId</p> <p>false , if we don't want to check the cache</p> <p>Annotations </p> <p>Available since version 1.3</p> <p>Allows separation of the target function from different types of functions, non-domain specific wrappers. </p> <p>e.g.: before making a post to a blog you should be logged in and your message should be checked for bad words maybe; after posting the message the cache should be invalidated and your post-count could be modified.</p> <p>Usual dataflow mechanisms can be used but it could make the code harder to read and repetitive. </p> <p>Annotations Controller Syntax</p> <p> % keeps the stored macros-include_lib(blog/include/utils_annotations.hrl). % before posting?AUTHENTICATE([administrator]).?CHECK(blog_post). % after posting?INVALIDATE(posts).?UPDATE(user).post(_Args) -&gt; ...</p> <p>Annotations Annotation Module Syntax</p> <p>?BEFORE. %% ?AFTER.annotation_name(AnnotationArg, TargetModule, TargetFunction, TargetFunctionArgs | TargetFunctionResult) -&gt; ResultResult :: {proceed, NewFunctionArgs | NewResult} | {skip, NewResult} | {error, {ErrorMod, ErrorFun, ErrorArgs}}</p> <p>Annotations Annotation Module Example</p> <p>?AFTER.check (blog_post, _Mod, _Fun, _Args) case is_bad(wpart:fget(post:msg)) of true-&gt; {skip, {redirect, http://correctiveschool.org}}; _-&gt; {proceed, _Args} end.</p> <p>e_components </p> <p>e_components is a mechanism for extending the capabilities of the framework</p> <p>Erlang-Web comes with e_components for authentication, mnesia backup and for generating rss feeds</p> <p>To search for an e_component we can run ./bin/e_componentsearch component_name, command that will list all available components matching the name and their description</p> <p>To install an e_component we run ./bin/e_component install component_name, the e_component is now copied in the lib folder of our application. ./bin/compile is now required, in order to compile the component's source files.</p> <p>e_components </p> <p>One more thing is required in order to use the component in our project: inserting a new tuple in the configuration file:{ecomponents, [{e_auth, []}, {e_auth_dets, []} ]}.</p> <p>After restarting the server we can verify that the components are running, by executing the function that lists all running applications : application:which_applications() </p> <p>e_components </p> <p>e_components is a mechanism for extending the capabilities of the framework</p> <p>Erlang-Web comes with e_components for authentication, mnesia backup and for generating rss feeds</p> <p>To search for an e_component we can run ./bin/e_componentsearch component_name, command that will list all available components matching the name and their description</p> <p>To install an e_component we run ./bin/e_component install component_name, the e_component is now copied in the lib folder of our application. ./bin/compile is now required, in order to compile the component's source files.</p> <p>Click to edit the outline text formatSecond Outline LevelThird Outline LevelFourth Outline LevelFifth Outline LevelSixth Outline LevelSeventh Outline LevelEighth Outline LevelNinth Outline Level</p>